統計グラフの色

[TODO] R 4.0.0 以降の色についてはそのうち書きます。とりあえず palette() のヘルプをご覧ください。palette("Okabe-Ito") とするとsafe colorsになります。→ A New palette() for RColor Universal DesignEssentials of color in R。あと Colorspace 2.0 とその JSS paper

はじめに

統計グラフに色を付けることは広く行われています。しかし,色は万人に共通のものではありません。日本人男性の5%,白人男性の8%は,RGB(赤緑青)のうち赤と緑の区別がうまくできません。その内訳は1:3で赤の感受性がないP型(1型,protanopia)と緑の感受性がないD型(2型,deuteranopia)に分かれます。青を感じない人や,RGBのうち2色以上を感じない人もいます。少数ですが女性にもあります。RGBをすべて区別できることを前提とした統計グラフは避けなければなりません。形やパターンを併用するなどの工夫が必要です。その上で,できるだけ多くの人に見分けやすく快い色遣いをしましょう。

Rでの色指定

Rの多くの描画関数では col="red" のようなオプションで色が指定できます。"red" のように名前が付いたものは数百色あり,Rのコンソールに colors() と打ち込めば列挙されます。より便利な指定法はHTMLと同じ16進6桁または8桁の "#rrggbb" または "#rrggbbaa" という書き方です(aa は不透明度を表すアルファ値)。あるいは0〜1の実数値を使って rgb(r,g,b) あるいは rgb(r,g,b,a) と書くこともできます。例えば "red""#FF0000""#FF0000FF"rgb(1,0,0)rgb(1,0,0,1) と同じです。

rgb() 以外に hsv()hcl() でも指定できます。色の変換には col2rgb()convertColor()adjustcolor() といった関数が用意されています。詳しくはヘルプをご覧ください(コンソールに例えば ?hcl と打ち込みます)。例えば adjustcolor("blue", 0.5) で半透明の青ができます。

モノクロ印刷物の場合は gray(0)(黒)から gray(1)(白)までの実数値による指定が便利でしょう。カラー印刷物に使われるCMYKについては下で説明します。

パレットの選び方

色を名前やRGB値で指定するより,色の集合すなわちパレットを用意しておき,そこから選んで使うほうが便利です。

色見本

Rでデフォルトのパレット palette()black, red, green3, blue, cyan, magenta, yellow, gray の8色です:

# png("rgbcolors.png", 400, 200)
# par(mar=c(0,0,0,0))
barplot(rep(1,8), col=palette(), axes=FALSE)
# dev.off()

上のような原色は,目が痛くなるので,なるべく使わないようにしましょう。

rainbow() を使えば任意の個数の色が作れますが,やはり原色に近く,明暗の差があるのでカテゴリーデータに向かないという説もあります。colorspace パッケージの rainbow_hcl() を使えば,できるだけ同じ明度・彩度の色になります。

色見本
library(colorspace)
# png("colors.png", 400, 200)
# par(mar=c(0,0,0,0))
plot(NULL, xlim=c(0,7), ylim=c(0,2),
     axes=FALSE, xlab="", ylab="")
rect(0:6, 1.1, 1:7, 2, col=rainbow(7))
rect(0:6, 0, 1:7, 0.9, col=rainbow_hcl(7, c=100))
# dev.off()

この rainbow_hcl() はHCL色空間で一定のC(chroma,彩度),L(luminance,明度)を保ちながらH(hue,色相)だけを変えて色を作ります。デフォルトでは彩度 c = 50,明度 l = 70 ですが,上の例では彩度を少し上げています。

印刷にはRGBベースではなくCMYKベースの色を使います。有名な W. S. Cleveland の The Elements of Graphing Data という本ではCMYKをベースに次の5色をカテゴリーデータ用に薦めています。

(c,m,y,k)#rrggbb (R)#rrggbb (Photoshop)色名
(1,0,0,0)#00ffff#00a0e9cyan
(0,1,0,0)#ff00ff#e4007fmagenta
(1,0,1,0)#00ff00#009944green
(0,0.5,1,0)#ff8000#f39800orange
(1,0.5,0,0)#0080ff#0068b7royal blue

最後の色をClevelandはlight blueと書いていますが,ここではより適切と思われるroyal blueを使いました。

上の表でPhotoshopのRGB値はPhotoshop CS5でJapan Color 2001 CoatedからsRGBに変換したものです。一方,RではRGB→CMYKを便宜上次のような簡単なアルゴリズムで変換しています:

c = 1.0 - r
m = 1.0 - g
y = 1.0 - b
k = min(c, m, y)
if (k == 1.0) {
  c = m = y = 0.0
} else {
  c = (c-k)/(1-k)
  m = (m-k)/(1-k)
  y = (y-k)/(1-k)
}

CMYKのPDFを出力するには,

pdf("hoge.pdf", width=7, height=4, colormodel="cmyk")  # グレースケールなら "gray"
...
dev.off()

のようにします。"cyan""magenta""yellow""black" のようにしてCMYKを指定します。あるいは,上のアルゴリズムを逆に計算したRGB値を使って色を指定します。例えばオレンジ (c,m,y,k) = (0,0.5,1,0) を使いたいなら rgb(1,0.5,0),紺 (c,m,y,k) = (1,0.5,0,0) を使いたいなら rgb(0,0.5,1) を指定します。

色見本

ちなみにClevelandは連続量をcyanとmagentaの濃淡で表す右図のような方法も提案しています。

色見本

私がよく使うのは紺(#0068b7)からオレンジ(#f39800)にかけての色です。オレンジはRGBのうち赤と緑を含み,紺は青を多く含むので,赤または緑の感受性を持たない人にも見分けやすいというわけです。

display.brewer.all() の出力

より現代的な色の選択を提案するサイトとして,Colorbrewer: Color Advice for Maps があります。RのパッケージRColorBrewerも開発されています。右図はRColorBrewerパッケージのサンプル

display.brewer.all()

の出力です。3段に分かれています。上のグループは順次的(sequential)と呼ばれるもので,小さい量から大きい量までの段階を表すのに使われます。真ん中のグループは定性的(qualitative)と呼ばれるもので,大小関係のないカテゴリー(名義尺度)を表すのに使われます。下のグループは発散的(diverging)と呼ばれるもので,正負の両側に延びる量を表すのに使われます。

例えばこの中のDark2というものに興味があれば,

display.brewer.pal(8, "Dark2")

でこの8色だけサンプル出力します。実際の色を取り出すには,例えば

cols = brewer.pal(8, "Dark2")

とすれば cols[1] から cols[8] までに色が入ります。

自前でこのようなパレットを作るには colorRampPalette() 関数を使います。

# png("110905d.png", 450, 80)
# par(mar=c(0,0,0,0))
cols = colorRampPalette(c("#0068b7","white","#f39800"))
plot(NULL, xlim=c(0,10), ylim=c(0,1),
     axes=FALSE, xlab="", ylab="")
rect(0:9, 0, 1:10, 1, col=cols(10))
# dev.off()
colorRampPalette() の例

連続色

0から1までの実数に色を対応させる関数を作るには,例えば次のようにします。

cols = colorRamp(c("#0080ff","white","#ff8000"))

これで cols(0)#0080ffcols(1)#ff8000 に対応するような0〜255の三つ組を返す関数 cols() が生成されます。これを使って何か描いてみましょう。

# png("110905b.png", 450, 80)
# par(mar=c(0,0,0,0))
plot(NULL, xlim=c(0,100), ylim=c(0,1), axes=FALSE, xlab="", ylab="")
rect(0:99, 0, 1:100, 1, col=rgb(cols(0:99/99)/255), border=NA)
# dev.off()
連続色の例1

colorRamp() にいろいろな色を与えてやってみてください。例:

cols = colorRamp(c("#004080","#0080ff","white","#ff8000","#804000"))
連続色の例2

(追記)corrplot パッケージのデフォルト色がオレンジ(負)〜紺(正)であることに気づきました:

colorRampPalette(c("#67001F", "#B2182B", "#D6604D", "#F4A582", "#FDDBC7",
                   "#FFFFFF", "#D1E5F0", "#92C5DE", "#4393C3", "#2166AC", "#053061"))
corrplotのデフォルト色

scales パッケージ

scales パッケージにも色を作る関数がいろいろあります。help(package="scales") で出てくる 何々_pal という関数がそれです。例:

cols = dichromat_pal("BluetoOrange.10")(10)
show_col(gradient_n_pal(cols)(seq(0, 1, length.out = 30)))
scalesパッケージの例

カラーユニバーサルデザインについて

色覚バリアフリーまたはカラーユニバーサルデザインの考え方について少し補足しておきます。

まず必要なことは,自分以外の色覚の人がどのように見えるかを把握することです。CS4以降のAdobe PhotoshopやAdobe IllustratorにはP型・D型の見え方に変換する機能があります。Adobe Photoshop CS5の場合,「表示」→「校正設定」→「P型(1型)色覚」または「D型(2型)色覚」を選びます(CS4では「表示」が「ビュー」になります)。

Adobe Photoshop CS5の色覚校正

同様なことをするMac用の無料ソフト Sim Daltonism を教えていただきました。

GIMP(Mac,Windows,Linux)でも,[表示]→[ディスプレイフィルター]で「色覚障害の視覚」フィルターをアクティブにして選択すれば,1型〜3型の色覚がシミュレートできます。

例えば福島県放射能測定マップの色分けは次のようになっています:

福島県放射能測定マップの色分け

これをPhotoshop CS5で「D型(2型)色覚」にしてみます:

福島県放射能測定マップの色分け・D型色覚

緑とオレンジが同じような色になってしまうことがわかります。そうでなくてもこの色分けは明るい色(黄)を中心に両側に暗い色があるので,これでマップを作ると明るい色の輪に見えてしまうことが,下に挙げた“Escaping RGBland”論文でも指摘されています。正の値が大きいか小さいかを表すにはsequentialな色分けが適当ですが,その前に一つ予備知識。

Rでカラーユニバーサルデザインの考え方に基づいて作られたパッケージに dichromat があります。

dichromatパッケージの例
library(dichromat)
# png("dichromat.png", 400, 200)
# par(mar=c(0,0,0,0))
plot(NULL, xlim=c(0,12), ylim=c(0,2),
     axes=FALSE, xlab="", ylab="")
rect(0:11, 1.1, 1:12, 2, col=colorschemes$Categorical.12)
rect(0:11, 0, 1:12, 0.9, col=dichromat(colorschemes$Categorical.12))
# dev.off()

colorschemes$Categorical.12 は12色のカテゴリカル用の色です。このパッケージに含まれる色に限らず,どんな色でも dichromat() 関数を通せば色覚を変化させることができます(オプション type="deutan"type="protan" が指定できます。デフォルトは前者)。

そこで,先ほどの福島県放射能測定マップの色分けですが,色そのものはRColorBrewerの適当なsequentialな色分けを使うのでよいと思います。dichromat() 関数でD型色覚の見え方も調べてみましょう。

RColorBrewerのYlOrBrの8色とD型色覚
# library(RColorBrewer)
# library(dichromat)
# png("110922c.png", 400, 200)
# par(mar=c(0,0,0,0))
cols = brewer.pal(8, "YlOrBr")
plot(NULL, xlim=c(0,8), ylim=c(0,2),
     axes=FALSE, xlab="", ylab="")
rect(0:7, 1.1, 1:8, 2, col=cols)
rect(0:7, 0, 1:8, 0.9, col=dichromat(cols))
# dev.off()

もっとも,これはグレースケールだけで判別できるので,調べるまでもなかったのですが。

RGBをグレースケールに変換する一番簡単な式は 0.3r + 0.59g + 0.11b です。これとdichromatパッケージを使ってD型・P型・グレイスケールへの変換結果を表示するには次のようにすればいいでしょう。

library(dichromat)

rgb2gray = function(col) {
  m = matrix(c(rep(0.3,3),rep(0.59,3),rep(0.11,3)), nrow=3)
  rgb(t((m/255) %*% col2rgb(col)))
}

rects = function(col) {
  n = length(col)
  oldpar = par(mar=c(0,0,0,0))
  plot(NULL, xlim=c(0,n), ylim=c(0,4.6), axes=FALSE, xlab="", ylab="")
  rect(0:(n-1), 3.6, 1:n, 4.6, col=col)
  rect(0:(n-1), 2.4, 1:n, 3.4, col=dichromat(col,type="deutan"))
  rect(0:(n-1), 1.2, 1:n, 2.2, col=dichromat(col,type="protan"))
  rect(0:(n-1), 0.0, 1:n, 1.0, col=rgb2gray(col))
  par(oldpar)
}

最後に,カラーユニバーサルデザイン推奨配色セット ver. 4 にあるアクセントカラー・ベースカラー・無彩色の #rrggbb 値を載せておきます(例えば as.hexmode(c(255,75,0)) などとして16進変換できます)。

      #ff4b00
      黄色#fff100
      #03af7a
      #005aff
      空色#4dc4ff
      ピンク#ff8082
      オレンジ#f6aa00
      #990099
      #804000
      明るいピンク#ffcabf
      クリーム#ffff80
      明るい黄緑#d8f255
      明るい空色#bfe4ff
      ベージュ#ffca80
      明るい緑#77d9a8
      明るい紫#c9ace6
      #ffffff
      明るいグレー#c8c8cb
      グレー#84919e
      #000000

Rの pdf(colormodel="cmyk") でPDFにする際の指定は次の通りです:

      rgb(1,0.25,0.1)
      黄色rgb(1,1,0)
      rgb(0.25,1,0.35)
      rgb(0,0.55,1)
      空色rgb(0.45,1,1)
      ピンクrgb(1,0.45,0.65)
      オレンジrgb(1,0.55,0)
      rgb(0.7,0.05,1)
      rgb(0.45,0.1,0) (注)
      明るいピンクrgb(1,0.75,0.85)
      クリームrgb(1,1,0.6)
      明るい黄緑rgb(0.75,1,0.2)
      明るい空色rgb(0.7,1,1)
      ベージュrgb(1,0.75,0.55)
      明るい緑rgb(0.55,1,0.55)
      明るい紫rgb(0.75,0.7,1)

(注) これでCMYK値は (0%,78%,100%,55%) になり,推奨値 (55%,90%,100%,0%) と違ってしまいますが,仕様上しかたないようです。

色の変換

Webで見ている分にはどうでもいいことでも,印刷が関係すると,いろいろ厄介な問題が生じます。このあたりに詳しくない人は,ぜひ「4色」って言っただけで印刷会社の社員がうなだれた……でも、なんで? 『今日も下版はできません!』第21話という漫画をお読みください。

印刷用途にスクリーンショットなどを使う際には,もちろんJPEGでなくPNGを使うことになりますが,RGBのPNGをグレイスケールに変換する必要が生じます。この話は[改訂第7版]LaTeX2e 美文書作成入門のpp.138--139にも書きましたが,ImageMagickのconvert(最近のバージョンではmagick)コマンドを使って

for x in *.png
do
  convert $x -colorspace Gray ${x%.png}-gray.png
done

のようにして一括変換できます。

Rで生成したPDFをインクルードして印刷に回す際にも,pdf() 関数なら,上に書いたように colormodel="cmyk" または colormodel="gray" のオプションが使えますが,Macで任意のシステムフォントを埋め込むために quartz(type="pdf") を使う場合には,どうしてもRGBになってしまいますので,後でグレイスケールに変換する必要が生じます。これは,Ghostscriptを使って

gs \
   -sDEVICE=pdfwrite \
   -sProcessColorModel=DeviceGray \
   -sColorConversionStrategy=Gray \
   -sColorConversionStrategyForImages=Gray \
   -dAutoRotatePages=/None \
   -o hoge-gray.pdf \
   hoge.pdf

のようにできます。Gray の部分は必要に応じて CMYK にします。Ghostscript 8 では -dUseCIEColor のようなオプションが必要かもしれません。逆に Ghostscript 9 では -dOverrideICC が必要かもしれません。うまく変換されたか Acrobat の印刷工程の出力プレビューでチェックしましょう。R で col=gray(0.9) と指定したら K 10% になるのが正しいのですが,RGB経由の変換では例えば K 13% のようになるかもしれません。

参考文献・サイト