ggplot2ガイド

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

R
公開

2024年11月17日

最終更新

2025年4月6日

はじめに

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

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

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

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

ggplot2の基本

Note

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

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

ひとまず主要なパッケージであるggplot2を読み込みます。

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

基本的な使い方としては、ggplot()aes()、そして図に応じてgeom_line()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)
  3. ggplotのコマンドは+で繋ぐ
  4. 散布図を作るにはgeom_point()を繋げる
    • その他、折れ線ならgeom_line()、ヒストグラムならgeom_histogram()など

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

今回はggplotの基本というよりも、そこからの拡張を主に書きたいので、この散布図をベースに進めます。

軸ラベル

まずはX軸、Y軸のラベルを変更します。次のコマンドを追加することで変更できます。

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

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

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", face = "italic", 
                              color = "red", size = 20)
  )
図 3: 文字の体裁が変わっています
  1. theme()内のaxis.titleで変更する
    • X軸Y軸のどちらかだけにしたければ、axis.title.xのようにします。
  2. element_text()内で具体的にフォントの体裁を指定
    • 今回はわかりやすく派手にしましたが、これらの要素を変更すれば調整できます。

    • 例えば、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))
図 4: 目盛りの間隔が変わっています

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

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

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

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

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

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

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

軸の範囲

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", face = "bold", 
                             color = "red", size = 20), 
    axis.ticks = element_blank()
  )
図 7: 範囲を変えました

注釈

文字

表の中に文字を入れることができます。

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", color = "#CC6666", size = 10)
図 8: 表内に文字を入れられました
  1. annotate("text", label = "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")
図 9: 斜めに棒線が入りました。
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")))
図 10: 矢印になりました。
  1. "segment"で棒線ができる
  2. xxendyyendを指定する必要がある
    • \((x, y)\)から\((xend, yend)\)までの2点を結ぶようにできます。
  3. linewidthで線の太さを指定可
    • 他の要素も文字と同様に指定できますが、sizelinewidthに置き換わっています。
  4. arrow = arrow(length = unit(x, units = "cm"))で矢じりが付く
    • xで矢じりのサイズ、unitsは単位で、他にmmやinchesなどがあります。

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

複数のプロットと凡例

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

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

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

表 1: 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()
図 11: すべてのデータを放り込みました

これでは各点がどの木のものなのか判別できない状態です。これを識別するには次のようにします。

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

これでとりあえずはどの点がどの木のものかわかるようになりました。color = Treeで、木の種類ごとに色を使うことを指定しています。

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

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

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

このようにaes()内で要素を追加することで種類ごとに分けることができます。

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

凡例の位置

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

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

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

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

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

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

凡例の調整

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

そんな場合は、以下のようにします。

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

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()
図 17: theme_minimal()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point() + 
  theme_bw()
図 18: theme_bw()

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

ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point() + 
  theme_classic()
図 19: theme_classic()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point() + 
  theme_linedraw()
図 20: theme_linedraw()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point() + 
  theme_light()
図 21: theme_light()
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point() + 
  theme_void()
図 22: 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()
図 23: 失敗例

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"))
図 24: 正しい例

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

発展1:ggrepel

ここからは、ggrepelというパッケージを用いて折れ線グラフを発展させた例をご紹介します。

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

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

表 2: データの一部
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)
  ) + 
  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)
  )
図 25: ggrepelを使ったプロット

おわりに

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

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

  1. 離散値の場合はscale_x_discrete()などで変更できますが、今回その説明は省きます。↩︎