箱ひげ図(boxplot)は、表にまとめると視認性が悪い複数のサブサンプルの四分位と最大最小の値を、簡潔に見やすく表示することで、分布の特徴の比較が簡単にできるプロットです。今では中高でも教えているぐらい一般化しています。
1 箱ひげ図(boxplot)の構造
プロットする前にRの箱ひげ図がどういうものかを確認しておきましょう。箱ひげ図はサブサンプルごとに以下の図を作成します。
第1四分位(Q1; 25%パーセンタイル)、第2四分位(Q2; 中央値)、第3四分位(Q3; 25%パーセンタイル)は教科書通りですが、最大と最小が異常値(extreme dataもしくはoutlier)を除いて計算されます。
異常値は\(\{x \mid x \lt \text{Q1} -
\text{range}\cdot\text{IQR}\} \cup \{x \mid x \geq \text{Q3} +
\text{range}\cdot\text{IQR}\}\)で定義されます。range
はデフォルトでは1.5
で、0
を指定すると\(\infty\)として解釈されます。\(\text{IQR}\)は\(\text{Q3}-\text{Q1}\)です。
2 箱ひげ図をプロットしてみる
習うより慣れろということで、プロットしてみましょう。
2.1 データセット
乱数から個体数4, サンプルサイズ160のパネル・データセットを作りましょう。
set.seed(1212)
<- with(list(n = 160, n1 = 15, nc = 4), {
df01 <- runif(n, min=0, max=5)
x <- c(rep(1, n1), rep(2, n/2 - n1), rep(3, n/4), rep(4, n/4))
z <- runif(nc, min=-5, max=5)
zv <- 1 + 2*x + zv[z] + rnorm(n)
y data.frame(y, x, z=factor(sprintf("group-%s", z)))
})
2.2 シンプルにプロット
単純に使っても、概ね期待通りのモノが出力されます。
<- boxplot(y ~ z, data = df01) r_boxplot
上のプロットはモデル式でデータを指定しましたが、
with(df01, boxplot(
=="group-1"],
y[z=="group-2"],
y[z=="group-3"],
y[z=="group-4"])) y[z
と、ベクトルを並べていくこともできます。ただし、names
を指定しないと、グループ名が入りません。
3 戻り値を確認
boxplot
はinvisible
で、
- 正常値の最小・四分位・正常値の最大(
stats
) - 観測数(
n
) - 95%信頼区間の近似
notch
\(= \pm 1.58 \text{IQR} / \sqrt{n}\)(conf
) - 異常値(
out
) - 異常値が属するグループ番号(
group
) - グループ名(
names
)
を要素として含むリストを戻します。
print(r_boxplot)
$stats
[,1] [,2] [,3] [,4]
[1,] -1.67134921 -3.3336851 -5.0511667 -0.3469611
[2,] -0.02562462 -0.0885454 -0.5182996 1.6359863
[3,] 2.25986714 3.0815960 1.4288402 3.8609982
[4,] 4.47099636 4.9971122 3.5222448 6.5509501
[5,] 6.98958634 8.4444394 6.7823874 10.2391417
$n
[1] 15 65 40 40
$conf
[,1] [,2] [,3] [,4]
[1,] 0.4254515 2.084935 0.4194317 2.633142
[2,] 4.0942828 4.078257 2.4382487 5.088854
$out
numeric(0)
$group
numeric(0)
$names
[1] "group-1" "group-2" "group-3" "group-4"
plot = FALSE
をつけると、描画しないで戻り値を戻します。
4 箱ひげ図に載せる情報の追加/調整
表示する情報を増やしたり、調整したりできます。
4.1
notch
を表示する
サブサンプルが同一かの目安に使うnotch
を表示できます。なお、統計的仮説検定(e.g. t検定)と対応しているわけではなく、尤度原理にも即していないので、参照するときは注意深く行ないましょう。
<- boxplot(y ~ z, data = df01, notch = TRUE) r_boxplot
4.2 サンプルサイズに比例した太さにする
サブサンプルごとに観測数が違う場合、varwidth = TRUE
をオプションにつけると、太さで表現することができます。
boxplot(y ~ z, data = df01, varwidth = TRUE)
4.3 異常値になる範囲を変更する
range
で異常値とする外れ値を調整できます。0
を指定すると、すべて正常値として取り扱います。
boxplot(y ~ z, data = df01, range=0.5)
4.3.1 異常値になる範囲(outlier)を注に入れる
異常値が無い場合は不要ですが、異常値は任意に指定できるrange
に依存するのでプロットする場合は、異常値の範囲を明示する必要があります。
例えば、以下のように書いておきましょう。
mtext(substitute(paste("Note)",
phantom(0),
"outlier"==paste(group("{", paste(x, "|", x>=Q3+range%.%IQR), "}"),
"∪",
group("{", paste(x, "|", x<Q1-range%.%IQR), "}"))), list(range = 1.5)),
1, 3, adj=0)
5 箱ひげ図の見栄えを調整する
デフォルトでは素朴ですが、見栄えの自由度は意外に広いです。
5.1 縦ならび
業界慣習の縦横にあわせる事もできます。
boxplot(y ~ z, data = df01, horizontal = TRUE)
5.2 箱の太さと、線の長さを変える
箱の部分はboxwex
を指定することで自動設定(0.8
)よりも太くしたり細くしたりできます。
boxplot(y ~ z, data = df01, boxwex = 0.5)
ヒゲの先の線の長さは、staplewex
で指定できます。boxwex
に対する相対サイズです。
boxplot(y ~ z, data = df01, staplewex = 1.0)
5.3 事細かに表示を調整する
pars
引数にリストで事細かに表示方法を指定できます。デフォルトのままでよいパラメーターは、リストの要素を消してしまえばよいです。グループごとに表示設定を変えたい場合は、グループの数と同じ長さのペクトル(もしくはグループの数の公約数と同じ長さのベクトル)をリストの値とすればよいです。
<- list(
params # 四分位の表示の設定
boxwex = 0.5,
boxlty = 1,
boxlwd = 1,
boxcol = "darkred",
boxfill = c("red", "pink", "purple", "yellow"),
# 中央値の表示の設定
medlty = NULL,
medlwd = NULL,
medpch = NULL,
medcex = NULL,
medcol = NULL,
medbg = NULL,
# ヒゲの部分(第1四分位未満/第2四分位以上から異常値にならない最小/最大値まで)の線の表示設定
whisklty = 2,
whisklwd = 1,
whiskcol = "blue",
# 最大/最小値の表示の設定
staplewex = c(0.5, 1, 0.8, 0.5),
staplelty = 1,
staplelwd = 2,
staplecol = "darkgreen",
# 異常値の表示の設定
outwex = NULL,
outlty = NULL,
outlwd = NULL,
outpch = 21,
outcex = 2,
outcol = "black",
outbg = "white"
)boxplot(y ~ z, data = df01, pars = params)
5.4 グリッド線を描く
boxplot
はpanel.first
が使えないので、グリッド線が箱ひげ図の上に描かれないように、低水準描画関数でプロット領域をつくって軸とグリッド線を描いた後に、add=TRUE
をつけてboxplot
を低水準描画関数として呼び出します。
plot.new()
<- c(0.5, 0.5 + length(levels(df01$z)))
xlim <- function(y, unit){
round_lim if(length(y)!=2) stop("Length of y must be two.")
<- sort(y)
y <- c(FALSE, TRUE)
larger sign(y)*ifelse(larger,
ifelse(y < 0, floor(abs(y) / unit), ceiling(abs(y) / unit)),
ifelse(y < 0, ceiling(abs(y) / unit), floor(abs(y) / unit))
*unit
)
}<- 3
y_interval <- round_lim(range(df01$y), y_interval)
ylim plot.window(xlim=xlim, ylim=ylim)
par(mar = c(4, 3, 0, 0))
axis(1, at = ceiling(xlim[1]):floor(xlim[2]),
labels = levels(df01$z), tick = FALSE)
axis(2, at=seq(ylim[1], ylim[2], y_interval), las=1, pos=xlim[1])
# grid関数だと目盛りにグリッド線をあわせられない為ablineで描く
abline(h = seq(ylim[1], ylim[2], y_interval), lty = 3, col="gray")
title(xlab = expression(z), ylab = expression(y))
boxplot(y ~ z, data = df01, add = TRUE, axes=FALSE)
6 まとめ
あまり箱ひげ図に親しみはなかったのですが、サブサンプルの基本統計量の確認作業に重宝しそうです。サブサンプル間の差異を検定したり、評価したりする方法ではない事を意識しながら、便利に使っていきましょう。boxplot
で差がはっきり見えたら、統計的検定をしても、ベイズファクターを見ても、だいたい差異があると言えるような結果になると思いますが。