10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

MicroAdAdvent Calendar 2018

Day 19

ggplotで箱ひげ図よりもシンプルに可視化する方法

Last updated at Posted at 2018-12-18

はじめに

この記事は MicroAd Advent Calendar 2018 の19日目の記事です。

データの分布をわかり易く可視化する方法として、箱ひげ図があります。
箱ひげ図は、何らかの水準ごとにデータの分布を確認するときに重宝しますが、1つの要因内の水準数が多くなってくると箱ひげ図の数が多すぎてグラフが見づらくなってしまいます。
そんなときに、箱ひげ図の代替になる可視化をggplot2によって実現する方法を考えてみました。

データの準備

まず、描画に利用するダミーデータを作成しておきましょう。以下ではこのデータを利用していきます。

library(dplyr)
library(ggplot2)
library(RColorBrewer)
####################################
# ダミーデータの作成
####################################

# 水準数
n_level <- 300 # 3, 50, 300 に変えてそれぞれ実行する 

# 各水準のサンプルサイズ
n_sample_level <- 100

# 全水準のサンプルサイズ
n_sample <- n_level * n_sample_level

# 水準ごとの分布の平均値
set.seed(0)
mean_values <- rnorm(n=n_level, mean=0, sd=10)

# 各サンプルが属する水準
level <- rep(seq(1, n_level), times= rep(n_sample_level, n_level)) %>% as.factor()

# 各水準のサンプルデータ
value <- c()
for (level_index in 1:n_level){
  # シードの固定
  set.seed(level_index)

  # 乱数を生成し、その水準のデータとする
  tmp <- rnorm(n=n_sample_level, mean=mean_values[level_index], sd=5)
  
  # 全水準のデータをまとめる
  value <- c(value, tmp)
}

# データフレームの生成
df <- data.frame(level = level,
                 value = value)

グラフの描画

箱ひげ図

箱ひげ図は geom_boxplot()を使って書くことができます。データの水準数によって見た目がどう変化するかみていきましょう。

水準数=3 の場合

3水準程度なら箱ひげ図を描いておけば何も問題はありません。

####################################
# 箱ひげ図の描画
####################################
ggplot(data = df) +
  geom_boxplot(aes(x = level, y = value)) 

image.png

水準数=50 の場合

50水準くらいになると、箱ひげ図の数が多くなって多少見づらくなってきます。
image.png

水準数=300

ここまでくると、もはや箱ではなくただの線ですね。もう少しシンプルにしたいところです。
このような可視化をするときは、分布の分布の様子を確認したい場合などでしょうか。

image.png

##点と線だけで
水準数=300の場合をどうにかしてみましょう。
箱ひげ図で表現される統計量をもう少しシンプルに描画することで、分布の(分布の)様子を把握しやすくします。

箱ひげ図に平均値を重ねて描画するときはstat_summary()を使いますが、中央値などの統計量もこの関数をつかて表示してみます。さらに、geom_segment()を使うと、線分をグラフ上の任意の場所に描くことができます。これも使ってみましょう。

縦線を引きたいときに使う関数としてはgeom_vline()がよく知られていますが、geom_segment()であれば端点の位置も直感的に指定することができます。

ここで確認したいデータの統計量は「最小値」「第1四分位数」「中央値」「平均値」「第3四分位数」「最大値」とします。簡単のため、箱ひげ図のように外れ値を分離して表示することは考えません。

平均値は赤色の×、その他の各統計量は○で表し、「第1四分位数」と「中央値」、「中央値」と「第3四分位数」のそれぞれを線で結んでみます。

####################################
# 統計量を可視化
####################################

# カラーマップ
library(RColorBrewer)
cols <- rainbow(9, start = .7, end = .1)
cols <- gray.colors(9, start = 0.1, end = 0.9)

# 四分位数を計算する関数
q1_val <- function(x){return(quantile(x, probs = 0.25))}
q3_val <- function(x){return(quantile(x, probs = 0.75))}

# levelごとに統計量を計算しておく
df_stats <- 
  df %>% 
  group_by(level) %>% 
  summarise(min = min(value),
            q1_val = q1_val(value),
            median = median(value),
            mean = mean(value),
            q3_val = q3_val(value),
            max = max(value)
            ) %>% 
  ungroup()

# 描画
ggplot(data = df, aes(x = level, y = value)) +
  stat_summary(fun.y = min, geom = "point", shape = 21, colour = "black", fill = cols[1]) +
  stat_summary(fun.y = q1_val, geom = "point", shape = 21, colour = "black", fill = cols[3]) +
  stat_summary(fun.y = median, geom = "point", shape = 21, colour = "black", fill = cols[5]) +
  stat_summary(fun.y = mean, geom = "point", shape = 4, colour = "black", fill = "green", size=1) +
  stat_summary(fun.y = q3_val, geom = "point", shape = 21, colour = "black", fill = cols[7]) +
  stat_summary(fun.y = max, geom = "point", shape = 21, colour = "black", fill = cols[9]) +
  xlab("水準(level)") +
  ylab("value")+
  geom_segment(data = df_stats,
               aes(x = level, xend=level, y = q1_val, yend = median),
               size = 0.25, lineend = "butt", color = cols[4]) +
  geom_segment(data = df_stats,
               aes(x = level, xend=level, y = median, yend = q3_val),
               size = 0.25, lineend = "butt", color = cols[6]) 

image.png

残念ながら箱ひげ図の場合と同じか、それ以上にゴチャゴチャしています。

ここからさらに、分布の様子を踏まえて横軸の水準を並び替えてみましょう。各水準の中央値の大きさで水準を並び変えます。

# 描画

ggplot(data = df, aes(x = reorder(x = level, X = value, FUN = median), y = value)) +
  stat_summary(fun.y = min, geom = "point", shape = 21, colour = "black", fill = cols[1]) +
  stat_summary(fun.y = q1_val, geom = "point", shape = 21, colour = "black", fill = cols[3]) +
  stat_summary(fun.y = median, geom = "point", shape = 21, colour = "black", fill = cols[5]) +
  stat_summary(fun.y = mean, geom = "point", shape = 4, colour = "red", size=1) +
  stat_summary(fun.y = q3_val, geom = "point", shape = 21, colour = "black", fill = cols[7]) +
  stat_summary(fun.y = max, geom = "point", shape = 21, colour = "black", fill = cols[9]) +
  xlab("水準(level)") +
  ylab("value") +
  geom_segment(data = df_stats,
               aes(x = level, xend = level, y = q1_val, yend = median),
               size = 0.25, lineend = "butt", color = cols[4]) +
  geom_segment(data = df_stats,
               aes(x = level, xend = level, y = median, yend = q3_val),
               size = 0.25, lineend = "butt", color = cols[6]) 

(変更前)x = level
(変更後)x = reorder(x = level, X = value, FUN = median)
このようにggplot()の中で指定するaes()x=の部分を変更するのがポイントです。1

image.png

さて、これでだいぶスッキリしました。赤色の×で平均値、線でつながれた3つの点(○)が、各水準の第1四分位数、中央値、第3四分位数を表しています。その外側にあるのが最小値と最大値です。

この水準数になると、各水準がどうというよりも、全体的にどのような傾向かが知りたくなります。
そんなときにこのような可視化をすると、各水準での分布が全体ではどのように分布しているのかを直感的に把握でき(る気がし)ますね。

  1. この記事を参考にさせてもらいました。

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?