読者です 読者をやめる 読者になる 読者になる

Mukku John Blog

取り組んでいること を つらつら と

ggplot2を使って、注釈を入れる-2

R グラフィックス クックブック 21回目

ggplot2パッケージを利用して、図内に注釈を入れます。

前回の記事で、annotate()を使ってテキストや線分、網掛けや
gridパッケージを使って矢印を、図内にプロットしました。
mukkujohn.hatenablog.com


今回は、色をあえて変えることでの強調表示やエラーバー - Wikipediaを扱います。

強調表示

こちらのデータセットを使います。
R: Results from an Experiment on Plant Growth

> str(PlantGrowth)
'data.frame':	30 obs. of  2 variables:
 $ weight: num  4.17 5.58 5.18 6.11 4.5 4.61 5.17 4.53 5.33 5.14 ...
 $ group : Factor w/ 3 levels "ctrl","trt1",..: 1 1 1 1 1 1 1 1 1 1 ...

強調表示を行う下準備として、group列の値が"trt1"である事を示す列を追加します。

pg <- PlantGrowth
pg$h1 <- "no"
pg$h1[pg$group=="trt1"] <- "yes"

> str(pg)
'data.frame':	30 obs. of  3 variables:
 $ weight: num  4.17 5.58 5.18 6.11 4.5 4.61 5.17 4.53 5.33 5.14 ...
 $ group : Factor w/ 3 levels "ctrl","trt1",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ h1    : chr  "no" "no" "no" "no" ...

h1列でグループ化した箱ひげ図をプロットします。

p <- ggplot(pg,aes(x=group,y=weight,fill=h1)) +
  geom_boxplot()
p

f:id:MukkuJohn:20160929200129p:plain
このグラフですと、どのグループの箱を見れば良いか分かりません。

そこであえて、塗りつぶしの色を手動で設定することで、
目につく箱を指定します。

手動で塗りつぶし色を指定するためには、scale_fill_manual()を使います。

#yes or no の2つの値であるh1をfillにマッピングしているため、
#塗りつぶし色も2色を指定する
p + scale_fill_manual(values=c("grey85","#FFDDCC"))

f:id:MukkuJohn:20160929200622p:plain

3levelsのgroup列をfillにマッピングした場合は、
scale_fill_manual()で3色指定します。

ggplot(PlantGrowth,aes(x=group,y=weight,fill=group)) +
  geom_boxplot() +
  scale_fill_manual(values=c("grey85","lightblue","#FFDDCC"))

f:id:MukkuJohn:20160929200836p:plain

levels数より多く指定しても、多い部分は無視されて図がプロットされます。

逆にlevels数に満たない場合はエラーになります。

Error: Insufficient values in manual scale. 3 needed but only 1 provided.

エラーバー

こちらのデータから、Cultivarがc39であるデータセットを使います。

> str(cabbage_exp)
'data.frame':	6 obs. of  6 variables:
 $ Cultivar: Factor w/ 2 levels "c39","c52": 1 1 1 2 2 2
 $ Date    : Factor w/ 3 levels "d16","d20","d21": 1 2 3 1 2 3
 $ Weight  : num  3.18 2.8 2.74 2.26 3.11 1.47
 $ sd      : num  0.957 0.279 0.983 0.445 0.791 ...
 $ n       : int  10 10 10 10 10 10
 $ se      : num  0.3025 0.0882 0.311 0.1408 0.2501 ...

subset()を使って、条件に合致するデータだけ選択・取得します。

ce <- subset(cabbage_exp, Cultivar == "c39")

棒グラフを作成します。

ggplot(ce, aes(x=Date,y=Weight)) +
  geom_bar(fill="white",colour="black",stat = "identity") 

f:id:MukkuJohn:20160929201857p:plain

上のグラフにエラーバーを追加しますが、
データセット内に、平均の標準偏差であるse列があるため、
この値を、geom_errorbar()を使って、追加します。

#y軸に対して、開始値:ymin~終了値:ymaxとして指定します。
#widthは、エラーバーの幅です。良さげな値を模索して指定します。
上のグラフ +
  geom_errorbar(aes(ymin=Weight-se,ymax=Weight+se),width=.2)

f:id:MukkuJohn:20160929202131p:plain

折れ線グラフでも同様です。

#折れ線→マーク→エラーバーの順でプロットしてます。
ggplot(ce, aes(x=Date,y=Weight)) +
  geom_line(aes(group=1)) +
  geom_point(size=4) +
  geom_errorbar(aes(ymin=Weight-se,ymax=Weight+se),width=.2)

f:id:MukkuJohn:20160929202606p:plain

このエラーバーですが、グループ化された棒グラフや折れ線グラフの場合、
pisitionを横にずらす必要があります。

ずらさないと、エラーバーがどちらのグループのバーか分かりません。
f:id:MukkuJohn:20160929202741p:plain

エラーバーをずらす場合は、geom_errorbar()内で
positionを指定します。

ggplot(cabbage_exp, aes(x=Date,y=Weight,fill=Cultivar)) +
  geom_bar(position="dodge",stat="identity")+
  geom_errorbar(aes(ymin=Weight-se,ymax=Weight+se),
                position = position_dodge(.9),width=.2)

f:id:MukkuJohn:20160929203018p:plain

折れ線グラフ

#複数回利用するため、オブジェクトに格納
pd <- position_dodge(.3)
#エラーバー → 折れ線 → マークの順にプロット
ggplot(cabbage_exp,aes(x=Date,y=Weight,colour=Cultivar,group=Cultivar))+
  geom_errorbar(aes(ymin=Weight-se,ymax=Weight+se),
                width=.2,size=.25,colour="black",position=pd)+
  geom_line(position=pd)+
  geom_point(position=pd,size=2.5)

f:id:MukkuJohn:20160929203248p:plain

ファセット

各ファセットに注釈を入れていきます。
mpg. ggplot2 2.1.0データセットを使います。

> str(mpg)
'data.frame':	234 obs. of  11 variables:
 $ manufacturer: Factor w/ 15 levels "audi","chevrolet",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ model       : Factor w/ 38 levels "4runner 4wd",..: 2 2 2 2 2 2 2 3 3 3 ...
 $ displ       : num  1.8 1.8 2 2 2.8 2.8 3.1 1.8 1.8 2 ...
 $ year        : int  1999 1999 2008 2008 1999 1999 2008 1999 1999 2008 ...
 $ cyl         : int  4 4 4 4 6 6 6 4 4 4 ...
 $ trans       : Factor w/ 10 levels "auto(av)","auto(l3)",..: 4 9 10 1 4 9 1 9 4 10 ...
 $ drv         : Factor w/ 3 levels "4","f","r": 2 2 2 2 2 2 2 1 1 1 ...
 $ cty         : int  18 21 20 21 16 18 18 18 16 20 ...
 $ hwy         : int  29 29 31 30 26 26 27 26 25 28 ...
 $ fl          : Factor w/ 5 levels "c","d","e","p",..: 4 4 4 4 4 4 4 4 4 4 ...
 $ class       : Factor w/ 7 levels "2seater","compact",..: 2 2 2 2 2 2 2 2 2 2 ...

この形でマッピングします。

  • x軸:displ-ピストンが動ける容積(リットルで表現)
  • y軸:hwy-ガロンあたり走れる距離(マイルで表現)(高速で実験)
  • ファセット:drv-駆動方式

まずは、上記のマッピングでプロットします。

p <- ggplot(mpg, aes(x=displ,y=hwy)) + 
  geom_point() + 
  facet_grid(. ~ drv)
p

f:id:MukkuJohn:20160929205822p:plain

各ファセットに注釈を追加するには、
geom_text()と注釈用のデータフレームを使います。

#上のグラフは、drvでファセットにしているので
#「drv」という列名と値を合わせる必要があります。
f_labels <- data.frame(drv = c("4","f","r"), 
                       label = c("4wd","Front","Rear"))
> f_labels
  drv label
1   4   4wd
2   f Front
3   r  Rear

p + geom_text(x=6, y=40,aes(label=label),data=f_labels)

f:id:MukkuJohn:20160929210209p:plain

ファセットに指定した"drv"列の値と
注釈用データフレームの"drv"列の値が一致させる必要があります。

一致していないとこんなグラフになってしまいます。

#あえて、FFを指定して、一致させないようにしています。
ng_lables <- data.frame(drv=c("4","FF","r"), 
                        label = c("4wd","Front","Rear"))
p + geom_text(x=6, y=40,aes(label=label),data=ng_lables)

f:id:MukkuJohn:20160929211005p:plain

注釈に使うデータフレームですが、ファセットに指定した列以外に、
複数の列を用意して、geom_text()を複数回指定できます。

#この様に、drv列以外に、formula列とr2列を用意します
> labels
  drv                              formula                  r2
1   4 italic(y) == 30.68 -2.88 * italic(x) italic(R^2) == 0.65
2   f italic(y) == 37.38 -3.60 * italic(x) italic(R^2) == 0.36
3   r italic(y) == 25.78 -0.92 * italic(x) italic(R^2) == 0.04

#回帰式を引いて
p + geom_smooth(method=lm,se=FALSE)+
#formula列の値を数式として追加して
  geom_text(x=3,y=40,aes(label=formula),
            data=labels,parse = TRUE,hjust=0) +
#r2列の値も数式として追加します
  geom_text(x=3,y=35,aes(label=r2),
            data=labels,parse = TRUE,hjust=0)

f:id:MukkuJohn:20160929212346p:plain


geom_text()を使って注釈を追加しましたが、
annotate()を使うとどうなるか確認してみます。

#テキスト
p + annotate("text",x=6,y=42,label="label text")

f:id:MukkuJohn:20160929211614p:plain

#線分
p + annotate("segment",x = 3,xend = 6,y=30,yend=40)

f:id:MukkuJohn:20160929211648p:plain

#網掛け
p + annotate("rect",xmin = 3,xmax = 6,ymin=30,ymax=40,
             fill="lightblue",alpha=.3)

f:id:MukkuJohn:20160929211719p:plain

annotate()を使うと全ての図に同じ内容が適用されますね。
前回の記事では、geom_text()を使うと、データの数だけプロットされました。

ファセットの場合は、geom_text()とデータフレームの組み合わせが正しいようです。


注釈を入れるのは、この記事で終わりです。
次回の記事からは、を制御する方法を扱っていきます。