ill-identified diary

所属組織の業務や見解とは一切無関係なアフィリエイト付きメモ帳。所属とは関係ないけどここを見て所属先にも興味を持っていただけると喜びます。

ggplot2 で積み上げ折れ線グラフ(エリアプロット)を作成する方法

概要

時系列データセットを加工し, ggplot2 で作図する場合を例に, 以下のことに言及する.

  • ggplot2で複数の系列の折れ線を1つのグラフに表示する方法
  • さらにそれを積み上げグラフ(エリアプロット)に変形する方法
  • デザインを洗練する方法 -- 色系統の変更 -- 凡例の変更 -- 軸の目盛り数値のフォーマット変更

ただし, Rやggplot2 のごく基本的な使用方法を知っていることが前提. ggplot2 の基本は ggplot2 の自分用メモ集を作ろう あたりで.

下準備

まずはパッケージとデータを読み込む.

library(ggplot2)
library(reshape2) # melt 関数は ggplot で複数系列のデータをプロットできるように変換するのに必要

データの用意

積み上げグラフをプロットするには, まず複数系列のデータを1つのグラフ内に収めなければならない. よって, 先に複数系列のデータをグラフ化する方法を書いておく. まずはデータを用意.

data(Seatbelts) # イギリスの交通事故犠牲者
df <- data.frame(Seatbelts, year=rep(1969:1984,each=12) ) # データフレームへ変換

#年ごとの合計に簡略化 (要 plyr パッケージ)
library(plyr) 
df.yearly <- ddply(df, summarise, .variables="year", drivers=sum(drivers), front=sum(front), rear=sum(rear), .progress="text")
detach("package:plyr")

他のデータセット持って来いと言われそうだが, 積み上げ折れ線グラフの作例にはイギリス国内の交通事故犠牲者の時系列データ Seatbelt を使用. 1969-84年までの月ごとのデータ. driversKilled に運転手の死亡・重傷者, front に前部座席に座っていた乗員の事故死亡者・重傷者 後部座席に座っていた乗員の死亡・重傷者が rear に格納されている. ただ, そのままでは見づらいので, plyr パッケージで年単位の合計に加工しなおした. データフレーム df.yearly に変換したものをもとに, まずはこの3つの時系列データの推移を折れ線グラフで表す. ggplot() 関数にデータフレームを与え, geom_line() を3回用いてでそれぞれの系列に分けて表す, という方法でも複数系列のプロットはできる. しかし ggplot2 には複数系列のプロット用に自動で色分けしたり凡例を設定したりしてくれる機能があるため, これに則った構文を用いたほうが便利である. ただし, そのためにはデータフレームの構造を変えることが必要.

df.yearly オブジェクトは,

  year drivers front rear
1 1969   19951 11373 4912
2 1970   21939 12527 5307
3 1971   22309 12047 5244
.
.
.

というふうなデータフレームになっている. ggplot2 で複数の時系列データを扱う時, y軸の値を同一の列に収め, それぞれの値が元の系列のどれであったかを記録する列を別に作る. まずは実際にやってみた方が分かりやすい. reshape2 パッケージに含まれる melt 関数でこれを実行する.

library(reshape2)
df.melt <- melt(data=df.yearly, id.vars="year", measure.vars=c("drivers", "front", "rear")) 
head(df.melt)
  year variable value
1 1969  drivers 19951
2 1970  drivers 21939
3 1971  drivers 22309
.
.
.

このように, variable 列に元の系列の名前が, value 列にy軸の値が格納される. melt 関数では, id.vars 引数に x 軸に対応する列を指定し, measure.vars に元のデータフレームの列のうち, value 列に格納したいものを与える. また, 出力後の variabel, value という列名はそれぞれ変更可能だが, 変更にあまり意味はないと思うので省略.

図の作成

melt 関数で形成した複数系列のデータをプロットするには, 本質的には以下のコードだけでいい.

ggplot(df.melt) + geom_line(mapping=aes(x=year, y=value, group=variable))

f:id:ill-identified:20140222183348p:plain

group 引数に variable を与えることで, ggplot が系列を識別してくれる. ただし, これだけではどれがどの系列に対応するかがわかりづらい. そこで, 線種を指定する linetype もしくは色を指定する color 引数にも variable を与える.

ggplot(df.melt) + geom_line(mapping=aes(x=year, y=value, group=variable, linetype=variable, color=variable))

f:id:ill-identified:20140222183400p:plain

ラベル名を日本語にしたいとか, 色が毒々しいとかデザインに関するものは後回しにして, 先に積み上げグラフのプロット例も記す. 複数系列の折れ線グラフを積み上げ折れ線グラフにするのも非常に単純で, geom_line() に position="stack" を与えるだけで良い (aes の引数でないことに注意). さらに, 線だけでは積み上げグラフであると認識しづらいため, 各領域を塗りつぶすために, geom_ribbon 関数を使う. こちらにも, group=variable, position="stack" の指定が必要なのに加え, 各領域を塗りつぶす色も折れ線と統一するため, fill=variable とする. また, 輪郭をはっきりさせるため, linetype の指定はせず, また geom_ribbon() の alpha 引数を指定して塗りつぶしを半透明にしている.

(g.stack <- ggplot(df.melt) + geom_line(mapping=aes(x=year, y=value, group=variable, color=variable), position="stack")  + geom_ribbon(mapping=aes(x=year,  y=value, ymin=0, ymax=value, group=variable, fill=variable), position="stack", alpha=.7) )

f:id:ill-identified:20140222183424p:plain

position="stack" とした場合, aes に新たに ymax 引数を与えることになる. geom_line 関数ならば ymax を与えなくとも自動で y で代用してくれるが, geom_ribbon は挙動がおかしくなるので入れておく. geom_* 関数は, 基本的どれも position 引数の指定だけで積み上げグラフとして扱ってくれる. 例えば, 上のグラフに geom_text 数値を表記したい場合も, position="stack" としない場合, 積み上げグラフとテキストラベルの位置がずれることになる.

デザインの洗練

軸ラベル・凡例の設定

軸のラベルを変更する場合, xlab, ylab 関数があるが, labs 関数でラベルをまとめて変更できる. また, title で表題を指定できる. 凡例のタイトル (ここでは variable) は color, fill, linetype それぞれに設定できる. 一方なくすには theme() 関数に legend.title=element_blank() を与える. また, 凡例のラベルを変更する場合, scale_* 系の関数を使うことになる. 凡例のラベルも color, fill, ... ごとに設定されているので, 今回は塗りつぶし色の fill のみに設定し, 残りの linetype, color に対しては guide=F を与えることで非表示にしている. また, 凡例の並びとグラフの積み上がっている順序が逆なのも気になる. そこで, guide=guide_legend(reverse=T) を使って凡例の順序を逆転させた

legend.labels <- c("運転手", "前部座席乗員", "後部座席乗員")
( graph2 <- g.stack + labs(x="年", y="死傷者数(人)", title="イギリスにおける交通事故死傷者数" )  + scale_fill_discrete(name="", labels=legend.labels, guide = guide_legend(reverse=T) ) + scale_linetype_discrete(guide=F) + scale_color_discrete(guide=F) + theme_bw()  )

軸の目盛りの変更

また, scales パッケージ併用で目盛り数値を変更することも可能. 例えばあまりに大きな数字が含まれていると, 数値が自動的に指数表記になるため, それが困るという場合に利用できる.

library(scales)
graph2 + scale_y_continuous(labels=comma) # y軸に3桁区切りコンマを付ける
graph2 + scale_y_continuous(labels=dollar) # y軸に3桁区切りのコンマを付け, ドル表記する
graph2 + scale_y_continuous(labels=percent) # y軸をパーセントとして扱う

# 応用
graph2 + scale_y_continuous(labels=function(x) paste("¥", format(x, big.mark=","), sep="")) # scales を使わず, 円記号と3桁区切りコンマを付ける

scale_x_*() もしくは scale_y_*() の labels 引数には, 任意の文字列を与えても良い.

element_text() 関数は文字の大きさや角度, 位置を調整できる. よって, これを使えば y軸ラベルを回転させ, 横書きにし, ラベルを上の方に持ってくることで日本で一般的な (?) 見た目にできる. 詳しくは element_text() および theme() 関数のヘルプ参照.

graph2 + theme(axis.title.y = element_text(angle = 90*0, vjust = 1) ) # vjust で [0,1] の範囲で垂直方向の位置調整

# この例ではグラフの左側が間延びするのでラベルを改行して調整. タイトルも改行可能.
graph2 + theme(axis.title.y = element_text(angle = 90*0, vjust = 1) ) + ylab("死傷者数\n(人)")

f:id:ill-identified:20140222184350p:plain

色の設定

塗りつぶし色を同系統にしたい場合, scale_fill_brewer() 関数でカラーパレットを指定できる. ただし, 今回は geom_line の線の色である color も scale_color_brewer() で変更したほうが良い.

graph2 + scale_fill_brewer(palette = "Greys") + scale_color_brewer(palette="greys")

カラーパレットについては, RColorBrewer パッケージからコントラストのはっきりしたものを利用できる.

参考