gnuplot 5.2.0以降では(本物の)配列が使えるようになりました(5.0系以前のバージョンで配列もどきを使いたい場合の説明はこちら)。配列を使うことで、様々な数値の処理が非常に簡単に行えるようになるだけでなく、ファイル名を配列に入れることで複数のファイルの処理も簡単に行えたりするようになります。また、数値の配列は、ファイルのように「プロット」してグラフ化することができます。
配列はarray
キーワードを用いて宣言します。例えば、Aというサイズが5の配列は以下のように宣言します。
array A[5]
なお、gnuplotの配列の番号は1から開始されます。上の例では、配列AはA[1]、A[2]、…A[5]というデータを持ちます。一方、例えばデータ行の番号(つまりusing
内で使う$0
やcolumn(0)
)など、gnuplotでは0から始まるものもあるので、注意が必要です。
さて、このままでは、配列はデータを持っていないので、値を代入してやる必要があります。数値の代入は、通常の変数への値の代入と同じように行えます。
A[1] = 1
A[2] = 2
A[3] = 3
A[4] = 4
A[5] = 5
これだと面倒なので、do for
等を用いたループを使うほうが便利な場合があります。
do for [i=1:5]{
A[i] = i
}
また、宣言と同時に値を代入することもできます。この場合は角かっこ[]
とカンマ,
を用います:
array A[5] = [1, 2, 3, 4, 5]
この方法で、代入の行が複数にわたる場合は\
を改行の前に入れる必要があります。
array A[5] = [\
1, 2, \
3, 4, \
5\
]
ちなみに、gnuplotの配列はデータの型に関する制限は無いので、必要であれば以下のように数値(実数、複素数、整数)と文字列を含むような配列を作ってもかまいません。(下記で、{2.0, 2.0}
は複素数2 + 2i
のことです。)
array B[4] = [\
1.0, {2.0, 2.0}, "third element", 4 \
]
宣言した配列のサイズを超えて代入しようとしたり、正でない整数の添え字を使おうとすると、エラーが出ます。以下の2つのスクリプトは、
array B[4]
B[5] = "5th element"
array C[4]
C[0] = 0.0
いずれも下のようなエラーが出て実行が止まってしまいます。
"C:/..../xxxx.plt", line x: array index out of range
一方以下の場合は、
array A[4] = [1, 2, 3, 4, 5]
下のようなエラーになります。
"C:/..../xxxx.plt", line x: unexpected or unrecognized token
このように定義した配列A
のi
番目の値は、多くのプログラミング言語と同様にA[i]
という形で参照することができます。例えば、以下のようなスクリプトでは、配列の内容をprint
コマンドでコンソールに出力します。
array A[4] = [1.0, {2.0, 2.0}, "third element", 4]
print A[1]
print A[2]
print A[3]
print A[4]
これを実行すると、
1.0
{2.0, 2.0}
third element
4
のような出力が得られます。
ここでさらに
print A[5]
として、宣言した範囲外の要素を呼び出そうとすると、
"C:/..../xxxx.plt", line 7: array index out of range
というエラーが出てしまいます。A[0]
やA[-1]
など、正でない整数の添え字を用いてしまった場合でも同じエラーが出ます。
一方、以下のように、宣言だけして、値を代入しなかった配列の要素を呼び出すと、
array B[4]
print B[1]
print B[2]
print B[3]
print B[4]
出力は以下のようになり、値の入っていない配列要素の呼び出しでは<undefined>
という値が返されることがわかります。
<undefined>
<undefined>
<undefined>
<undefined>
データファイルのデータを丸ごと配列に入れることができれば、データにいろいろな演算をすることができて便利なケースがあります。ここでは、まず、以下のような簡単なデータファイルarray_data_1.datを配列に読み込むことを考えます。
1 3
3 8
5 11
6 12
7 6
ファイルから数値を配列に代入するには、以下のようにstats
コマンドと、累次代入演算子(,)
を使います。
# データサイズの取得
stats "array_data_1.dat" using 1 nooutput
N = STATS_records # データの行数
# 配列の宣言
array A[N]
# データの配列への保存
stats "array_data_1.dat" using (A[$0+1] = $1, 0) nooutput
# 配列の内容の表示
do for [i=1:N]{
print A[i]
}
1行目のstats
コマンドでは、データの数を取得しています。そのデータの数Nだけのサイズの配列A[N]を宣言し、その次のstats
コマンドのところで、1コラム目のデータ$1を配列要素A[$0+1]に代入してから0という値を評価しています。配列の番号が$0+1となっているのは、先にも述べた、「gnuplotでは行番号は0から始まるのに、配列の番号は1から始まる」という微妙な(しかし結構厄介な)違いのためです。また、累次代入演算子(,)
の最後に評価する0は、他の値を使ってもかまいません。
このスクリプトを実行すると、以下のような出力が得られます。きちんとデータファイルの内容が配列に入れられていることがわかります。
1.0
3.0
5.0
6.0
7.0
累次代入演算子の中身を増やせば、1列目のデータを配列Xへ、2列目のデータを配列Yへ格納したりすることもできます:
# データサイズの取得
stats "array_data_1.dat" using 1 nooutput
N = STATS_records # データの行数
# 配列の宣言
array X[N]
array Y[N]
# データの配列への保存
stats "array_data_1.dat" using (X[$0+1] = $1, Y[$0+1] = $2, 0) nooutput
# 配列の内容の表示
do for [i=1:N]{
print X[i], Y[i]
}
このスクリプトを実行すれば、以下のようになり、1列目のデータが配列Xへ、2列目のデータが配列Yへ、それぞれ格納出来ていることがわかります。
1.0 3.0
3.0 8.0
5.0 11.0
6.0 12.0
7.0 6.0
これを繰り返せばどんなサイズのデータファイルも配列に格納できますが、データの列数が多くなってくると、累次代入演算子の部分を書くのが大変になってきます。例えば、以下のような10行×10列のarray_data_2.datのすべてのデータを配列に入れることを考えます。
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
81 82 83 84 85 86 87 88 89 90
91 92 93 94 95 96 97 98 99 100
これを読み込むために、例えばA1[10]、A2[10]、…A10[10]という10の配列を定義して、累次代入演算子をつかって代入していってもいいのですが、それだと面倒ですし、列の数が増減したときにはスクリプトを大きく書き換えなければならなくなってしまいます。一方、以下のようなスクリプトを使うと、データの列数によらず、i列j行目のデータをA[i+(j-1)*M]に格納することができます(Mはデータの列数)。
# データサイズの取得
stats "array_data_2.dat" nooutput
N = STATS_records # データの行数
M = STATS_columns # データの列数(コラム数)
# 配列の宣言
array A_2d[N*M]
# データの配列への保存
stats "array_data_2.dat" using (sum[i=1:M] (A_2d[i + $0*M] = column(i), 0)) nooutput
# 配列の内容の表示
do for [i=1:N*M]{
print A_2d[i]
}
このスクリプトでは、まずstats
コマンドを使って、データの列数と行数を取得して、それぞれMとNという変数に保存しています。その後、N*Mというサイズの配列A_2d
を宣言します。その後、stats
コマンドの中でループを使って配列にデータを保存したいのですが、gnuplotではusing
の中で使えるループが存在しません。そこで、using
の中でも使えるsum
演算子を、繰り返しループとして用います。sum演算子でi変数を1からMまでループさせ、i列目のデータ(column(i)
)をA_2d[i + $0*M]に代入しています($0は「データの行数−1」であることに注意)。
このスクリプトの実行結果は、以下のようになり、ちゃんと全部のデータが配列A_2dに格納されていることがわかります。
1.0
2.0
3.0
4.0
5.0
(中略)
96.0
97.0
98.0
99.0
100.0
同じように、画像などのバイナリデータの内容も配列に格納できます。下図のような900×600ピクセルのjpg画像(photo.jpg)の内容を配列に格納することを考えます。
そのためには、以下のスクリプトを用います。以下の例では、stats
コマンドの代わりにunknown
ターミナル(つまり、何もプロットしない)にplot
しながら、配列に値を代入しています。plot
するときにはbinary filetype=auto
とwith rgbimage
を使って、バイナリのjpgファイルを読み込んでいます。後で述べるように、配列はファイルと同じようにplot
することが可能で、その機能を使って配列の表示テストをすると、元の写真と同じ画像がgnuplot上に表示できていることがわかります。(using
の引数が (int(Nh*Nv-$1)%Nh)
や(int(Nh*Nv-$1)/Nh)
となっているのは、1番目のピクセルが画像の左上のピクセルになっているためです。)
Nh = 900 # 画像の横ピクセルサイズ
Nv = 600 # 画像の縦ピクセルサイズ
# 配列の確保(r-g-b)
array Pr[Nh*Nv]
array Pg[Nh*Nv]
array Pb[Nh*Nv]
# 配列への読み込み
set term unknown
i=1
plot "photo.jpg" binary filetype=auto using (Pr[i]=$1, Pg[i]=$2, Pb[i]=$3, i=i+1, $1):2:3 with rgbimage
set term pop
# 配列の表示テスト
set size ratio -1
plot Pr using (int(Nh*Nv-$1)%Nh):(int(Nh*Nv-$1)/Nh):(Pr[$1]):(Pg[$1]):(Pb[$1]) with rgbimage
gnuplotの配列の特徴として、plot
コマンドに配列名を渡すことで、配列のデータをグラフにプロットできるという点が挙げられます。
例えば、以下のように、配列Aをplot
コマンドに渡すと、配列の中身をグラフ化できます。この場合、横軸の値は配列の添え字・縦軸の値は配列に格納されている値になります。
array A[4] = [1, 5, 3, 8]
plot A with linespoints
配列のプロットの場合もusing
を使えます。この場合、1コラム目が配列の添え字、2コラム目が配列の値になります。下のコマンドでは、上の図と同じプロットができます。
array A[4] = [1, 5, 3, 8]
plot A using 1:2 with linespoints
需要が多いと思うのが、Xデータを例えば配列X[]に、Yデータを例えば配列Y[]に入れておいて、(X[i], Y[i])をプロットするという方法です。これは上記のusing
を使えば可能になります。
set size ratio -1
set angles degrees
array X[12] = [cos(0), cos(30), cos(60), cos(90), cos(120), cos(150), cos(180), cos(210), cos(240), cos(270), cos(300), cos(330)]
array Y[12] = [sin(0), sin(30), sin(60), sin(90), sin(120), sin(150), sin(180), sin(210), sin(240), sin(270), sin(300), sin(330)]
plot Y using (X[$1]):2 w lp
こうすると、配列Yの添え字(using
の$1
)が配列Xに与えられるので、以下のように(X[i], Y[i])をプロットできます。
やや二度手間感はありますが、plot
の部分を以下のようにした方がスクリプトの意味は読み取りやすいかもしれません。
plot Y using (X[$1]):(Y[$1]) w lp