HOWTO · R

R でグループ化された箱ひげ図を作成する

この記事では、R でグループ化された箱ひげ図を作成する方法を紹介します

このページの内容

ggplot2 でグループ化箱ひげ図を作成するうえで重要なのは、比較したいグループにデータを分ける変数をどれにするかという一点です。その変数を fill エステティックにマップすれば、geom_boxplot() が各 x 位置の中に箱を横並びで描きます。あるいは facet_wrap() でデータを個別のパネルに分割してもかまいません。以下の例では gapminder データセットと dplyr パッケージを使います。どちらの方法でも、自分の列に合わせて調整できるグループ化箱ひげ図を作成できます。

geom_boxplot() でグループ化された箱ひげ図を作成する

箱ひげ図オブジェクトは、一般に ggplot 関数と geom_boxplot を組み合わせて作成します。ggplot の第 1 引数はデータセット、第 2 引数は aes で作成するエステティックマッピングのリストです。以下のマッピングでは、x と y が gapminder の continent 列と lifeExp 列を指しており、データは dplyr の関数で選択した 3 つの年に絞り込んでいます。なお、コード例と、コード内で表示される軸ラベルなどの文字列は英語のままです。

if (requireNamespace("ggplot2", quietly = TRUE) &&
    requireNamespace("gapminder", quietly = TRUE) &&
    requireNamespace("dplyr", quietly = TRUE)) {
  library(ggplot2)
  library(gapminder)
  library(dplyr)

  dat <- gapminder %>%
    filter(year %in% c(1972, 1992, 2007))

  print(ggplot(dat, aes(x = continent, y = lifeExp)) +
    geom_boxplot() +
    scale_y_continuous(name = "Average Life Expectancy") +
    scale_x_discrete(labels = abbreviate, name = "Continent"))
}

この呼び出しは大陸ごとに 1 つの箱を描きます。すでに x 変数でグループ化されていますが、各大陸の中の年はまだ 1 つにまとめられたままです。次のステップは、2 つ目のグループ化変数を追加することです。requireNamespace によるガードのおかげで、描画パッケージがインストールされていない環境でもスクリプトは正常に終了します。その場合は描画がスキップされるため、このコードは構文の例として扱い、記事の後半に掲載したレンダリング済みの図を参照してください。

fill エステティックへのマッピングで箱をグループ化する

列を fill にマップすると、geom_boxplot は各 x 位置の中でグループごとに別々の箱を描きます。ただし 1 つ注意点があります。fill が明確なグループを作るのは、マップした変数が離散型の場合だけです。gapminder の year 列は数値型なので、fill = year は連続的なカラーグラデーションとして扱われ、プロットは年ごとの箱に分割されません。factor で包むと、数値がカテゴリー型の水準に変換され、意図した横並びの箱が得られます。この違いは R 単体のコード例で確認できます。なお、コード例と出力に表示される値や文字列は英語のままです。

year <- c(1972, 1992, 2007)

print(class(year))
print(class(factor(year)))
print(levels(factor(year)))
[1] "numeric"
[1] "factor"
[1] "1972" "1992" "2007"

数値の year は連続値のままですが、factor(year) は名前付きの水準を持ちます。そして、fill が別々の箱に展開するのは後者の形式だけです。ggplot2 でも同じ区別がレイアウトを決めます。

if (requireNamespace("ggplot2", quietly = TRUE) &&
    requireNamespace("gridExtra", quietly = TRUE) &&
    requireNamespace("gapminder", quietly = TRUE) &&
    requireNamespace("dplyr", quietly = TRUE)) {
  library(ggplot2)
  library(gridExtra)
  library(gapminder)
  library(dplyr)

  dat <- gapminder %>%
    filter(year %in% c(1972, 1992, 2007))

  p1 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = year)) +
    geom_boxplot() +
    scale_y_continuous(name = "Average Life Expectancy") +
    scale_x_discrete(labels = abbreviate, name = "Continent")

  p2 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = factor(year))) +
    geom_boxplot() +
    scale_y_continuous(name = "Average Life Expectancy") +
    scale_x_discrete(labels = abbreviate, name = "Continent")

  grid.arrange(p1, p2, nrow = 2)
}

連続値の year の fill と factor(year) の fill を比較したグループ化箱ひげ図。個別の箱が描かれるのは factor 版のみ

上のパネルは year を数値のままにしているためグループ化されません。一方、下のパネルは factor(year) を使い、選択した各年につき 1 つずつ、大陸ごとに 3 つの箱を表示しています。

facet_wrap() でグループをパネルに分割する

facet_wrap 関数は、選択した変数でグループ化された箱ひげ図を描くもう 1 つの方法です。ここでは年ごとに 1 つのパネルを作成します。facet_wrap は fill マッピングがなくても動作しますが、fill = factor(year) を残して箱に色を付けると、パネルどうしを見分けやすくなります。なお、コード例と、コード内で表示される軸ラベルなどの文字列は英語のままです。

if (requireNamespace("ggplot2", quietly = TRUE) &&
    requireNamespace("gapminder", quietly = TRUE) &&
    requireNamespace("dplyr", quietly = TRUE)) {
  library(ggplot2)
  library(gapminder)
  library(dplyr)

  dat <- gapminder %>%
    filter(year %in% c(1972, 1992, 2007))

  p3 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = factor(year))) +
    geom_boxplot() +
    facet_wrap(~year) +
    scale_y_continuous(name = "Average Life Expectancy") +
    scale_x_discrete(labels = abbreviate, name = "Continent")

  print(p3)
}

年ごとに 1 つのパネルを持ち、x 軸と y 軸のスケールを共有するファセット箱ひげ図

デフォルトでは、facet_wrap が生成するすべてのパネルで同じスケールが共有されるため、パネル間で値を直接比較できます。scales = "free" を渡すと、各パネルが含まれるデータに合わせて軸を個別に調整します。範囲の狭いパネルでも細部を見やすくできる一方、同じ高さが同じ値を意味しなくなるため、パネル間の比較が誤解を招く可能性があります。

if (requireNamespace("ggplot2", quietly = TRUE) &&
    requireNamespace("gapminder", quietly = TRUE) &&
    requireNamespace("dplyr", quietly = TRUE)) {
  library(ggplot2)
  library(gapminder)
  library(dplyr)

  dat <- gapminder %>%
    filter(year %in% c(1972, 1992, 2007))

  p4 <- ggplot(dat, aes(x = continent, y = lifeExp, fill = factor(year))) +
    geom_boxplot() +
    facet_wrap(~year, scales = "free") +
    scale_y_continuous(name = "Average Life Expectancy") +
    scale_x_discrete(labels = abbreviate, name = "Continent")

  print(p4)
}

各年のパネルがそれぞれ独自の軸スケールに調整されるファセット箱ひげ図

レイアウトを選んで結果を確認する

グループの数が少なく、各 x カテゴリーの中で直接比較したい場合は fill によるグループ化を使います。グループが多い場合や、各サブセットに独立したパネルを割り当てたい場合は facet_wrap を使います。どの変数を fill にマップし、どの変数でファセットするかを入れ替えることもできます。以下の例ではフィルタリングしていない gapminder データセットに戻り、continent を fill にマップし、facet_wrap には利用可能なすべての年を渡します。なお、コード例と、コード内で表示される軸ラベルなどの文字列は英語のままです。

if (requireNamespace("ggplot2", quietly = TRUE) &&
    requireNamespace("gapminder", quietly = TRUE)) {
  library(ggplot2)
  library(gapminder)

  p5 <- ggplot(gapminder, aes(x = continent, y = lifeExp, fill = continent)) +
    facet_wrap(~year) +
    geom_boxplot() +
    scale_y_continuous(name = "Average Life Expectancy") +
    scale_x_discrete(labels = abbreviate, name = "Continent")

  print(p5)
}

フィルタリングしていない gapminder データセット全体を年でファセットし、continent を fill の色にマップした箱ひげ図

どのレイアウトを選んでも、レンダリングされたプロットで、グループごとの箱やパネルの数が想定どおりかを確認してください。数値列が個別の箱ではなく連続的なカラー凡例を生成した場合は、まず factor で変換します。また、パネルの形は同じでも位置が異なる場合は、グラフから値を読み取る前にスケールが共有されていることを確認してください。