Skip to content

Latest commit

 

History

History
606 lines (496 loc) · 14.9 KB

03-グラフ描画の基本と応用.md

File metadata and controls

606 lines (496 loc) · 14.9 KB

3-1 グラフ描写を効率化する重要性

3-2 統一的な記法によるグラフ描写(ggplot2パッケージ)

# ダイヤモンドの価格・カラット数・色の関係を散布図として可視化

# ライブラリ読み込み
library(ggplot2)

# 可視化
ggplot(diamonds) + # 可視化したいデータフレームの指定
  aes(             # 列名をx軸、y軸、色に割り当て。引用符は使わない
    x = carat,
    y = price,
    color = color
  ) +
  geom_point()     # 散布図として可視化

基本的な使い方

グラフに使うデータフレームの指定(ggplot関数)

データフレームの列を軸や点の色に指定(aes関数)

# ダイアモンドのカラット数と価格の関係の散布図を
# 1カラット以上か否かで塗り分ける
ggplot(diamonds) +
  aes(x = carat, y = price, color = carat >= 1) +
  geom_point()
# 審美的属性の指定方法3パターン(結果省略)

# 1. mapping変数をレイヤとして利用
ggplot(diamonds) +
  aes(x = carat, y = price) +
  geom_point()
# 2. mapping変数をggplot関数のmapping引数に指定
ggplot(diamonds, mapping = aes(x = carat, y = price)) +
  geom_point()
# 3. mapping変数をgeom_point関数のmapping引数に指定
ggplot(diamonds) +
  geom_point(mapping = aes(x = carat, y = price))
# geom関数群によるグラフ全体の審美的属性のオーバーライド
# (結果省略)
ggplot(diamonds) +
  aes(x = carat, y = table) +
  geom_point(aes(y = price)) # x = carat, y = priceに相当

グラフの種類を選ぶ(geom関数群)

# 同日に同種の餌を与えられた雛の体重の中央値を集計
library(magrittr)
chick_weight <- ChickWeight %>%
  dplyr::group_by(Diet, Time) %>%
  dplyr::summarize(Weight = median(weight), .groups = "drop")

# 体重の中央値の推移を餌ごとに可視化する
ggplot(chick_weight) +
  aes(x = Time, y = Weight, shape = Diet) +
  geom_line() +
  geom_point(size = 4)

3-3 グラフの色や形を変更(scale関数群)

色覚多様性や印刷環境への対応

# 散布図の点をviridisで色付け
ggplot(mtcars) +
  aes(wt, mpg, color = cyl) +
  geom_point(size = 3) +
  scale_color_viridis_c()

散布図の点の形に意味を持たせる

# 外れ値を含むデータの作成
d <- data.frame(
    condition = "good",
    x = runif(100, 0, 10)
  ) %>%
  dplyr::mutate(y = rnorm(dplyr::n(), x)) %>%
  tibble::add_row(x = 8, y = 1, condition = "bad")

# 外れ値を含む散布図の作成
g <- ggplot(d) +
  aes(x, y, shape = condition) +
  geom_point()

# (a) 点の形をパッケージにまかせて表示
g
# (b) 点の形はデータが正常なら●で、外れ値なら×にする
g + scale_shape_manual(
  values = c(good = "circle", bad = "cross")
)
# condition列を凡例に利用したとき、
# goodがbadより順序が上にくるように変数dに前処理を加える
d <- d %>%
  dplyr::mutate(
    condition = forcats::fct_relevel(condition, "good", "bad")
  )
g <- ggplot(d) +
  aes(x, y, shape = condition) +
  geom_point()

散布図の点を縁取る

# (a) 散布図の点の枠線と塗り潰しがうまく反映されない例
ggplot(mtcars) +
  aes(wt, mpg, fill = cyl) +
  geom_point(color = "black", size = 3)

# (b) 散布図の点の枠線と塗り潰しがうまく反映される例
ggplot(mtcars) +
  aes(wt, mpg, fill = cyl) +
  geom_point(
    color = "black",
    size = 3,
    shape = "circle filled"
  )
# 散布図で塗り潰し可能な形を複数利用する例
ggplot(mtcars) +
  aes(
    wt, mpg, fill = cyl,
    shape = as.factor(am) # shape審美的属性に数値は使えない
  ) +
  geom_point(color = "black", size = 3) +
  scale_shape_manual(
    name = "am", # 凡例のタイトルを「as.factor(am)」から変更
    values = c(  # am列の各値(0または1)に割り当てたい形を指定
        "1" = "circle filled",
        "0" = "triangle filled"
      )
  )

3-4 軸の調整(scale関数群とcoord関数群)

対数軸の利用

# ヒストグラムの作成
g <- ggplot(diamonds) +
  aes(carat) +
  geom_rug() +
  geom_histogram()

# (a) 通常の軸のグラフ
g
# (b) y軸が対数スケールのグラフ
g + scale_y_log10()

特定領域の拡大

# 車重と燃費の関係を散布図と回帰曲線で比較する
g <- ggplot(mtcars) +
  aes(wt, mpg) +
  geom_point() +
  geom_smooth(se = FALSE) # 回帰曲線。信頼区間は非表示

# (a) 全領域を表示
g
# (b) 指定範囲のみ表示
g + coord_cartesian(xlim = c(3, 4))
# (c) 指定範囲外を欠損値に変換
# 欠損の発生と回帰直線の再計算が起きている旨の警告が発生
g + xlim(3, 4) # scale_x_continuous(xlim = c(3, 4)) と同じ

3-5 凡例/軸のラベルを変更(labs関数)

# 車の重量と燃費の比較
g <- ggplot(mtcars) +
  aes(x = wt,
      y = mpg,
      shape = ifelse(am == 1, "AT車", "MT車")) +
  geom_point(size = 3)

# (a) ラベルの調整なし
g
# (b) ラベルの調整あり
g + labs(
    # 審美的属性ごとのラベル変更
    x = "車重(1000lbs)",
    y = "燃費(miles/(US) gallon)",
    shape = NULL,
    # その他のラベル変更
    title = "車の重量と燃費の関係"
  )

3-6 日本語表示のためのRStudioの設定

3-7 テーマを変えフォントを指定する(theme関数群)

# ミニマルなテーマでフォントにIPAexGothicを使う
ggplot(mtcars) +
  aes(wt, mpg) +
  geom_point() +
  theme_minimal(base_family = "IPAexGothic")
# グラフのテーマをggThemeAssistパッケージを使って調整する
g <- ggplot(mtcars) + aes(wt, mpg) + geom_point()
ggThemeAssist::ggThemeAssistGadget(g)

セッション中のテーマを統一する

# デフォルトフォントをIPAexゴシックに変更する
theme_set(theme_gray(base_family = "IPAexGothic"))
library(magrittr)
systemfonts::system_fonts()$family %>%
  unique %>%
  stringr::str_subset("IPA")

3-8 画像として保存

ggsave関数による画像の保存

# ggplotオブジェクトを変数に保存
g <- ggplot(mtcars) +
  aes(wt, mpg) +
  geom_point()

# ggplotオブジェクトを5cm四方の画像に保存
ggsave(
  filename = "mtcars.png",
  plot = g,
  width = 5,
  height = 5,
  units = "cm"
)
ggsave("example.png", g, device=ragg::agg_png)

RStudioのGUIによる画像の保存

3-9 特定のデータを強調

ラベルで強調(ggrepelパッケージ)

# 車の重量と燃費の比較

# 前処理: 行名をmodel列に変換する
motor_cars <- tibble::rownames_to_column(mtcars, "model")

# 散布図の作成
g <- ggplot(motor_cars) +
  aes(wt, mpg, label = model) +
  geom_point()

# (a) 散布図上のすべての点に車種名をラベリングする
g + ggrepel::geom_text_repel()
# (b) 散布図上の燃費が良い車種をラベリングする
g + ggrepel::geom_text_repel(
  data = function(x) dplyr::slice_max(x, mpg, n = 5)
)

コラム:コードを再利用可能にする自作関数

# 2 * x ^ 2 + x + 1
## 関数を定義せずx = 3とx = 5の場合を計算
f_x3 <- 2 * 3 ^ 2 + 3 + 1
f_x5 <- 2 * 5 ^ 2 + 5 + 1
# 2 * x ^ 2 + x + 1
## 関数を定義してx = 3とx = 5の場合を計算
quadratic <- function(x) {
  2 * x ^ 2 + x + 1
}
f_x3 <- quadratic(x = 3)
f_x5 <- quadratic(5) # 実行時の引数名は省略可能

コラム:自作関数でtidyverseを使う

# mtcarsの任意の列を選んで箱ひげ図にする関数(失敗例)
boxplot_mtcars_ng <- function(x) {
  ggplot(mtcars) +
    aes(x) +
    geom_boxplot()
}
boxplot_mtcars_ng(wt)
# mtcarsの任意の列を選んで箱ひげ図にする関数(成功例1)

## aes関数と同様に引用符を使わず列を選択
boxplot_mtcars_ok1 <- function(x) {
  ggplot(mtcars) +
    aes({{ x }}) + # aes(x) を aes({{ x }}) に修正
    geom_boxplot()
}

## 箱ひげ図の表示
boxplot_mtcars_ok1(wt)
# mtcarsの任意の列を選んで箱ひげ図にする関数(成功例2)

## 文字列で列を選択
boxplot_mtcars_ok2 <- function(x) {
  ggplot(mtcars) +
    aes(.data[[x]]) + # aes(x) を aes(.data[[x]]) に修正
    geom_boxplot()
}

## 箱ひげ図の表示
## 結果は `boxplot_mtcars_ok1(wt)` と同じなので省略
boxplot_mtcars_ok2("wt")
# mtcarsの複数の列に対し箱ひげ図をプロット(結果省略)
for (x in c("wt", "mpg")) {
  print(
    boxplot_mtcars_ok2(x)
    # または boxplot_mtcars_ok1(.data[[x]])
  )
}

色で強調する(gghighlightパッケージ)

g <- ggplot(ChickWeight) +
  aes(Time, weight, color = Diet, group = Chick) +
  geom_line() +
  geom_point()

# (a) gghighlightパッケージ未使用時
g
# (b) gghighlightパッケージにより餌2を強調
g + gghighlight::gghighlight(Diet == 2)
# gghighlightパッケージによる出力の見た目を変える
g +
  gghighlight::gghighlight(
    Diet == 2,
    unhighlighted_params = list(color = "white"),
    use_direct_label = FALSE,
    keep_scales = TRUE
  )
# 車の重量と燃費の比較
# gghighlight関数を用いて燃費の良い車種を強調

# 前処理: 行名をmodel列に変換する
motor_cars <- tibble::rownames_to_column(mtcars, "model")

# ベースとなる散布図の作成
g <- ggplot(motor_cars) +
  aes(wt, mpg) +
  geom_point()
  
# (a) 燃費が上位の車種を強調
g +
  gghighlight::gghighlight(
    rank(-mpg, ties.method = "min") <= 5
  )
# (b) 燃費が上位の車種にラベルを付け、他を濃い灰色にする
g + 
  gghighlight::gghighlight(
    rank(-mpg, ties.method = "min") <= 5,
    label_key = model,  # 強調対象にラベルを付ける
    unhighlighted_params = list(
      color = "gray50") # 強調対象外を濃い灰色にする
  )
# gghighlightを用いないデータの強調(コードのみ)
ggplot(motor_cars) +
  aes(wt, mpg) +
  geom_point(color = "gray80") +
  geom_point(
    color = "black",
    data = function(x) dplyr::slice_max(x, mpg, n = 5)
  )

3−10 グラフ配置によるデータの俯瞰

変数間の関係を把握するために散布図を並べる(facet_wrap関数)

# ダイヤモンドの色合いごとに、カラット数と価格を比較
ggplot(diamonds) +
  aes(carat, price) +
  geom_point() +
  facet_wrap(
    vars(color), # 分割する基準となる変数を指定
    nrow = 1,    # 分割する行数を指定(省略可)
    ncol = 7     # 分割する列数を指定(省略可)
  )
# データの変形

# price列以外のnumeric型の列の値をx_value列にまとめ、
# x_value列のどの値がどの列由来かx_title列に記録する
diamonds_price_vs_others <- diamonds %>%
  tidyr::pivot_longer(
    where(is.numeric) & !price,
    names_to = "x_title",
    values_to = "x_value"
  )

# 結果の確認 
str(diamonds_price_vs_others)
# ダイヤモンドの価格と価格以外の数値の関係を
# 複数の散布図にプロットして並べる
ggplot(diamonds_price_vs_others) +
  aes(x_value, price, color = color) +
  geom_point() +
  facet_wrap(
    vars(x_title),
    scales = "free_x" # x軸の範囲を固定しない
  )

関連するグラフを1枚にまとめる(patchworkパッケージ)

# 3つのグラフをpatchworkパッケージを使って並べた例
library(patchwork)
g <- ggplot(diamonds) + aes(price) + geom_density()
g | (g / g)
# 散布図にy軸の周辺分布を追加する
library(patchwork)

# 並べたいグラフに共通のレイヤ
gg_base <- ggplot(diamonds) +
  coord_cartesian(ylim = range(diamonds$price))

# 散布図の作成
gg_scatter_carat_vs_price <- gg_base +
  aes(carat, price, color = color) +
  geom_point(show.legend = FALSE)

# 周辺分布(ヒストグラム)の作成
gg_hist_price <- gg_base +
  aes(y = price, fill = color) +
  geom_histogram(bins = 30)

gg_scatter_carat_vs_price | gg_hist_price
# グラフのサイズ比を指定する
patchwork::wrap_plots(
  list(            # 並べたいグラフのリスト
    gg_scatter_carat_vs_price,
    gg_hist_price
  ),
  ncol = 2,        # 2列に並べる。余りは改行
  widths = c(3, 1) # 1列目と2列目の幅の比を3:1にする
) +
  patchwork::plot_annotation(
    title="ダイヤモンドの価格とカラット数の関係",
    tag_levels = "a",                  # a, b,...とタグ付け
    tag_prefix = "(", tag_suffix = ")" # タグを括弧で囲う
  )

相関を見るために散布図に色を付ける(patchworkパッケージ)

# 表示色にマッピングする列を変更しながら
# diamondsデータセットのカラット数と価格を比較

# 散布図を描写する関数
scatter_diamonds <- function(color = "cut") {
  ggplot(diamonds) +
    aes(
      carat, price,
      color = .data[[color]] # 後述
    ) +
    geom_point()
}

# diamondsデータセットの中からfactor型の列名を抽出し、
# それぞれを散布図の色に割り当てたグラフを作成し、
# patchworkパッケージで1枚にまとめる
diamonds %>%
  select(where(is.factor)) %>%     # factor型の列を選択
  names %>%                        # 選択中の列名を抽出
  purrr::map(scatter_diamonds) %>% # さまざまな列を表示色にマッピング
                                   # 出力はグラフのリスト
  patchwork::wrap_plots(ncol = 1)  # リストを1枚のグラフにまとめる

コラム:facet_wrap関数で分割したグラフの書式を整理

# stripをx軸タイトルに変更する
ggplot(diamonds_price_vs_others) +
  aes(x_value, price, color = color) +
  geom_point() +
  lemon::facet_rep_wrap(                        # (A)
    vars(x_title),
    scales = "free_x",
    repeat.tick.labels = TRUE,                  # (A.1)
    strip.position = "bottom"                   # (B)
  ) +
  theme_classic() +
  theme(                                        # (C)
    strip.placement = "outside",                # (C.1)
    strip.background = element_blank(),         # (C.2)
    strip.text = element_text(size = rel(1.1)), # (C.3)
    axis.title.x = element_blank()              # (C.4)
  )

3−11 最低限のコードによるグラフの対話的操作

# 対話的に操作可能なグラフの作成
gg_diamonds <- ggplot(diamonds) +
  aes(x = carat, y = price, color = color) +
  geom_point()

plotly::ggplotly(gg_diamonds)
# マウス操作で見てきたグラフをggplot2パッケージで再現

## carat列が0.2から1、price列が0から5000の範囲を拡大
gg_diamonds +
  coord_cartesian(xlim = c(0.2, 1), ylim = c(0, 5000))

## color列がJな列を強調表示
gg_diamonds +
  gghighlight::gghighlight(color == "J")

3−12 まとめ

その他の便利なパッケージ

ggplot2の使い方をさらに学ぶ

書籍

Webページ