【4.0.0対応!】ggplot2使い方ガイド

ggplotを使うのに役立つコマンドを紹介します!

R
公開

2024年11月17日

最終更新

2025年10月18日

はじめに

皆さん、ggplot、使ってますか?多分お使いですよね。

僕は数か月前ggplotのガイドブックを買いまして、いろいろと勉強しているところでございます。

こんなこともできるのか!という発見が多々あり、Rユーザーの皆様にはぜひ買っていただきたい代物なのですが、いかんせんお値段が張りますので、いくつかピックアップしてご紹介します。

書籍のリンクは以下です。

さらに、2025年9月、ggplot2がバージョン4.0.0にアップデートされました!それを記念し、4.0.0の要素も取り入れたページにアップデートしました!🎉

ぜひご参考になさってください!

{ggplot2}の基本

Note

使ったことがある方はこの辺はスキップしてください。

今回使うデータは、デフォルトで用意されているirisOrangeです。

ひとまず主要なパッケージである{ggplot2}を読み込みます。{ggplot2}はTidyverseに含まれていますので、データを色々加工したうえでグラフを書きたい場合が多いことを考えると、{tidyverse}を読み込むのが便利だと思います。

# install.packages("tidyverse")
library(tidyverse)

# {ggplot2}単体の場合
# install.packages("ggplot2")
# library(ggplot2)

基本的な使い方としては、ggplot()aes()、そして図に応じてgeom_line()(折れ線)やgeom_point()(散布図)などを使っていきます。

今回は例として、アイリスのがく片の長さ、幅をそれぞれ軸としてプロットします。

散布図

まずは基本的な散布図を描いてみます。散布図はgeom_point()を使うことで描画できます。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point()
図 1: irisデータフレームを使った散布図
  1. ggplot(data, aes())という形で使用するデータを指定
  2. aes()内ではX軸とY軸をそれぞれ指定
    • 例:aes(x = Sepal.Length, y = Sepal.Width)
    • 第1引数がX、第2引数がYと決まっているので、x =y =は書かなくてもよいです。
  3. ggplotのコマンドは+で繋ぐ
  4. 散布図を作るためにgeom_point()を繋げる

これが土台です。ggplot()だけではプロットは表示されませんので、geom_point()など何かしら繋ぐ形にしてください。

回帰線を引きたい場合:

早速応用ではありますが、散布図を描いたら回帰線を引きたいことがありますよね。そのためには、geom_smooth()を使います。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  geom_smooth(method = lm)
図 2: 回帰直線を引いた散布図

method = lmで回帰直線を引くオプションを付けることができ、デフォルトではYをXに回帰する直線が引かれます。式を変えたい場合はformula = y ~ log(x)などと式を追加してください。

また、デフォルトでは信頼区間がプロットされます。要らない場合は、se = FALSEを追加します。

また、lm以外にも、method = "loess"などもあります。?geom_smoothで確認してみてください。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  geom_smooth(method = "loess", se = FALSE)
図 3: loessを使った散布図

折れ線グラフ

折れ線グラフはgeom_line()を使います。

# fmt: skip
d <- tibble::tribble(
  ~a, ~b,
  1, 5,
  2, 3,
  3, 4,
  4, 2
)

ggplot(d, aes(x = a, y = b)) +
  geom_line()
図 4: 折れ線グラフ

今回はdという何のひねりもないデータフレームを作成して、それを用いて折れ線グラフを作成しています。

なぜわざわざデータフレームを自分で作ったかというと、1個体についてのみ時系列をとったデータを探してくるより、作った方が早かったからです😅

というのも、折れ線グラフを作るときは、多くの場合各個体ごとに何本かの折れ線を描くことが多いと思います1。各個体ごとに折れ線を作る方法については、後のセクションでご確認ください。

ここで覚えていただきたいのはgeom_line()で折れ線グラフを作れるということです。

棒グラフ

棒グラフも汎用性が高いグラフです。棒グラフはデータフレーム内の集計をするときに役に立ちます。

まずは、irisデータセットのSpecies(種)ごとに個体数を数えた棒グラフを作成します。

ggplot(iris, aes(x = Species)) +
  geom_bar()
図 5: 棒グラフ

おっと…。まさかのすべて50個体ずつで、棒グラフが全部同じ高さになってしまいました。これはミスではありません!

geom_bar()を使って棒グラフを書く際には、縦は自動で個体数をカウントすることになるので、aes(x = Species)とXを指定するだけで横に種類、縦にカウントをとった棒グラフが描けてしまいます。

もし横向きにしたければ、aes(y = Species)とYを指定すればOKです。

ggplot(iris, aes(y = Species)) +
  geom_bar()
図 6: 横向きの棒グラフ

もう1種類いきましょう。irisデータセットのSpecies(種)ごとに、Sepal.Length(がく片の長さ)の平均を計算して、その平均値を棒グラフで表示します。

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_bar(stat = "summary", fun = "mean")
図 7: 種ごとのがく片の長さの平均

今回はgeom_bar(stat = "summary", fun = "mean")とすることで、SpeciesごとにSepal.Lengthの平均を計算して棒グラフを描いています。種類ごとに集計をするのだけど、じゃあ集計するときには平均を使ってね、ということをgeom_bar()内で指定しています。

この場合は種類ごとにがく片の長さを計算する、ということでXもYもデータフレーム内の変数を使いますのでaes(x = Species, y = Sepal.Length)とX, Y両方明示しています。

既にプロットしたい値がデータフレーム内に入っている場合にはgeom_col()が活躍します。

mean_sepal_length <- iris |>
  summarise(
    mean_length = mean(Sepal.Length),
    .by = Species
  )

ggplot(mean_sepal_length, aes(x = Species, y = mean_length)) +
  geom_col()
図 8: geom_colを使った棒グラフ

ここではmean_sepal_lengthというデータフレームを作成し、SpeciesごとにSepal.Lengthの平均を計算して格納しています。つまり、既にプロットしたい値がデータフレーム内に入っている状態です。この場合、geom_col()を使うことで、XとY両方を指定して棒グラフを描くことができます(こちらの方が使用頻度は高いかもしれませんね)。

箱ひげ図

箱ひげ図は、分布を可視化するのに便利なグラフです。散布図と似ていますが、分布の要約がわかるようになっています。

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot()
図 9: 箱ひげ図

各種類において、Sepal.Length(がく片の長さ)の分布がわかります。四分位数や外れ値がわかるのが特徴です。

バイオリンプロット

バイオリンプロットは、分布を可視化するのに便利なグラフです。箱ひげ図と似ていますが、分布の形状もわかるようになっています。

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_violin()
図 10: バイオリンプロット

各種類において、Sepal.Length(がく片の長さ)の分布がわかります。例えば、setosaはがく片の長さが5あたりに集中していることがわかります。virginicaは全体的に長い傾向があることもわかりますね。

色を付ける

グラフに色を付ける方法について説明します。

基本的にはgeom_xxx()内でcolor = "red"のように指定すれば、色を付けることができます。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point(color = "red")
図 11: 散布図の点を赤色に

このように、geom_point(color = "red")で点を赤色にしています。

他のケースも見てみましょう。

ggplot(d, aes(x = a, y = b)) +
  geom_line(color = "blue")
図 12: 折れ線グラフの線を青色に

これも同様に、geom_line(color = "blue")で線を青色にしています。

棒グラフの場合は、少し事情が異なります。colorは棒の枠線の色を指定することになるので、棒自体の色を変えたい場合はfillを使います。

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_bar(stat = "summary", fun = "mean", color = "darkgreen")

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_bar(stat = "summary", fun = "mean", fill = "darkgreen")
図 13: 棒グラフの枠線が緑色に
図 14: 棒の色が緑色に

fillを使うのは棒グラフやバイオリンプロットなど、塗りつぶしがある場合です。もし枠線と塗りつぶしの両方を変えたい場合は、colorfillの両方を指定してください。

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_violin(color = "darkgreen", fill = "lightgreen")
図 15: 枠線と塗りつぶしの両方を指定

少しいじる

ここでは少しグラフをいじって、点の形を変えたり、線の種類や太さを変えたりする方法を説明します。

点の形

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point(shape = 17, color = "blue", size = 3)
図 16: 点の形と大きさが変わっています
  1. shapeで点の形を指定
    • 形は0から25まであります。?pointsで確認できます。
  2. sizeで点の大きさを指定
    • 数値が大きいほど大きくなります。

線の種類と太さ

ggplot(d, aes(x = a, y = b)) +
  geom_line(color = "blue", linetype = 2, linewidth = 1.5)
図 17: 線の種類と太さが変わっています
  1. linetypeで線の種類を指定
    • 0から6まであります。?linetypeで確認できます。
  2. linewidthで線の太さを指定
    • 数値が大きいほど太くなります。

基本的なグラフを紹介できたところで、次にこれらのグラフを使いながら、軸の扱いについて説明します。

軸ラベル

まずはX軸、Y軸のラベルを変更します。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  labs(x = "Length of Sepal", y = "Width of Sepal")
図 18: 軸ラベルを変更

labs(x = "Length of Sepal", y = "Width of Sepal")でX軸とY軸のラベルを変更しています。

また、以下のコードは同じ結果を示します。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point() + 
  xlab("Length of Sepal") + 
  ylab("Width of Sepal")

すなわち、labs()はそれ一つでX軸もY軸もラベルを変更することができますが、xlab()ylab()を使えば片方ずつでも変更できるということです。

次に、軸ラベルの体裁を変更したい場合、以下のようにします。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  labs(x = "Length of Sepal", y = "Width of Sepal") +
  theme(
    axis.title = element_text(
      family = "Times New Roman",
      face = "italic",
      color = "red",
      size = 20
    )
  )
図 19: 文字の体裁が変わっています
  1. theme()内のaxis.titleで変更する
    • X軸Y軸のどちらかだけ変更したければ、axis.title.xのようにします。
  2. element_text()内で具体的にフォントの体裁を指定
    • フォントはTimes New Romanを使用しています。これはWindowsの設定であり、MacやLinuxではTimesで表示されると思います。

    • 今回はわかりやすく派手にしましたが、これらの要素を変更すれば調整できます。

    • 例えば、face = "bold"にすれば太字にできます。

    • 他にもありますが、おおむね使うのはこのあたりでしょう。

軸の目盛り

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  scale_x_continuous(breaks = seq(4.5, 8, .5)) +
  scale_y_continuous(breaks = seq(2, 4, 1))
図 20: 目盛りの間隔が変わっています

図 19 と比較してもらえればわかりますが、目盛りがX軸は0.5刻み、Y軸は1刻みになるよう変更しました。このように、X(Y)が連続値である場合、scale_x_continuous()scale_y_continuous())で変更できます。seq()は数列を作る関数で、seq(from, to, by)の順に指定します。

また、連続変数ではなくカテゴリ変数の場合は、scale_x_discrete()scale_y_discrete())を使います。

ggplot(iris, aes(x = Species, y = Sepal.Length)) +
  geom_bar(stat = "summary", fun = "mean") +
  scale_x_discrete(
    breaks = c("setosa", "versicolor", "virginica"),
    labels = c("A", "B", "C")
  )
図 21: カテゴリのラベルが変わっています

breaksで元のラベル、labelsで新しいラベルを指定します。それぞれA, B, Cに変更されました。

目盛りも同様にフォントのスタイルを変更できます。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme(
    axis.text = element_text(
      family = "Times New Roman",
      face = "bold",
      color = "red",
      size = 20
    )
  )
図 22: 数字のフォントが変わっています

もはや訳が分からない図ですが、このように体裁は変更可能です。要素は大体上と同じで、axis.text.xのようにどちらかだけ変更することも可能です。

また、axis.ticksで目盛りを消去することも可能です。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme(
    axis.text = element_text(
      family = "Times New Roman",
      face = "bold",
      color = "red",
      size = 20
    ),
    axis.ticks = element_blank()
  )
図 23: 目盛りが消えています

お判りいただけますか?軸の数字は残っていますが、目盛り線は消えています。図 22 と見比べてみてください。

軸の範囲

X軸とY軸の範囲を設定する方法です。これまでの図ではだいたい4から8あたりがXの範囲、2.0から4.5がYの範囲でした。xlim(a, b)ylim(a, b)で設定できます。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  xlim(5, 7) +
  ylim(3, 4) +
  theme(
    axis.text = element_text(
      family = "Times New Roman",
      face = "bold",
      color = "red",
      size = 20
    ),
    axis.ticks = element_blank()
  )
図 24: 範囲を変えました

注釈

グラフの中に文字や線を入れる方法について説明します。文字や線を入れるにはannotate()関数を使います。

文字

まずは文字を入れる方法です。文字はannotate()内で"text""segment"を指定することで挿入可能です。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  annotate(
    "text",
    label = "ggplot is\nwonderful!",
    x = 6.5,
    y = 4,
    family = "Times New Roman",
    color = "#CC6666",
    size = 10
  )
図 25: 表内に文字を入れられました
  1. annotate("text", label = "hogehoge")hogehogeと文字を入れることができる

  2. xyの位置を指定しないと反映されない

  3. 改行したい場合、\nを入れる

  4. フォント、色、サイズも指定できる

関数一つで意外と簡単にできます。

セグメント

棒線や矢印を付けることも可能です。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  annotate(
    "segment",
    x = 5,
    xend = 7,
    y = 4,
    yend = 2.5,
    linewidth = 2,
    color = "#CC6666"
  )
図 26: 斜めに棒線が入りました。
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  annotate(
    "segment",
    x = 5,
    xend = 7,
    y = 4,
    yend = 2.5,
    linewidth = 2,
    color = "#CC6666",
    arrow = arrow(length = unit(2, units = "cm"))
  )
図 27: 矢印になりました。
  1. "segment"で棒線ができる
  2. xxendyyendを指定する必要がある
    • \((x, y)\)から\((xend, yend)\)までの2点を結ぶようにできます。
  3. linewidthで線の太さを指定可
    • 他の要素も文字の場合と同様に指定できますが、sizelinewidthに置き換わっています。
  4. arrow = arrow(length = unit(x, units = "cm"))で矢じりが付く
    • xで矢じりのサイズ、unitsは単位で、他にmmやinchesなどがあります。
    • ややこしいですが、xendyendの方に向かって矢印が付きます。

以上のように、文字だけでなく線や矢印を追加することができます。

今回はわかりやすくでかでかと描きましたが、例えばグラフ内の特徴的な部分に注釈をつけたい場合などに役立ちます。

時系列のプロット

時系列データをプロットする際に、元データには年と月が別々に入っていることもあると思います。それらのデータをうまく使いながら時系列プロットを作成する方法について説明します。

まずは年と月が別々に入っているデータを想定し、仮のデータフレームを作成します。

# fmt: skip
time_data <- tibble::tribble(
  ~year, ~month, ~value,
  2020, 4, 22,
  2020, 5, 30,
  2020, 6, 26,
  2020, 7, 35,
  2020, 8, 40,
  2020, 9, 38,
  2020, 10, 45,
  2020, 11, 50,
  2020, 12, 48,
  2021, 1, 52,
  2021, 2, 55,
  2021, 3, 53
)

このように、yearmonthが別々に入っているデータを想定します。

このデータを時系列プロットにするためには、まずyearmonthを結合して日付データに変換する必要があります。これにはlubridateパッケージを使うと便利です。

# install.packages("lubridate")
library(lubridate)

time_data <- time_data |>
  mutate(date = make_date(year, month, 1))
表 1: 日付データを追加したデータフレーム
year month value date
2020 4 22 2020-04-01
2020 5 30 2020-05-01
2020 6 26 2020-06-01
2020 7 35 2020-07-01
2020 8 40 2020-08-01
2020 9 38 2020-09-01
2020 10 45 2020-10-01
2020 11 50 2020-11-01
2020 12 48 2020-12-01
2021 1 52 2021-01-01
2021 2 55 2021-02-01
2021 3 53 2021-03-01

ここでmake_date(year, month, 1)を使って、各年と月の1日の日付データを作成しています。これでdate列が追加されました。

次に、このdate列を使って時系列プロットを作成します。

ggplot(time_data, aes(x = date, y = value)) +
  geom_line() +
  geom_point()
図 28: 時系列プロット

これで時系列がプロットできていることがわかるかと思います。

ただ、このままだと目盛りが「4 2020」のようになっており、日本人には馴染みが薄い形式です。そこで、scale_x_date()を使って目盛りの形式を変更します。

ggplot(time_data, aes(x = date, y = value)) +
  geom_line() +
  geom_point() +
  scale_x_date(
    date_labels = "%Y-%m",
    date_breaks = "2 month"
  )
図 29: 目盛りの形式を変更しました

date_labels = "%Y-%m"で年-月の形式に変更し、date_breaks = "2 month"で2か月ごとに目盛りを設定しています。間隔を調整したいときは、date_breaksの値を変更してください。

ラベルはいろいろ変更できます。

ggplot(time_data, aes(x = date, y = value)) +
  geom_line() +
  geom_point() +
  scale_x_date(
    date_labels = "%Y年%m月",
    date_breaks = "3 month"
  )
図 30: 別の形式に変更しました

%Yで年、%mで月を表します。

ちなみにこの状態だと1桁の月は0埋めされてしまいます。1桁の月を0埋めせずに表示したい場合は、WindowsとMac/Linuxで異なるのですが、以下の方法で実装可能です。

Windowsの場合は少々複雑です。

ggplot(time_data, aes(x = date, y = value)) +
  geom_line() +
  geom_point() +
  scale_x_date(
    breaks = "2 months",
    labels = \(x) {
      paste0(
        format(x, "%Y年"),
        as.integer(format(x, "%m")),
        "月"
      )
    }
  )
図 31: 1桁の月を0埋めしない形式(Windows)

注目すべきところはlabels = \(x) { ... }の部分です。format(x, "%Y年")で年を取得し、as.integer(format(x, "%m"))で月を取得したのち整数に変換しています。こうすることで1桁の月は0埋めされなくなります。最後にpaste0()で年と月の整数、“月”を結合しています。

MacやLinuxの場合はWindowsより簡単です。

ggplot(time_data, aes(x = date, y = value)) +
  geom_line() +
  geom_point() +
  scale_x_date(
    date_labels = "%Y年%-m月",
    date_breaks = "2 month"
  )

僕の環境はWindowsなので実行できませんが、%-mとすることで1桁の月を0埋めせずに表示できます。

複数のカテゴリのプロットと凡例

複数のカテゴリのプロット

ここではOrangeデータセットを使って複数のカテゴリのプロットと、凡例について説明します。

複数のカテゴリのプロットとは、以下のデータを見ていただけると早いと思います。

表 2: `Orangeデータセットの一部
Tree age circumference
1 118 30
1 484 58
1 664 87
1 1004 115
1 1231 120
1 1372 142
1 1582 145
2 118 33
2 484 69
2 664 111

Treeは木の識別子で、1~5まであります。ageは日齢、circumferenceは幹の周囲です。

ここでやりたいことは、木の種類ごとに、日齢と幹の周囲を軸にとってプロットすることです。これをただプロットしてしまうと、

ggplot(Orange, aes(x = age, y = circumference)) +
  geom_point()
図 32: すべてのデータを放り込みました

これでは各点がどの木のものなのか判別できない状態です。

これを識別するために、色を付けたいとしましょう。

ggplot(Orange, aes(x = age, y = circumference, color = Tree)) +
  geom_point()
図 33: 色が分かれました

前のセクションではgeom_point(color = "red")のように線全体に対して同じ色を付けましたが、今回の方法では、色を付けることを前提として、aes()内のcolor =で指定した種類ごとに色が付くという設定です。

これでとりあえずはどの点がどの木のものかわかるようになりました。

今は散布図なので色を分けるだけで済みますが、折れ線グラフのような場合は、色だけでなく線の種類を変えたいかもしれません。そんな時は次のようにします。

ggplot(Orange, aes(x = age, y = circumference, color = Tree, linetype = Tree)) +
  geom_point() +
  geom_line()
図 34: 折れ線の種類も分かれました

linetype = Treeを追加することで線の種類も木の種類ごとに変更することができました。

このようにaes()内で要素を追加することで種類ごとに分けることができます。他にも散布図の点の形をカテゴリごとに変えたければshape = Treeのようにすれば可能です。

まだ色遣いなどは不格好ですが、いったんこのまま進めます。

凡例の位置

次にしたいのは、凡例の設定です。凡例は現在右側に表示されていますが、まずはこれを下に表示したいとします。

これは、theme()内のlegend.positionで設定できます。

ggplot(Orange, aes(x = age, y = circumference, color = Tree)) +
  geom_point() +
  theme(legend.position = "bottom")
図 35: 凡例が下に来ました

theme(legend.position = "bottom")を設定することで、凡例の位置が下になりました。もちろんbottom以外にもtop, left, right(デフォルト)などを指定することができます。

さらに凡例を消したい場合は次のようにします。

ggplot(Orange, aes(x = age, y = circumference, color = Tree)) +
  geom_point() +
  theme(legend.position = "none")
図 36: 凡例を消しました

theme(legend.position = "none")で凡例を消すことができました。

凡例の調整

例えば、凡例のタイトルだけ消したい場合が結構あると思います。図 33 の例でいえば、Treeという文字は消したいということです。

そんな場合は、guides()を使います。

ggplot(Orange, aes(x = age, y = circumference, color = Tree)) +
  geom_point() +
  guides(color = guide_legend(title = NULL))
図 37: 凡例のタイトルを削除

Treeというタイトルが消えました。

ここではguides(color = guide_legend(title = NULL))としていますが、aes()内で例えばlinetypeを使っている場合は、guides()内もlinetype =にする必要があります。

テーマ

ggplotではテーマを選ぶことができます。デフォルトではこれまでの図のように背景がグレーになっていますが、ほとんどの場合で背景は白にしたいですよね。

背景だけでなく、罫線等もテーマで変更することができます。

プリセットのテーマ

いくつかのテーマが準備されています。代表的なものをいくつかご紹介します。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_minimal()
図 38: theme_minimal()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_bw()
図 39: theme_bw()

theme_minimal()と比べて外枠が付いています。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_classic()
図 40: theme_classic()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_linedraw()
図 41: theme_linedraw()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_light()
図 42: theme_light()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_void()
図 43: theme_void()

さすがにやりすぎでは…と思うかもしれませんが、GISで地図を可視化するようなときに重宝します。

theme()関数

ここまでご紹介したものの中でtheme()を使ったものがいくつかありました。

ここでtheme()theme_xxx()の後に使うということに注意してください。

これは、theme_xxx()theme()の設定を上書きしてしまうためです。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme(axis.title = element_text(color = "red")) +
  theme_minimal()
図 44: 失敗例

theme(axis.title = element_text(color = "red"))で軸ラベルを赤色に設定していますが、出力されたものは黒いラベルになっています。

順番を逆にすれば、

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_minimal() +
  theme(axis.title = element_text(color = "red"))
図 45: 正しい例

このようにしっかり反映されます。

theme()theme_xxx()で設定できない部分を細かく調整したい場合に使います。できることはたくさんあるので全てを説明することはできませんが、例えば以下のようなことができます。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_minimal() +
  theme(
    panel.grid = element_blank(),
    legend.position = "none",
    axis.title = element_text(family = "Times New Roman", size = 18),
    axis.text = element_text(family = "Times New Roman", size = 14)
  )
図 46: カスタムテーマ
  1. panel.grid = element_blank()で罫線を消す
  2. legend.position = "none"で凡例を消す
  3. axis.titleaxis.textで軸ラベルと目盛りのフォントとサイズを変更

このように、「グラフのここを変えたいんだけどなぁ…」みたいなことはtheme()で解決できることが多かったりします。

詳しくは?themeで確認してみてください。引数名はわかりやすいので、比較的目的を見つけやすいと思います。

新しくなったtheme()の設定

paperink

ggplot2のバージョン4.0.0からは、theme_xxx()内で色に関するデフォルト設定が追加できるようになりました。

百聞は一見に如かずということで、以下のコードをご覧ください。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_minimal(paper = "papayawhip", ink = "darkgreen")
図 47: theme()内での色のデフォルト設定

paperで背景色、inkで前景色を指定することができます。前景色は、軸ラベル、目盛り、点などに適用されます。

ここのポイントは、使用する色を統一的に指定できるということで、例えばtheme()内でaxis.titleaxis.textの色を指定する必要がなくなります。

geom

また、theme()内でgeomを設定することで、色に関するデフォルトを設定しつつgeom_xxx()内でその設定を参照することが可能です。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  theme_minimal(paper = "papayawhip", ink = "darkgreen") +
  theme(
    geom = element_geom(color = "coral", pointsize = 3)
  )
図 48: geom内での色のデフォルト設定

ここでは、theme(geom = element_geom(color = "coral", pointsize = 3))で点の色と大きさを指定しています。element_geom()geom_point()geom_line()geom_bar()などで共通して使われる設定を指定できます。一元管理できる点が便利ですね。

ちなみにgeomを設定しないと、点の色はinkの色、すなわちdarkgreenになります。

from_theme()

さらに、geom_xxx()内でfrom_theme()を使うことにより、theme()内で設定した色をgeom_xxx()内で参照することも可能です。

p <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
  geom_point(aes(color = from_theme(accent))) +
  theme_minimal()

p + theme(geom = element_geom(accent = "blue"))
p + theme(geom = element_geom(accent = "orange"))
図 49: アクセントカラーを青に
図 50: アクセントカラーをオレンジに

ここでは、from_theme(accent)theme()内で設定したaccentを参照しています。theme(geom = element_geom(accent = "blue"))のように設定することで、アクセントカラーを青に変更できます。

これはelement_geom(color = "blue")と一見同じことをしているように思えますが、たくさんのプロットを作成する場合、accentを一括で変更できる点で便利です。

使用例

  • ケース:
    • 論文執筆をしており、論文用とスライド用で色を変えたい
    • geom_point()geom_smooth()を使っており、geom_smooth()の色は論文用とスライド用で変えたい
  • 解決策:
  • テーマ側でアクセントカラーを定義
  • geom_line()のみfrom_theme(accent)を参照させる
  • こうするとプロットの本体コードは共通のまま、テーマを差し替えるだけで線の色だけ切り替わる

まず、あらかじめテーマを2つ用意しておきます。

# 論文用のテーマ
theme_paper <- theme_minimal(paper = "white", ink = "black") +
  theme(geom = element_geom(accent = "navy"))

# スライド用のテーマ
theme_slide <- theme_minimal(paper = "black", ink = "white") +
  theme(geom = element_geom(accent = "orange"))

次に、テーマを除くプロット本体を作成します。

p <- ggplot(Orange, aes(age, circumference)) +
  # 散布図は灰色で固定
  geom_point(color = "gray60", size = 2) +
  geom_smooth(
    aes(color = from_theme(accent)),
    method = lm,
    se = FALSE,
    size = 1
  ) +
  labs(x = "Age (days)", y = "Circumference (mm)")

この時点でpをプロットしても、折れ線の色は変わりません。まだtheme()を付けておらず、アクセントカラーが指定されていないためです。

それでは先に設定したテーマをpに適用します。

p_paper <- p + theme_paper
p_slide <- p + theme_slide

p_paper
p_slide
図 51: 論文用のテーマ
図 52: スライド用のテーマ

今回は1種類のスライドを論文用とスライド用に分ける想定しかしていませんが、たくさんのスライドを作る場合、アクセントカラーを一括で変えられる点で便利です。コードはそのままに、tmeme_papertheme_slideaccentだけ変えればよいからです。

新しくなったラベル設定

ここまではlabs()xlab()ylab()で軸ラベルを設定してきましたが、あらかじめラベルを設定しておき、自動で表示させることが可能になりました。

Orangeデータセットを使って説明します。

まず、何もしない場合は以下のようになります。

ggplot(Orange, aes(x = age, y = circumference)) +
  geom_point()
図 53: デフォルトのラベル

X軸はage、Y軸はcircumferenceと表示されています。

これを、あらかじめラベルを設定しておくことができるようになりました。

library(tibble)

# tibbleは属性を保持しやすい
df <- as_tibble(Orange)

# 単位: age=days, circumference=mm
attr(df$age, "label") <- "Age (days)"
attr(df$circumference, "label") <- "Trunk circumference (mm)"
  • attr()で変数に属性を追加
    • dfagelabel属性にAge (days)を追加している、という解釈です。
    • Age (days)Trunk circumference (mm)の部分がラベルなのでここを自由に変更できます。

これでdfを使ってプロットすると、

ggplot(df, aes(x = age, y = circumference)) +
  geom_point()
図 54: カスタムラベル

labs()を使わなくても、X軸とY軸のラベルが変更されました。

1つのグラフであればlabs()で設定した方が労力が少ないかもしれませんが、大量のグラフを作成する場合、あらかじめラベルを設定しておけば、labs()をいちいち書く必要がなくなるので便利です。

Notetibble形式について

皆さんはCSVデータを読み込むとき、何の関数を使用していますか?

多いのはbase Rのread.csv()や、tidyverseに含まれるreadrパッケージのread_csv()だと思います。

read.csv()はdata.frame形式、read_csv()はtibble形式でデータを読み込みます。tibbleはdata.frameを改良したデータ構造で、tidyverseの多くのパッケージで標準的に使われており、以下のような特徴があります。

  • 見やすい表示 すべての行を出さず、必要に応じて端だけを表示するので、データの中身を把握しやすいです。
  • 型情報を保持 各列のデータ型(数値・文字列など)が一緒に表示され、解析時に便利です。
  • 属性を保持 labelのような属性が処理中に消えにくく、ラベル管理や可視化との相性が良いです。

tibbleはdata.frameとほぼ同じように使えるので、tidyverseを使う場合はtibble形式でデータを扱うことをおすすめします。困ったらtibble::as_tibble()で変換できます。

カラーパレット

ggplot2では、連続値やカテゴリ値を色で表現する際に「カラーパレット」を使います。

例えばscale_colour_brewer()scale_fill_viridis_c()を指定すると、データの値に応じて自動的に一貫した配色が割り当てられます。

パレットを選ぶことで、見やすさや配色の意味(アクセシビリティ対応や論文用の白黒印刷など)を調整でき、グラフの理解度や印象に大きく影響します。

連続値のカラースケール

まずは例を見てみましょう。

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, colour = Petal.Length)) +
  geom_point(size = 2) +
  scale_colour_continuous() +
  labs(colour = "Petal length")
図 55: scale_colour_continuous()を使った例

scale_colour_continuous()は連続変数を色で表すためのカラースケールで、デフォルトの配色が使われ、数値の大小をなめらかなグラデーションで示しています。

自分で色を指定することも可能です。

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Petal.Length)) +
  geom_point(size = 2) +
  scale_colour_continuous(low = "blue", high = "red") +
  labs(colour = "Petal length")

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Petal.Length)) +
  geom_point(size = 2) +
  scale_colour_continuous(palette = c("#FEE0D2", "#FC9272", "#DE2D26")) +
  labs(colour = "Petal length")
図 56: 左: 青から赤へのグラデーション
図 57: 右: 指定した3色のグラデーション

lowからhighまでの2色を指定する方法や、自分で3色以上を指定する方法などがあります。

また、他の例としてscale_colour_viridis_c()を使う方法もあったりします。

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Petal.Length)) +
  geom_point(size = 2) +
  scale_colour_viridis_c(option = "plasma") +
  labs(colour = "Petal length")
図 58: scale_colour_viridis_c()を使った例

scale_colour_viridis_c()は連続変数を色で表すためのカラースケールで、視認性・色覚バリアフリー・白黒印刷対応に優れたviridisパレットを使います。plasma以外にもmagmainfernocividisなどのバリエーションがあります。

カテゴリ値のカラースケール

カテゴリ変数を色で表す場合は、scale_colour_brewer()scale_colour_viridis_d()などを使います2

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Species)) +
  geom_point(size = 2) +
  scale_colour_brewer(palette = "Set1") +
  labs(colour = "Species")
図 59: scale_colour_brewer()を使った例

scale_colour_brewer()paletteにはSet1Set2Dark2Pairedなどのカラーパレットがあり、用途に応じて選択できます。

また、scale_colour_discrete()を使う方法もあります。

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Species)) +
  geom_point(size = 2) +
  scale_colour_discrete() +
  labs(colour = "Species")
図 60: scale_colour_discrete()を使った例

なんというか、これぞggplot2!という感じの配色ですね。もちろん色を自分で指定することも可能です。

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Species)) +
  geom_point(size = 2) +
  scale_colour_manual(values = c("darkred", "darkblue", "darkgreen")) +
  labs(colour = "Species")
図 61: 指定した3色

さらに、scale_colour_viridis_d()だと以下のようになります。

ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Species)) +
  geom_point(size = 2) +
  scale_colour_viridis_d(option = "plasma") +
  labs(colour = "Species")
図 62: scale_colour_viridis_d()を使った例

今回はSpeciesがカテゴリ変数なので、先ほどのscale_colour_viridis_c()よりもscale_colour_viridis_d()の方が適していると言えますね。

今回は代表的なカラースケールをご紹介しましたが、他にもカラーパレットはたくさんあります。今回は入門編ということでこのあたりにしておきますが、いろいろ紹介してくれているページはあるので、例えば以下のようなページを参考にしてみてください。

いろいろ含めたグラフを作ってみる

ではここで、これまでの内容を踏まえて、新しいプロットを作ってみます。データはこれもデフォルトで用意されているmpgデータセットを使います。

df <- as_tibble(diamonds)

attr(df$cut, "label") <- "Cut"
attr(df$price, "label") <- "Price (USD)"
attr(df$carat, "label") <- "Carat"

theme_use <- theme_minimal(paper = "gray98", ink = "navy") +
  theme(
    geom = element_geom(
      accent = "purple",
      paper = "pink",
      pointsize = 2,
      borderwidth = 1
    ),
    axis.title = element_text(family = "Times New Roman", size = 16),
    axis.text = element_text(family = "Times New Roman", size = 12),
    legend.title = element_text(family = "Times New Roman"),
    legend.text = element_text(family = "Times New Roman")
  )

ggplot(df, aes(cut, price)) +
  geom_violin(aes(color = from_theme(accent))) +
  theme_minimal(paper = "gray98", ink = "navy") +
  theme_use

ggplot(df, aes(carat, price, color = cut)) +
  geom_point() +
  annotate(
    "text",
    label = "Fair < Good < Very Good < Premium < Ideal",
    x = 3.4,
    y = 2500,
    family = "Times New Roman"
  ) +
  scale_color_viridis_d(option = "plasma") +
  guides(color = guide_legend(reverse = TRUE)) +
  theme_use
(a) カットと価格の関係
(b) カラットと価格の関係
図 63: 作ってみました

発展:ggrepel

ここからは、ggrepelというパッケージを用いて折れ線グラフを発展させた例をご紹介します。ここからはだいぶややこしいので、出来上がったグラフを見て、必要性を感じていただけたらコードを読み解いてもらえればと思います。

# インストール
# pak::pak("ggrepel")
library(ggrepel)

まず、以下のようなデータがあるとします。firm_idは30まであります。

表 3: データの一部
firm_id state_id year treated_1998 is_treated y
1 50 1980 0 0 0.6546029
1 50 1981 0 0 1.9160271
1 50 1982 0 0 2.1400894
1 50 1983 0 0 1.8364579
1 50 1984 0 0 1.7661105
1 50 1985 0 0 1.2856041

これを使って横軸にYear、縦軸にyをとってグラフにします。

詳細を書くと長くなるので、適宜メモを入れました。ご参考まで。

df <- df |>
  mutate(
    # ハイライトする群とそれ以外に分ける
    group = if_else(treated_1998 == 1, as.factor(firm_id), "other"),
    # 最後の年にだけラベルを付ける
    group_lab = if_else(
      treated_1998 == 1 & year == 2015,
      paste0("Firm ", firm_id),
      NA_character_
    )
  )

ggplot(
  # まずハイライトする群だけプロット
  df |> filter(treated_1998 == 1),
  aes(x = year, y = y, group = firm_id)
) +
  theme_minimal() +
  theme(
    # 罫線を削除
    panel.grid = element_blank(),
    # 凡例を削除
    legend.position = "none",
    # 軸のタイトルと文字のフォントとサイズを調整
    axis.title = element_text(family = "Times", size = 18),
    axis.text = element_text(family = "Times", size = 14),
    # X軸タイトルを若干左寄せ
    axis.title.x = element_text(hjust = 0.4)
  ) +
  geom_vline(
    # 垂直線を描写
    xintercept = seq(1980, 2015, by = 5),
    color = "gray91",
    linewidth = .6
  ) +
  geom_segment(
    # 水平線を描写
    # 描写のためにデータを準備
    data = tibble(y = seq(-2.5, 5.0, by = 2.5), x1 = 1980, x2 = 2015),
    aes(x = x1, xend = x2, y = y, yend = y),
    inherit.aes = FALSE,
    color = "gray91",
    linewidth = .6
  ) +
  geom_segment(
    # 薄い水平線を描写
    data = tibble(y = seq(-2.0, 4.5, by = .5), x1 = 1980, x2 = 2015),
    aes(x = x1, xend = x2, y = y, yend = y),
    inherit.aes = FALSE,
    color = "gray97",
    linewidth = .3
  ) +
  geom_segment(
    # 縦軸が0のところに水平線を描写
    data = tibble(y = 0, x1 = 1980, x2 = 2015),
    aes(x = x1, xend = x2, y = y),
    inherit.aes = FALSE,
    linetype = "dashed",
    color = "gray40"
  ) +
  geom_vline(
    # 処置年に垂直線を描写
    xintercept = 1998,
    linetype = "dashed",
    color = "gray40"
  ) +
  geom_line(
    # ハイライトしない群をプロット
    data = df |> filter(group == "other"),
    color = "gray75",
    alpha = .5
  ) +
  geom_line(
    # ハイライトする群をプロット
    aes(color = group)
  ) +
  geom_text_repel(
    # ハイライトした線にラベルを追加
    aes(color = group, label = group_lab),
    family = "Times",
    hjust = 0,
    # 2017年の位置にラベルを書く
    xlim = c(2017, NA),
    size = 4,
    segment.linetype = "dotted"
  ) +
  xlab("Year") +
  ylab("Value") +
  scale_x_continuous(
    expand = c(0, 0),
    # ラベルが見えるように図の端の2015年より広くとる
    limits = c(1980, 2021),
    breaks = seq(1980, 2015, by = 5)
  ) +
  scale_y_continuous(
    expand = c(0, 0),
    limits = c(-2.5, 5.0),
    breaks = c(-2.5, 0, 2.5, 5.0)
  )
図 64: ggrepelを使ったプロット

おわりに

ひとまず僕がよく使うggplotの設定をまとめてみました。ggplotを駆使して、スタイリッシュなプロットを作成していきましょう!

適宜追記する予定ですので、ご参考になれば幸いです。

  1. 例えば男性と女性という2種類について、それぞれ平均寿命の時系列グラフを作る場合など。↩︎

  2. ちなみにscale_colour_viridiscの方はcontinuousで、dの方はdiscreteですね。↩︎