gnuplotで円グラフを書く

たまに「gnuplotでは円グラフを作れない」という解説を見ることがありますが、実は最近のgnuplotではwith circlesオプションを用いることで円グラフを作成できます。

円グラフの基本

まず、plotコマンドのwith circlesオプションの引数について紹介しましょう。このオプションを用いる場合、usingの1つ目と2つ目の引数が円の中心のx座標とy座標、3つ目の引数は円の半径、4つ目が扇形を書く場合の始めの角度、5つ目が扇形の終わりの角度です。これらの角度は、set anglesの指定に関わらず、角度単位で与える必要があります(つまり、ラジアン単位での指定は不可)。なお、扇形ではなく円を書く場合は、3つ目以降の引数は省略することもできます。

以上のことを踏まえると、with circlesオプションを用いて円グラフを書く場合、基本的には以下のようなデータファイルを用意する必要があります(もっと簡単なデータファイルから円グラフを書く方法は後ほど説明します

ビール	0	120
日本酒	120	200
焼酎	200	260
ワイン	260	320
その他	320	360

データの2列目は円グラフの扇形を書く始めの角度、3列目は扇形の終わりの角度です。これらの角度は、set anglesの指定に関わらず、角度単位で与える必要があります。

このデータファイルの名前をcircle-data0.datとすると、以下のようなスクリプトで最も単純な円グラフを書くことができます。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
plot "circle-data0.dat" using (0):(0):(1):2:3 with circles

この例では、usingの1つ目と2つ目の引数には共に(0)を与えています。これは扇形を原点中心に描画すると言う指定です。ここで、0がカッコで囲われていることに注意して下さい。カッコで囲わない場合、0はデータの番号を意味することになってしまいます。カッコで囲うことによって、ゼロという値を直接引数に与えることができます。

3番目の引数は半径の指定ですので、上と同じように1という値を引数に与えるために、かっこで囲んだ(1)という指定の仕方をしています。

最後に、4番目と5番目の引数に扇形の開始角度と終了角度を指定することで、円グラフが描けます。なお、1行目のset size ratio -1はグラフの縦横比(アスペクト比)をx軸とy軸の範囲の比と一致させるための命令です。今の場合はx軸、y軸とも範囲が[-1:1]なので、縦横比が1:1になります。こうすると、円がゆがんだり欠けたりせず、きちんと円グラフを書くことができます。

円グラフの改善

軸を消す

次にこのグラフを改善していきます。円グラフの場合は縦横の軸は不要ですので、unset borderunset xtics等を用いて消してしまいます。また、凡例もunset keyで消してしまいます。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
plot "circle-data0.dat" using (0):(0):(1):2:3 with circles

データラベルを追加する

また、この例ではどの扇形がどのデータに対応しているか全くわからないので、同じデータファイルをwith labelsオプションを用いて再度プロットすることでデータのラベルを着けます。以下の例では、データファイル1列目のラベルを、半径0.7の円弧上に配置するようにしています。ラベルを配置する角度は、円グラフの扇形のちょうど中央(つまり開始角度と終了角度の平均)にしています。cossinの引数が角度単位になるように、set angles degreesを追加することも必要です。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
plot "circle-data0.dat" using (0):(0):(1):2:3 with circles,\
     "" using (0.7*cos(($2+$3)/2.0)):(0.7*sin(($2+$3)/2.0)):1 with labels

色を塗る

円グラフに色を塗る場合はset style fillオプションを用います。例えば、以下の例では、グラフに半透明(transparent solid)で色を塗る(透過度0.4)ようにし、さらに枠線を黒にしています。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill transparent solid 0.4 border lc rgb "black"
plot "circle-data0.dat" using (0):(0):(1):2:3 with circles,\
     "" using (0.7*cos(($2+$3)/2.0)):(0.7*sin(($2+$3)/2.0)):1 with labels

扇形の色を塗り分けたい場合は、以下のように、usingの第6引数に($0+1)を追加し、with circlesの後にlc varを追加します。これは、データ番号($0)に1を足した色番号を使うと言う指定です。なぜ1を足すかというと、1行目のデータの色番号が0(=黒)になってしまい、見栄えが良くないためです。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill transparent solid 0.4 border lc rgb "black"
plot "circle-data0.dat" using (0):(0):(1):2:3:($0+1) with circles lc var,\
     "" using (0.7*cos(($2+$3)/2.0)):(0.7*sin(($2+$3)/2.0)):1 with labels

もし、それぞれの項目の色を自分で指定したい場合は、set style lineを用いて、色番号の設定を変えてしまう方法があります。下の例では、色番号1に黄色("yellow")等を指定しています。実際に指定した色でグラフの色が塗り分けられていることが分かると思います。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill transparent solid 0.4 border lc rgb "black"
set style lines 1 lc rgb "yellow"
set style lines 2 lc rgb "cyan"
set style lines 3 lc rgb "brown"
set style lines 4 lc rgb "magenta"
set style lines 5 lc rgb "gray"
plot "circle-data0.dat" using (0):(0):(1):2:3:($0+1)  with circles lc var,\
     "" using (0.7*cos(($2+$3)/2.0)):(0.7*sin(($2+$3)/2.0)):1 with labels

12時の位置から時計回りのグラフにする

これまでのグラフはx軸(3時の位置)から反時計回りにデータが積まれて行くようなグラフでした。一方、よくつかわれる円グラフでは、12時の位置から時計回りに回って行くような場合が多いと思います。そのようなグラフを作るためには、まず3時の位置から反時計回りに測る角度を12時の位置から時計回りに測る角度に変換するための関数を間に挟んでやる必要があります。下の例ではangle_conv(x)関数がそれに当たります。この関数をusingでデータの2列目と3列目を読み込んでいる部分に挟んでやっています。また、角度のまわり方が逆になるため、開始角度と終了角度を逆にする必要があります。つまり、usingの4つ目と5つ目の引数が、以前は2:3だったのが、今回は(angle_conv($3)):(angle_conv($2))と、23が逆になっているのです。

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill transparent solid 0.4 border lc rgb "black"
set style lines 1 lc rgb "yellow"
set style lines 2 lc rgb "cyan"
set style lines 3 lc rgb "brown"
set style lines 4 lc rgb "magenta"
set style lines 5 lc rgb "gray"
angle_conv(x) = -x + 90.0
plot "circle-data0.dat" using (0):(0):(1):(angle_conv($3)):(angle_conv($2)):($0+1) \
     with circles lc var,\
     "" using (0.7*cos((angle_conv($2)+angle_conv($3))/2.0)):(0.7*sin((angle_conv($2)+angle_conv($3))/2.0)):1 \
     with labels

このスクリプトを用いると、下のように、12時位置から時計回りにデータが積み上げられていく円グラフを作成できます。

より実際的なデータファイルからの円グラフ作成

さて、実際のところ、何かの調査などの結果が上に挙げたような開始角度と終了角度のデータの形で与えられる場合はほとんど無く、下の例のように結果が実際の数値で与えられている場合がほとんどだと思います。ここから、開始角度と終了角度を含むデータファイルの形に変換するのは意外と面倒です。

ビール	60
日本酒	40
焼酎	30
ワイン	30
その他	20

では、上記のようなデータファイル(以下ではcircle-data1.datとします)から直接に円グラフを作る方法を考えてみましょう。

そのためには、累次代入演算子(,)と統計概要を得るコマンドstatsを用います。

statsコマンドはplotコマンドと似たような書き方で使いますが、実行することによってグラフをプロットするのではなく、データの統計情報を得ることができるというものです。例えば、以下の例ではデータファイルの2列目の統計情報を計算し、それらをSTATS_**という変数に格納します。例えば、データ全体の合計値はSTATS_sumという変数に入れられます。従って、statsコマンドを実行してからSTATS_sumを参照すれば、データの2列目の合計を計算等に使うことができます。下の例では、扇形の角度を算出するためにSTATS_sumを用いています。

また、累次代入演算子を用いて、angleという変数に、現在描いている扇形の終了角度を保存しておき、それを次の扇形の開始角度として用いています。このようにして、角度情報を持たないデータファイルから円グラフを作成しているのです。

file = "circle-data1.dat"
stats file using 2

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics

angle = 0.0
plot "circle-data1.dat" using (0):(0):(1):(angle):(angle = angle + 360.0*$2/STATS_sum, angle) \
     with circles

より実際的なデータファイルからの円グラフの改善

このような、実際的なデータファイルから円グラフを作るスクリプトも、一つ前の例と同じように改善していくことができます。

データラベルを付ける

先ほどと同じように、with labelsを併用することで、以下のように円グラフにデータラベルを付けることができます。下の例では、もう一つangle_oldという変数を作成し、そこに現在の扇形の開始角度を格納して、ラベルを置く位置を決める際に参照しています。あと、sinなどの引数を角度単位にするように、set angles degreesを追加しているのにも注意して下さい。

file = "circle-data1.dat"
stats file using 2

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees

angle = 0.0
angle_old = 0.0
plot angle = 0.0,\
     "circle-data1.dat" using (0):(0):(1):(angle):(angle = angle + 360.0*$2/STATS_sum, angle) \
     with circles,\
     angle = 0.0,\
     "" using (angle_old = angle, angle = angle + 360.0*$2/STATS_sum, 0.7*cos( (angle+angle_old)*0.5) ):(0.7*sin( (angle+angle_old)*0.5) ):1 \
     with labels

円グラフの色付け

円グラフの色付けは、先の例と全く同じ方法でできます。以下の例では、塗りつぶしの色として、0.4の透明度の色(set style fill transparent solid 0.4)ではなく、普通の不透明の色(set style fill solid)を使っています。

file = "circle-data1.dat"
stats file using 2

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill solid border lc rgb "black"
set style lines 1 lc rgb "yellow"
set style lines 2 lc rgb "cyan"
set style lines 3 lc rgb "brown"
set style lines 4 lc rgb "magenta"
set style lines 5 lc rgb "gray"

angle = 0.0
angle_old = 0.0
plot angle = 0.0,\
     "circle-data1.dat" using (0):(0):(1):(angle):(angle = angle + 360.0*$2/STATS_sum, angle):($0+1) \
     with circles lc var,\
     angle = 0.0,\
     "" using (angle_old = angle, angle = angle + 360.0*$2/STATS_sum, 0.7*cos( (angle+angle_old)*0.5) ):(0.7*sin( (angle+angle_old)*0.5) ):1 \
     with labels

12時位置から時計回りに回って行くグラフにする

先の例と同じように、angle_conv(x)関数を挟んだり、開始角度と終了角度を入れ替えたりすることで、12時位置から時計回りにデータが積み上げられていくグラフにも出来ます。

file = "circle-data1.dat"
stats file using 2

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill solid border lc rgb "black"
set style lines 1 lc rgb "yellow"
set style lines 2 lc rgb "cyan"
set style lines 3 lc rgb "brown"
set style lines 4 lc rgb "magenta"
set style lines 5 lc rgb "gray"

angle = 0.0
angle_old = 0.0
angle_conv(x) = -x + 90.0
plot angle = 0.0,\
     "circle-data1.dat" using (0):(0):(1):(angle_old = angle, angle = angle + 360.0*$2/STATS_sum, angle_conv(angle)):\
     (angle_conv(angle_old)):($0+1) with circles lc var,\
     angle = 0.0,\
     "" using (angle_old = angle, angle = angle + 360.0*$2/STATS_sum, 0.7*cos( (angle_conv(angle)+angle_conv(angle_old))*0.5) ):\
     (0.7*sin( (angle_conv(angle)+angle_conv(angle_old))*0.5) ):1 \
     with labels

さらに

さらにgnuplotの機能を使っていけば、より高度なグラフもできます。例えば、with labelsの部分でstringcolumn()sprintf()を併用することで、データの詳細をグラフ上に出力することもできます。

file = "circle-data1.dat"
stats file using 2

set size ratio -1
set xrange [-1:1]
set yrange [-1:1]
unset key
unset border
unset xtics
unset ytics
set angles degrees
set style fill solid border lc rgb "black"
set style lines 1 lc rgb "yellow"
set style lines 2 lc rgb "cyan"
set style lines 3 lc rgb "brown"
set style lines 4 lc rgb "magenta"
set style lines 5 lc rgb "gray"

angle = 0.0
angle_old = 0.0
angle_conv(x) = -x + 90.0
plot angle = 0.0,\
     "circle-data1.dat" using (0):(0):(1):(angle_old = angle, angle = angle + 360.0*$2/STATS_sum, angle_conv(angle)):\
     (angle_conv(angle_old)):($0+1) with circles lc var,\
     angle = 0.0,\
     "" using (angle_old = angle, angle = angle + 360.0*$2/STATS_sum, 0.7*cos( (angle_conv(angle)+angle_conv(angle_old))*0.5) ):\
     (0.7*sin( (angle_conv(angle)+angle_conv(angle_old))*0.5) ):(stringcolumn(1) . "\n" . sprintf("%d人(%.1f%%)", $2, 100.*$2/STATS_sum)) \
     with labels