gnuplot で 円グラフを描くには

はじめに

gnuplot 5.0 からplotting styleにcircles が追加されて円グラフ(pie chart)を描けるようになりました。

円グラフは描けるは描けますが、ノウハウというかトリックが結構必要です。

基本

サンプルの入力データ

使うデータはこのブログをアクセスしたユーザー環境のOS内訳です:

Windows  59.21
Linux    11.84
MacOS    11.84
iOS       9.87
Android   7.24

一番簡単な例は次のような例です:

set terminal pdfcairo color enhanced font "Helvetica,18"
set output "pie-chart-simple.pdf"

unset xlabel 
unset ylabel
unset tics
unset border
unset key
set size ratio -1
set xrange [-1.5:1.5]
set yrange [-1.5:1.5]
set style fill transparent solid 0.9 noborder
plot '-' using 1:2:3:4:5:6 with circles lc var
0 0 1   0 213.156 1
0 0 1 213.2 255.8 2
0 0 1 255.8 298.4 3
0 0 1 298.4 334.0 4
0 0 1 334.0 360.0 5
e
unset output

using の書式は 円弧の中心x座標 円弧の中心y座標 円弧の半径 円弧の開始角度 円弧の終了角度 色 です。角度は度(degree)です。

安直にやるには、データの換算が必要

上の例では(インラインデータです)、usingで使う値を直接データにしています。 元のOS内訳から換算しています。 これが一番安直ですが、生データを直接使えないのでちょっと面倒です。

見慣れた円グラフとは始点、回転方向が違う

円弧の開始角度0度は3時になり、回転方向が反時計回りになってしまいます。 よく見慣れた円グラフの始点は12時で、回転方向は時計回りです。

円グラフのテンプレート

上記をいじって改善したのが次の例です:

set terminal pngcairo color enhanced font "Helvetica,18"
set output "pie-chart-label-inside.png"
set size ratio -1
unset key

data = "os.dat"

stats data using 2 noout
print STATS_sum
ang(x) =  x * 360.0 / STATS_sum
perc(x)=  x * 100.0 / STATS_sum  

set xrange [-1.5:1.5]
set yrange [-1.5:1.5]
unset tics
unset border
set angles degrees
set style fill transparent solid 0.65 noborder

Ai = 0.0 # init angle for pie-chart
Bi = 0.0 # init angle for labels
mid = 0.0 # mid angle for labels

a(x) = x - floor(x)
print a(0.9)
plot data using (0):(0):(1):(A0 = Ai, Ai = Ai + ang($2), 90 - Ai ):(90 - A0):\
     (hsv2rgb((a($0 / 9 + 0.7)), 0.95, 0.9)) with circles lc rgb var,\
     data using (mid = Bi  + ang($2) / 2,  Bi = 2.0 * mid - Bi, 1.4 * cos(90 - mid)):\
                (1.4 * sin(90 -  mid)):(stringcolumn(1) . sprintf('\n%.1f\%',  perc($2))) with labels

#     data using (mid = Bi  + ang($2) / 2,  Bi = 2.0 * mid - Bi, 0.7 * cos(90 - mid)):\
#                (0.7 * sin(90 -  mid)):($1) with labels

unset output


改善点

見慣れた円グラフにするために改善した点について述べます。

始点・回転を修正

安直に円グラフを描くと円弧の開始角度0度は3時になり、回転方向が反時計回りになってしまいます。 これを12時(0時)から時計回りにするにはusingの円弧の角度を指定する部分で次のように工夫します:

using (円弧の中心x座標):(円弧の中心y座標):(円弧の半径):(90 - 円弧の終了角度):(90 - 円弧の開始角度):(色)

始点が90度オフセットして、回転方向が逆なので、90度から引き算しています。また始点・終点の角度を入れ替えます。

生データを扱えるように

元データをいちいち手動で換算するのは面倒で、ネットを調べるとみなさん似たようなことをやっています。

基本的には

  • statsコマンドでデータの総和を出しておく
  • 各円弧を描く度にデータを足して、開始角度と終了角度を計算する
ことを行います。

上記の例では角度を計算する関数ang(x)を定義してあります。

あと、円グラフではパーセンテージを使うことが多いですからパーセントを計算する関数perc(x)も定義しています。

色の指定

色の指定は linecolor variable を使うか rgbcolor variable を使うのが良さそうです。

linecolor rgbcolor variableを使う

データファイルのデータを使って色を指定します。gnuplotのマニュアルの例では

rgb(r,g,b) = 65536 * int(r) + 256 * int(g) + int(b)
splot "data" using 1:2:3:(rgb($1,$2,$3)) with points lc rgb variable

のように指定しています。円グラフでは上は使いにくいので、hsv2rgbを使って

plot data using (0):(0):(1):(A0 = Ai, Ai = Ai + ang($2), 90 - Ai ):
     (90 - A0):(hsv2rgb($0, 0.95, 0.9)) with circles lc rgb var

のようにしてみました。hsv2rgbの1番目の引数の$0はデータの番号(何番目のデータか)を表していいます。

$0はデータ番号なので、そのままでは上手く色指定が出来ない場合がありますし、好みの色にならないこともあります。 そこで最終的にはa(x) = x – floor(x)という出力が1以下になる周期関数を定義して、

hsv2rgb((a($0 / 9 + 0.7)), 0.95, 0.9)

としてみました。a(x)の引数はデータ番号を使って適当に書いています。0.7は開始の色を変えるために入れています。

linecolor variableを使う

line color variable を指定すると color indexを入力データファイルから読み取るようになります。 color index はstyle で指定したindexに対応します。

set style lines 1 lc rgb "orange"

しかしぼくの環境ではstyleを指定しても、plotでは反映されませんでした。デフォルトの色が使われてしまいます。何が違うのかな。

label 表示

label表示もトリッキーです。

     data using (mid = Bi  + ang($2) / 2,  Bi = 2.0 * mid - Bi, 1.4 * cos(90 - mid)):\
                (1.4 * sin(90 -  mid)):(stringcolumn(1) . sprintf('\n%.1f\%',  perc($2))) with labels

の部分です。

まずラベルの位置を計算しています。開始角度と終了角度の半分の角度を計算します。 それをcos, sinに入れて x,y座標を計算し、plotting style を label にして表示させています。

stringcolumn(x)はx番目のカラムにある文字列を返す関数です。ピリオド”.”はPerlと同様に文字列を連結する演算子です。

参考にしたページ