yokaのblog

湖で微生物の研究してます

Rの作図におけるベストな配色の選び方

論文のFigはほぼRで描いているのだけど、複雑なデータをコンパクトに見せるためにカラフルな図を作ることが多い。そこでいつも悩むのが「いかに効率よく配色するか」ということだ。カスタムの配色セットを作ってみたり、カラーパレットのパッケージをあれこれ試してみたりしたのだけど、自分なりに今落ち着いているのがkhromaとcirclizeという2つのパッケージなので簡単に紹介したい。

khromaはPaul Tol’s Colour Schemesに準じたカラーパレットを出力できるパッケージだ。このカラースキームの特長として、

  • カラーユニバーサル
  • モノクロ印刷した際の視認性も考慮
  • 質データ(Qualitative)、2極データ(Diverging)、連続データ(Sequential)のそれぞれに対応した複数のカラーパレットが準備されている

という点が挙げられる。自分が知る限りでは、最も綿密な考慮の上で選ばれた配色セットであり、色の数や種類に選択肢があるのも嬉しい。詳細は本家のウェブサイトを参照。

一方のcirclizeは、名前の通り環状レイアウトの作図が本来の用途なのだけど、これに入っているcolorRamp2という関数がヒートマップ等の連続データの配色で大活躍する。ちなみに今回は紹介しないけど、circlizeの作者が公開しているパッケージにComplexHeatmapという(その名の通り)複雑なヒートマップを手軽に描ける素晴らしいパッケージもあって、この中でもcolorRamp2の使用が推奨されている。

khromaによるカラーパレットの出力

colourという関数で直接カラーパレットを呼び出すことができる。

require("khroma")
col.pal<-colour("palette名")
col.pal("色数")

ポイントとしては、colourが出力するのは色ではなく、カラーパレットが入った関数であり、色を呼び出すためにはその関数に「色の数」を渡してやる必要があるということだ。palette名にはPaul Tol’s Colour Schemesで示されている名がそのまま使える。具体的には以下の通り。

Qualitative data
bright (7), contrast (3), vibrant (7), muted (9), pale (6), dark (6), light (9).
Diverging data
sunset (11), BuRd (9), PRGn (9).
Sequential data
YlOrBr (9), iridescent (23), discrete rainbow (23), smooth rainbow (34).

ここで、リストの()内は各カラーパレットの色の数を示す。Qualitative (質)データの場合は用意された色数を超える値を設定することはできないけど、Diverging (2極)データとSequential(連続)データにおいては、大きな値を入れると自動的に中間的な色を補ってその数に合わせた色数を返してくれる。具体例としてはこんな感じだ。

col.pal<-colour("bright")
col.pal(7)
#"#4477AA" "#EE6677" "#228833" "#CCBB44" "#66CCEE" "#AA3377" "#BBBBBB"

colour("YlOrBr")(7) #代入を挟まずこれ1行で出力することもできる
#[1] "#FFFFE5" "#FEF0AD" "#FECE65" "#FB9A29" "#E1640E" "#AA3C03" "#662506"

colour("YlOrBr")(20) #上記と同じカラーパレット(YlOrBr)で20色出力
# [1] "#FFFFE5" "#FFFBD3" "#FFF8C2" "#FEF1B0" "#FEE99E" "#FEDF8A" "#FED26E" "#FEC552" "#FCB441" "#FBA231" "#F79124" "#F17F1B" "#EA6E13"
#[14] "#DC5E0B" "#CF4F03" "#BB4402" "#A63A03" "#903104" "#7B2B05" "#662506"

色を確認するにはplot_schemeが使える。

plot_scheme(colour("bright")(7))

f:id:yokazaki:20200325221933j:plain

plot_scheme(colour("YlOrBr")(7))

f:id:yokazaki:20200325222153j:plain

plot_scheme(colour("YlOrBr")(20))

f:id:yokazaki:20200325222225j:plain
その他の例はcolourのヘルプに色々載っている。

circlizeのcolorRamp2を使って配色を数値と対応させる

配色セットが欲しいだけなら上記のcolourで事足りる。このcolorRamp2は二極値や連続値をヒートマップで表現するにあたり「数値と色を対応させる」のに便利な関数だ。特に素晴らしいのが、

  • 整数値でない値も含め、指定した色の中間的な色を連続的に割り当ててくれる
  • どの値にどの色を割り振るか任意に決めることができ、スケーリングが自由自在

という点だ。
例えば、0→3→6と、白→黄→赤と変化する配色を使いたければ

require("circlize")
col.pal1<-colorRamp2(c(0,3,6),c("white","yellow","red"))

とすれば、col.pal1に0から6の連続値を渡したときに対応する色を返してくれる関数が格納される。

col.pal1(c(0,0.5,2.3,4.8))
#[1] "#FFFFFFFF" "#FFFFDFFF" "#FFFF63FF" "#FF8D00FF"

さらに、最大値を黒で表現したければ

col.pal2<-colorRamp2(c(0,2,4,6),c("white","yellow","red","black"))

とでき、黒と白を極端な値のみに割り振りたければ

col.pal3<-colorRamp2(c(0,0.5,5.5,6),c("white","yellow","red","black"))

としてスケールを変えることもできる。
これでも十分なのだけど、これをさらにcolourと組み合わせて、

col.pal4<-colorRamp2(seq(0,6,length.out=23),colour("iridescent")(23))

さらにきめ細かくして、3以上の数字に全て黒を割り当てる(3以下の変化に着目する)のに

col.pal5<-colorRamp2(c(0,seq(0.5,3,length.out=50),3,6),c("white",colour("iridescent")(50),"black","black"))

と書くこともできる。
以上の5つをを比較するとこんな感じ。

win.graph(10,5)
plot(NA,xlim=c(0,6),ylim=c(2.7,0),ann=F,axes=F)
axis(1)
axis(2,at=seq(0.5,2.5,0.5),labels=c("col.pal1","col.pal2","col.pal3","col.pal4","col.pal5"),las=1,lwd=0)
points(seq(0,6,length.out=100),rep(0.5,100),pch=21,cex=5,bg=col.pal1(seq(0,6,length.out=100)))
points(seq(0,6,length.out=100),rep(1,100),pch=21,cex=5,bg=col.pal2(seq(0,6,length.out=100)))
points(seq(0,6,length.out=100),rep(1.5,100),pch=21,cex=5,bg=col.pal3(seq(0,6,length.out=100)))
points(seq(0,6,length.out=100),rep(2,100),pch=21,cex=5,bg=col.pal4(seq(0,6,length.out=100)))
points(seq(0,6,length.out=100),rep(2.5,100),pch=21,cex=5,bg=col.pal5(seq(0,6,length.out=100)))

f:id:yokazaki:20200325235312j:plain
間違いの無い配色に素早くありつけるようになり、情報量満載カラフルfig作りが捗っている。