さいしょに
この記事はR言語 Advent Calendar 2024年の14日目の記事です。Advent Calendar に参加するのは2回目です。
モチベーション
ggplotのカラーパレット何が良いかな、と悩んでいました。というのも、ggplotのデフォルトのカラーパレットで2要素に色分けすると、ピンクと緑であまり良くないとされている組み合わせなのかな、と思ったからです。
それで色々調べたりしたのですが、結局結論まではたどり着いていません。ですが、とりあえず「こうやっておくのがベターかな」とはなったので、それをまとめます。
色覚異常について
以下のサイトによると、赤と緑の見分けがつきにくい人が男性で5%, 女性で0.2%いるとのことです。男性のうち5%というのは無視できない割合ですので、考慮した作図が求められます。
先天赤緑色覚異常の発生頻度は、日本人では男性の5%、女性の0.2%です。つまり、男性では20人に1人、女性では500人に1人の割合です。
色覚異常の方からの見え方について
下のサイトを主に参考にしています。程度問題の面もあるので、必ずこう見えるという訳ではないとのことですが、dichromat
を使って色覚異常の影響を近似することが出来ます。試しにggplotのデフォルトであるhue_pal()
で2要素の見え方をテストすると、色合いのイメージは変わりますが、まあ区別出来てるから大丈夫かあ、となります。もともと自分は赤と緑と思っていましたが、デフォルトでもちゃんと配慮されており、特に気を揉む必要はなかったと言えます。
ちなみに、Protanopiaは赤が見えにくいタイプ、Deutanopiaは緑が見えにくいタイプ、Trichromacyは3色見えるタイプ(一般的なタイプ)、Tritanopiaは青と黄色を区別しにくいタイプとのことです。
library(dichromat)
library(scales)
hue_colors <- hue_pal()(2)
# convert to the three dichromacy approximations
protan <- dichromat(hue_colors, type = "protan")
deutan <- dichromat(hue_colors, type = "deutan")
tritan <- dichromat(hue_colors, type = "tritan")
# plot for comparison
layout(matrix(1:4, nrow = 4)); par(mar = rep(1, 4))
recolorize::plotColorPalette(hue_colors, main = "Trichromacy")
recolorize::plotColorPalette(protan, main = "Protanopia")
recolorize::plotColorPalette(deutan, main = "Deutanopia")
recolorize::plotColorPalette(tritan, main = "Tritanopia")
複数の要素の場合
しかし、複数の要素に分けた場合、考慮が必要かなと思いました。宗さんと矢内さんのページによると、hue_pal()
は循環する仕組みとなっているためか、Tritanopiaの方から見たら対称性のある似た色合いになってしまいます。
hue_colors <- hue_pal()(7)
# convert to the three dichromacy approximations
protan <- dichromat(hue_colors, type = "protan")
deutan <- dichromat(hue_colors, type = "deutan")
tritan <- dichromat(hue_colors, type = "tritan")
# plot for comparison
layout(matrix(1:4, nrow = 4)); par(mar = rep(1, 4))
recolorize::plotColorPalette(hue_colors, main = "Trichromacy")
recolorize::plotColorPalette(protan, main = "Protanopia")
recolorize::plotColorPalette(deutan, main = "Deutanopia")
recolorize::plotColorPalette(tritan, main = "Tritanopia")
また、Hannah Wellerさんのサイトではviridis
が推奨されています。確かに、ここでシミュレーションした全てのタイプに対し、区別出来ているように見えます。
ただ個人的には、色合いが順番に変わっていくので、連続的な印象を与えかねないかな、とも思いました。まあ3つくらいであれば全然問題無いと思いますが、下のような7要素だと、1から7に徐々に変化していくもの、と見られなくもないからです。
viridis_pal <- scale_color_viridis_d()
viridis_color <- viridis_pal$palette(7)
# convert to the three dichromacy approximations
protan <- dichromat(viridis_color, type = "protan")
deutan <- dichromat(viridis_color, type = "deutan")
tritan <- dichromat(viridis_color, type = "tritan")
# plot for comparison
layout(matrix(1:4, nrow = 4)); par(mar = rep(1, 4))
recolorize::plotColorPalette(viridis_color, main = "Trichromacy")
recolorize::plotColorPalette(protan, main = "Protanopia")
recolorize::plotColorPalette(deutan, main = "Deutanopia")
recolorize::plotColorPalette(tritan, main = "Tritanopia")
3つくらいの要素であれば全然問題ないように思います。
library(palmerpenguins)
penguins %>% ggplot(aes(x = bill_depth_mm, y = bill_length_mm, colour = species))+
geom_point(size = 2) +
scale_color_viridis_d()
個人的な落としどころ
というのを経て、今のところ自分はggokabeito
パッケージを使うようにしています。これもTritanopiaの方からは判別が難しいように見えるのですが、以下の理由でとりあえずこれがベターかなと考えました。
-
viridis
だと要素毎に分けていても連続的な印象を与えそう(要素が3つくらいだと問題無いとは思う)- これはデフォルトの
hue_pal
も起こりえるかも
- これはデフォルトの
- 一方
okabe_ito
では連続的な印象は与えにくい - 個人的に黄色が
viridis
より見やすい
当然、これを使っておけばOK、という訳ではないと思います。結局は、要素数を減らしたりshape
と組み合わせるなど、そもそもの工夫をしたうえでカラーパレットを選択すると思いますので、単純な話ではないなあ、というのが色々調べて思った感想です。
おまけ
ggplotのデフォルトのカラーパレットが循環しているということを確認しました。
> hue_color <- hue_pal()(10000) %>% unique() #10000は適当なデカい数値
> length(hue_color) #色は1176通りあるっぽいことが分かる
[1] 1176
> head(hue_color)
[1] "#F8766D" "#F8766C" "#F8776C" "#F7776B" "#F7776A" "#F77769"
> tail(hue_color)
[1] "#F97571" "#F97570" "#F87570" "#F87670" "#F8766F" "#F8766E"
こう見ると、#F8766Dから始まり#F8766Eで終わっているので、確かに循環しているっぽいです。
なので、試しにどんな感じで循環しているか可視化してみたところ、当たり前といえば当たり前ですが境目なくきれいに繋がっている事が確認できました。
サンプルコード
library(ggeasy)
library(xkcd)
rad <- seq(0, 2 * pi, length.out = 1176)
circle <- tibble(x = cos(rad),
y = sin(rad),
n = 1:1176)
circle %>%
ggplot(aes(x = x, y = y, colour = factor(n))) + #ここのfactorを外せばこんな変なグラフにはならない
geom_point(size = 3) +
easy_remove_legend() +
easy_labs(title = "circulation") +
theme(text = element_text(size = 16, family = "xkcd"))
参考にしたもの