Lesson 4-1: ggplot2 による可視化

2018/11/19

前のステップ

Lesson 3-1: dplyr 入門 では次のことを勉強しました。

このセッションの目標

前回は,dplyr の導入を中心に ggplot2 の使い方を簡単に見ました。 今回は,逆に ggplot2 による可視化が中心ではありますが,dplyr の復習も兼ねていきます。

準備

ggplot2tidyverse の一部です。

library(tidyverse)
## ── Attaching packages ───────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
## ✔ ggplot2 3.0.0     ✔ purrr   0.2.5
## ✔ tibble  1.4.2     ✔ dplyr   0.7.7
## ✔ tidyr   0.8.1     ✔ stringr 1.3.1
## ✔ readr   1.1.1     ✔ forcats 0.3.0
## ── Conflicts ──────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

今回もPenn World Table を使っていきます。ダウンロードがお済みでない方は こちら を参考にダウンロードしてください。

pwt90 <- haven::read_dta("Data/pwt90.dta")

一部,gapminder パッケージのデータを使う。

install.packages("gapminder")
library(gapminder)

ggplot2 のグラフ

基本の構造

ggplot2 による可視化の基本部分は次の2つのパートです。

ggplot(<データ>) + 
  geom_*(aes(<パラメータ>))

あるいは,

ggplot(<データ>, aes(<パラメータ>)) + 
  geom_*()
  • ggplot() は座標平面を作成します。+ 記号を使って,座標平面にグラフを重ねていきます。
  • aes() (aesthetic properties)はデータを図の外観的特徴と対応付けます。状況に応じて ggplot() の中に書いたり geom_*() 関数の中に書いたりします。利用する geom_*() に応じて aes() のパラメータが変わります。
  • geom_*() はグラフを作成します。散布図は geom_point(), 折れ線グラフは geom_line() などの グラフの種類に応じた関数が用意されています。

ggplot() の第一引数がデータなので,パイプを使って次のように記述することもできます。

<データ> %>% 
  ggplot() + 
    geom_*(aes(<パラメータ>))

折れ線グラフ

折れ線グラフは geom_line() を使います。geom_line() に対しては,aes() は次のパラメータが使われます。

  • x: 横軸(必須)
  • y: 縦軸(必須)
  • alpha: 不透明度
  • colour/color: 色
  • group: グループ
  • linetype: 線種
  • size: 線の太さ
pwt90 %>% 
  filter(country %in% c("Japan", "United States", "China"), year >= 1960) %>% 
  ggplot() +
    geom_line(aes(x = year, y = rgdpo / pop))

出力

おっと,何かがおかしいですね。

今,3カ国分の時系列データがあって3本の線グラフを描いて欲しかったのですが,国ごとにグループ分けをするという情報が geom_line() に渡されていないので1本のグラフが出来上がってしまいました。

上のコードでは期待通りになりません。aes() に国のデータを渡す必要があります。

カラーで出力できる場合は color を使います。color = country はデータを country ごとにグループ化して,country のデータごとに色を変えるということをやってくれます。

pwt90 %>% 
  filter(country %in% c("Japan", "United States", "China"), year >= 1960) %>% 
  ggplot() +
    geom_line(aes(x = year, y = rgdpo / pop, color = country))

モノクロの場合は線種 linetype を指定します。

pwt90 %>% 
  filter(country %in% c("Japan", "United States", "China"), year >= 1960) %>% 
  ggplot() +
    geom_line(aes(x = year, y = rgdpo / pop, linetype = country))

color, linetype を指定すると自動的にレジェンド(凡例)を作成してくれます。

問題4.1.1

上のデータでは group, alpha はあまり役に立たない。どのようなケースでこれらのパラメータが役に立つだろうか?

散布図

1965年の実質GDPと2014年の実質GDPの関係を見てみたいとしよう。データの準備として次のようなコードを書く。

gdp65to14 <-
  pwt90 %>% 
    filter(year == 1965 | year == 2014) %>% 
    group_by(country, countrycode) %>% 
    summarize(rgdp1965 = first(rgdpo), rgdp2014 = last(rgdpo)) %>% 
    filter(!is.na(rgdp1965)) %>% 
    ungroup()
gdp65to14
## # A tibble: 114 x 4
##    country                          countrycode rgdp1965 rgdp2014
##    <chr>                            <chr>          <dbl>    <dbl>
##  1 Algeria                          DZA           69687.  509313.
##  2 Argentina                        ARG           77557.  862902.
##  3 Australia                        AUS          182392. 1045083.
##  4 Austria                          AUT           78059.  389266.
##  5 Bangladesh                       BGD           83312.  464047.
##  6 Barbados                         BRB            2078.    2901.
##  7 Belgium                          BEL          105858.  448497.
##  8 Benin                            BEN            4416.   22538.
##  9 Bolivia (Plurinational State of) BOL            8248.   61893.
## 10 Botswana                         BWA             298.   33734.
## # ... with 104 more rows

1965年の実質GDPのデータがある 114カ国について,横軸に rgdp1965, 縦軸に rgdp2014 をプロットしてみよう。下のコードは実際には対数値をプロットしていることに注意。対数を取らないとグラフが読みにくくなる。

散布図を作るときには geom_point() を使用する。

ggplot(gdp65to14) + 
  geom_point(aes(x = log(rgdp1965), y = log(rgdp2014)))

geom_point()aes() に次のパラメータを指定できる。

  • x (必須)
  • y (必須)
  • alpha
  • colour
  • fill
  • group
  • shape
  • size
  • stroke

スケール

先程は log() を掛けたデータをプロットしたが,データを変更するのではなく軸のスケールを変換することもよくある。

ggplot(gdp65to14) + 
  geom_point(aes(x = rgdp1965, y = rgdp2014)) +
  scale_x_log10() +
  scale_y_log10()

テキスト

特徴的な点をさして「それはどの国か?」という質問を受けることがある。点では国名がわからなくなるので,点の代わりにラベルを「散布」するやりかたもある。そのようなケースでは,geom_text() を使う。x, y, label が必須の aes パラメータである。

ggplot(gdp65to14) + 
  geom_text(aes(x = rgdp1965, y = rgdp2014, label = countrycode)) +
  scale_x_log10() +
  scale_y_log10()

回帰直線

散布図はおおよそ直線に並んでいたので,ggplot2 には回帰直線を簡便に書く方法がある。それが geom_smooth という関数である。以下の例では,同じ aes(x = ...., y = ....)geom_point()geom_smooth() で2回使うので,ggplot()aes() を書いている。

ggplot(gdp65to14, aes(x = rgdp1965, y = rgdp2014)) + 
  geom_point() +
  geom_smooth(method = lm, se = FALSE) +
  scale_x_log10() +
  scale_y_log10()

method = lm は linear model (線形回帰モデル)によって近似曲線を計算するという意味である。小さいデータ(観測が1000点以下)に対してデフォルトは LOESS (loess 関数を使う)となっている。詳しくはモデルの話が出てきたときに説明する予定である。

ヒストグラム

所得の分布を知りたいとしよう。ヒストグラムは横軸方向に rgdp2014 を取って,縦軸方向にはデータのカウントを取るのだから aes(x = rgdp2014) を使うと予想できるだろう。実際,geom_histogram() は次のように使う。

ggplot(gdp65to14) +
  geom_histogram(aes(x = rgdp2014)) + 
  scale_x_log10()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ここで,scale_x_log10() で対数スケールにしていることに注意せよ。横軸の分割数は自動的に計算されている。これを変えたい場合には,

ggplot(gdp65to14) +
  geom_histogram(aes(x = rgdp2014), bins = 20) + 
  scale_x_log10()

ヒストグラムをカウントデータではなく確率にしたい場合は aes(y = stat(density) または aes(y = ..density..)geom_histogram() にわたしてやる。密度推定により滑らかな曲線で分布を近似したい場合は,geom_density() を使う。

ggplot(gdp65to14, aes(x = rgdp2014)) +
  geom_histogram(aes(y = stat(density))) + 
  geom_density() +
  scale_x_log10()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

ファセット

ファセット・ラップ

1970年から2010年まで, 10年おきのデータを使おう。世界の所得分布がどのように推移してきたかを見てみたいとしよう。このような場合には facet_wrap() を使う。year が異なるデータをグループ化して,個別にグラフを作成してくれる。

pwt90 %>% 
  filter(year %in% c(1970, 1980, 1990, 2000, 2010)) %>% 
  ggplot() + 
    geom_histogram(aes(x = rgdpo)) + 
    scale_x_log10() + 
    facet_wrap(~ year)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## Warning: Removed 56 rows containing non-finite values (stat_bin).

ファセット・グリッド

所得分布の変化を大陸ごとに描きたいとしよう。そのような場合には facet_grid() という関数を使う。年と大陸という2つの列でデータを分類し,それぞれの分類ごとにデータをプロットすることができる。残念ながらPWTには大陸の情報がないので,gapminder を使おう。

gapminder %>% 
  filter(year %in% c(1967, 1987, 1997, 2007)) %>% 
  ggplot() + 
    geom_histogram(aes(x = gdpPercap)) + 
    scale_x_log10() + 
    facet_grid(continent ~ year)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

配布

自分が図を見て分析のヒントにする場合には図の見栄えにはあまりこだわらないほうがよいのだが,他人が見る図には多少気を使う必要もあるだろう。ここでは,ggplot2 のグラフを装飾する方法に簡単に触れる。

軸のラベル

軸のラベルを設定したいときは labs() という関数を使う。

pwt90 %>% 
  filter(country %in% c("Japan", "United States", "China"), year >= 1960) %>% 
  ggplot() +
    geom_line(aes(x = year, y = rgdpo / pop, color = country)) +
    labs(
      x = "Year",
      y = "GDP per capita"
    )

日本語のラベルを使わなければいけない場合には次のようにする。レポートに図を挿入する場合はキャプションがあるはずだが,図単体で使用する場合にはタイトルを付けておくと何かと便利かもしれないので,タイトルも付けておこう。

theme_set(theme_gray(base_family="HiraKakuProN-W3"))  # 環境に応じてフォントを変える

pwt90 %>% 
  filter(country %in% c("Japan", "United States", "China"), year >= 1960) %>% 
  ggplot() +
    geom_line(aes(x = year, y = rgdpo / pop, color = country)) +
    labs(
      title = "経済成長",
      x = "年",
      y = "ひとりあたりGDP"
    ) 

フォントの設定は extrafont というパッケージを利用するとよいようです。https://cran.r-project.org/web/packages/extrafont/README.html

散布図の一部にラベルを付ける

散布図の一部にラベルを付けることが便利なことがある。この場合は,ラベルを付けたい部分集合のデータを定義する。

ggrepel というパッケージの geom_label_repel() を使うと読みやすいラベルを付けてくれるのでオススメである。

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

world <- pwt90 %>% 
  filter(year >= 1965) %>% 
  mutate(rgdpo_pc = rgdpo / emp) %>% 
  group_by(country) %>% 
  summarize(avg_growth = mean((log(rgdpo_pc)) - lag(log(rgdpo_pc)), na.rm = TRUE),
            rgdpo_pc_1965 = first(rgdpo_pc)) %>% 
  filter(!is.na(rgdpo_pc_1965)) 

world_3 <- world %>% 
  filter(country %in% c("Japan", "United States", "United Kingdom"))

ggplot(world, aes(rgdpo_pc_1965, avg_growth)) +
  geom_point(size = 3) +
  geom_label_repel(data = world_3, aes(label = country)) +
  geom_point(data = world_3, size = 5, shape = 1) + 
  labs(
    x = "GDP per worker in 1965",
    y = "Average GDP growth rate, 1965-2014"
  ) +
  scale_x_log10()

保存

ggplot2 の画像を保存するには ggsave() を使うのが基本である。

ggsave("Graphics/figure.pdf", width = 6, height = 6 / 1.618)

日本語フォントの保存がうまく行かない場合には他の方法もある。https://oku.edu.mie-u.ac.jp/~okumura/stat/ggplot2.html などを参照。

さらに詳しく学ぶために

このレッスンでは ggplot2 の機能の一部を紹介しました。さらに詳しく学びたい方は Wickham and Grolemund, R for Data Science

が参考になります。分からないことがあったら ggplot2 のホームページはよい出発点です。