Mukku John Blog

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

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

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

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

注釈

データの解釈をサポートするために、図内に注釈を入れるのですが、
注釈自体は、いろいろあります。

  1. テキスト(文字列、数式)
  2. 線(線分、矢印)
  3. 網掛け
  4. 強調
  5. エラーバー

これらを、1個ずつ取り上げます。

最後にファセットグリッドで、グループ化された全ての図に
注釈を入れる方法を取り上げます。

テキスト(文字列、数式)

annotate()を使います。
第1引数に、textを指定します。

まずは、注釈を入れる図を用意します。
データセットR: Old Faithful Geyser Dataを使います。

p <- ggplot(faithful, aes(x=eruptions, y=waiting)) +
  geom_point()

p

f:id:MukkuJohn:20160928214115p:plain

文字列の注釈を入れます。

#注釈を入れる位置を、x軸/y軸の値で指定します。
#文字列は、label引数に指定します。
p + 
  annotate("text", x=3,   y=48, label="Group 1") +
  annotate("text", x=4.5, y=65, label="Group 2")

f:id:MukkuJohn:20160928214458p:plain

x軸/y軸が、連続値の場合は、Infを用いて、
図のコーナーに文字列を配置することができます。

コーナーに配置する場合は、hjustvjustで調整する必要があります。

p +
  annotate("text",x=-Inf,y=Inf,label="Upper Left",hjust=-.2,vjust=2) +
  annotate("text",x=mean(range(faithful$eruptions)),y=-Inf,label="Bottom Middle",vjust=-.4)

f:id:MukkuJohn:20160928215021p:plain

注釈文字列のフォントや斜体、色やサイズも指定できます。

p + 
  annotate("text", x=3, y=48, label="Group 1",
           family="serif",fontface="italic",colour="darkred",size=3) +
  annotate("text", x=4.5, y=65, label="Group 2",
           family="serif",fontface="italic",colour="blue",size=10)

f:id:MukkuJohn:20160928214723p:plain


文字列を注釈に入れる際には、geom_text()は使わない方が良いです。
geom_text()は、データの数だけ、繰り返し実行されてしまいます。

#Group 2の文字列がオーバープロットです。
#透過を0.1に指定していますが、
#透過が分からないくらい重ねてプロットされています。
p + 
  annotate("text", x=3, y=48, label="Group 1",
           family="serif",fontface="italic",colour="darkred",size=10) +
  geom_text(x=4.5,y=65,label="Group 2", alpha=.1)

f:id:MukkuJohn:20160928215219p:plain


文字列に数式表現を使う場合は、parse引数に、TRUEを指定します。

p <- ggplot(data.frame(x=c(-3,3)),aes(x=x)) + 
  stat_function(fun = dnorm)

#parse=TRUEで、labelが数式表現であることを指定します。
p + annotate("text", x=2, y=0.3, parse=TRUE,
             label="frac(1,sqrt(2 * pi)) * e ^ {-x^2 / 2}") 

f:id:MukkuJohn:20160928215608p:plain

数式表現内で、プレーンテキストを使う場合は、プレーンテキスト部分を
シングルクォーテーションでくくります。

#Function: 部分がプレーンテキストです。
p + annotate("text", x=2, y=0.3, parse=TRUE,
             label="'Function: ' * y==frac(1,sqrt(2 * pi)) * e ^ {-x^2 / 2}") 

f:id:MukkuJohn:20160928215740p:plain

線(線分、矢印)

線により使う関数が異なります。

  1. 水平線:geom_hline()
  2. 垂直線:geom_vline()
  3. 傾きがある線:geom_abline()

このデータセットを使います。
R: Risk Factors Associated with Low Infant Birth Weight

グループ化に用いる列をファクタにして、ラベルを設定します。

birthwt1 <- birthwt
birthwt1$smoke <- factor(birthwt1$smoke) 
birthwt1$smoke <- revalue(birthwt1$smoke, c("0"="No Smoke","1"="Smoke"))

まずは、注釈なしでプロットします。

p <- ggplot(birthwt1, aes(x=age,y=bwt,colour=smoke)) + 
  geom_point()
p

f:id:MukkuJohn:20160928220354p:plain

水平線と、垂直線を追加します。

#線を引く位置を、水平線ならy軸の値、垂直線ならx軸の値で指定します。
p + geom_hline(yintercept=2000) + geom_vline(xintercept=20)

f:id:MukkuJohn:20160928220616p:plain

傾きのある線を追加します。

#y=slope * 100 + interceptの傾きのある線を図示します。
p + geom_abline(intercept=500,slope=100) 

f:id:MukkuJohn:20160928221126p:plain

別途、算出した値の線を追加することもできます。
グループ化している項目ごとに、平均値を算出します。

library(plyr)
bw_means <- ddply(birthwt1,"smoke",summarise,bwt=mean(bwt))
bw_means
     smoke      bwt
1 No Smoke 3055.696
2    Smoke 2771.919

上で算出した、グループ化項目と平均値のデータフレームを
水平線に設定します。

p + geom_hline(aes(yintercept=bwt,colour=smoke),data=bw_means,
               linetype="dashed",size=1)

f:id:MukkuJohn:20160928221442p:plain

軸が離散値の場合は、ファクタのレベルを指定することで
水平線、垂直線を追加できます。

x軸が離散値の図を、サンプルに使います。

pb <- ggplot(birthwt1,aes(x=smoke,y=bwt)) + geom_point()
pb

f:id:MukkuJohn:20160928221739p:plain

離散値としているsmoke列は、ファクタで、
1つ目のNo Smokeのレベルは1、2つ目のSmokeのレベルは2となっています。

> str(birthwt1$smoke)
 Factor w/ 2 levels "No Smoke","Smoke": 1 1 2 2 2 1 1 1 2 2 ...

ファクタのレベルを指定することで、線を引くことができます。

#ファクタのレベルを数値で指定します。
pb + geom_vline(xintercept=2)

f:id:MukkuJohn:20160928222044p:plain

上は、ファクタのレベルを数値で指定しましたが、
which(levels())を用いることで、名称から指定することもできます。

pb + 
  geom_vline(xintercept = which(levels(birthwt1$smoke)=="No Smoke"))

f:id:MukkuJohn:20160928222219p:plain


今までは「線」を扱ってきましたが、ここからは「線分」を追加していきます。
線分は、annotate()を使い、引数に、"segment"を指定します。

この図を使います。
f:id:MukkuJohn:20160928222522p:plain

線分を追加するには、x軸の開始値と終了値、y軸の開始値と終了値を指定します。

#x軸は、x~xendで指定します。
#y軸は、y~yendで指定します。
上のグラフ + annotate("segment", x=1950,xend=1980, y=-.25,yend=0.25,
             colour="blue", size=3)

f:id:MukkuJohn:20160928222903p:plain

矢印や、線分の両端をT字にするためには、gridパッケージのallow()を使います。

library(grid)
#矢印はarrow()
p + annotate("segment",x=1850,xend=1820,y=-.8,yend=-.95,colour="blue",
             size=2,arrow=arrow())+
#両端がT字にするには、ends引数を指定します。
  annotate("segment",x=1950,xend=1980,y=-.25,yend=-.25,
           arrow=arrow(ends = "both",angle = 90,length = unit(.2,"cm")))

f:id:MukkuJohn:20160928223226p:plain

網掛け

annotate()を使います。引数は、rectです。
範囲の指定が、線分とちょっと異なります。

#x軸は、xmin~xmaxで指定します
#y軸は、ymin~ymaxで指定します
p + annotate("rect",xmin=1950,xmax=1980,ymin=-1,ymax=1,
             alpha=.1,fill="blue") +
  annotate("rect",xmin=1800,xmax=2000,ymin=-1,ymax=-.5,
           alpha=.1,fill="green")

f:id:MukkuJohn:20160928223435p:plain


今までの注釈は、端から端までの線を除き、全てannotate()を使っています。

  • 文字列:annotate("text",~)
  • 線分:annotate("segment",~)
  • 網掛け:annotate("rect",~)

注意点は、データの数だけプロットされるgeom_text()を使わないことです。


次回は、強調表示とエラーバー、グリッドで分割された図を扱います。