第2版 update: 2022年9月 葛西 真寿 弘前大学大学院理工学研究科
gnuplot はグラフ作成アプリケーションですが,条件分岐や繰り返し処理などを利用することで,プログラミング言語としても利用できます。この Notebook では,プログラミング言語としての共通部分に焦点を絞って解説します。
エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。
消費支出 110,000円,飲食費 37,000円の人のエンゲル係数を求める例。gnuplot では以下のように print
コマンドを使うとすぐ答えが出ます。
本稿のように Jupyter Notebook 環境では,以下の1行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。
(パーセントにするのに 100
ではなく,100.0
をかける理由は後で説明します。)
print 37000 * 100.0/110000
ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。
以下の例では,
#
で始まる部分はコメント。コメントはそれを読む人間のための注釈であり,プログラムの実行時には無視されます。spending
に消費支出である 110000
(円)という数値を代入し,food
に飲食費である 37000
(円)という数値を代入し,eng
に代入し,表示させています。# エンゲル係数を求める
# 消費支出 spending, 飲食費 food, エンゲル係数 eng
spending = 110000
food= 37000
eng = food * 100.0 / spending
print eng
意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。
掛け算を示す演算子は *
,割り算を示す演算子は /
であるところを誤って,×
や ÷
と書くと,これらの記号は gnuplot では演算子として定義されていないのでエラーとなります。このようなエラーは文法エラー と呼ばれ,Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。
spending = 110000
food= 37000
eng = food × 100.0 ÷ spending
print eng
別の種類の誤りもあります。たとえば,「100.0」を「1000」と入力した場合...
spending = 110000
food= 37000
eng = food * 1000 / spending
print eng
上記の例では文法エラーの警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)
このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。
プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Shift + Enter)します。この作業をデバグといいます。
上記の例の 1行目では,spending
という名前の変数に 110000
という
数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。
変数 moji
に文字列を代入する場合は,以下のように "
で囲みます。
moji = "エンゲル係数"
print moji
print 2 + 50 - 5 * 6 /2
print 2 + (50 - 5 * 6)/2
文字列の連結演算は .
を使います。以下の例を参照。
mo = "エンゲル"
ji = "係数"
moji = mo . ji
print moji
文字列と数値を直接連結することはできません。
spending = 110000
food= 37000
eng = food * 100.0 / spending
print "エンゲル係数は " . eng
そんなときは,sprintf()
関数で数値を文字列に変換して連結します。
spending = 110000
food= 37000
eng = food * 100.0 / spending
answer = "エンゲル係数は" . sprintf("%6.1f", eng)
print answer
print
のところは,文字列にして連結しなくても,以下のようにも書くことができます。
print
文は,カンマ (,
) で区切ることによって複数の項目(変数)を表示することができます。
spending = 110000
food= 37000
eng = food * 100.0 / spending
print "エンゲル係数は ", eng, " です。"
除算 /
は常に浮動小数点数を返します。 //
演算子は 整数除算を行い、(小数部を切り捨てた) 整数値を返します。剰余は、%
で求めます。
除算 /
の例を以下に示します。
1行目では,整数を整数で割るので,小数分を切り捨てた整数値を返します。
2行目では,17.
と最後に小数点をつけたので,実数値を返します。
3行目のように,剰余は、%
で求めます。
print 17 / 3
print 17./ 3
print 17 % 3
冪乗は **
です。
print 2**4
print 2**(1./2)
print sqrt(2)
1 天文単位(1 au)の距離を光速 c で進むのに要する時間はいくらか。ただし,
149597870700
m299792458
m/s である。参考:
上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみたいところですが,gnuplot にはキーボードからの入力を受け付けるような適当な命令文がないようです。
(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。
以下の例では,1行目でエンゲル係数を計算する関数 calc()
を定義し,5行目でその関数を呼び出しています。
calc(spending, food) = food * 100.0 / spending
spending = 110000
food = 37000
print "エンゲル係数は ", calc(spending, food)
いったん定義された関数は,以下のように何回でも利用できます。
print calc(120000, 53000)
消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。
gnuplot では,$\sin x, \cos x, \tan x$などの数学関数が利用できます。また,数学定数として円周率 pi
などが使えます。
print pi, " ", sin(pi/2)," ", cos(pi/3)," ", tan(pi/4)
以下では,関数の例として,
floor()
int()
を使用しています。
ceil()
です。print floor(12.3), int(12.3)
print floor(-12.3), int(-12.3)
spending = 110000
food = 37000
eng = calc(spending, food)
print "エンゲル係数は ", floor(eng), " です。(小数点以下切り捨て)"
また,現在のところ gnuplot には四捨五入する関数がありませんので,「書式指定」で説明する sprintf()
でフォーマットを指定して表示します。
gnuplot で使える組み込み関数については,以下のサイトが参考になります。
小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。
条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。
spending = 110000
food = 37000
eng = calc(spending, food)
if (eng >= 40) {
print "エンゲル係数は ", floor(eng), " です。高い値です。"}
else {
print "エンゲル係数は ", floor(eng), " です。"}
food = 47000
eng = calc(spending, food)
if (eng >= 40) {
print "エンゲル係数は ", floor(eng), " です。高い値です。"}
else {
print "エンゲル係数は ", floor(eng), " です。"}
if
文の書式は以下の通りです。
if (条件式) {
条件式が満たされた場合に実行する文
} else {
それ以外の場合に実行する文
}
if
文は入れ子にもできます。
上の例では,eng >= 40
つまり eng
が 40
以上のとき,という関係演算子を使いました。他の例は以下のとおりです。
a < b
a
が b
より小さい
a <= b
a
が b
以下
a > b
a
が b
より大きい
a >= b
a
が b
以上
a == b
a
と b
が等しい
以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。
1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000
円)から,5行目の条件式(ここでは 80000
円以下)が成り立つ場合に,8行目にあるように 5000
円ずつ増やしながらエンゲル係数を表示します。
spending = 110000
food = 30000
print "消費支出 ", spending, "円に対するエンゲル係数の値"
while (food <= 50000){
eng = calc(spending, food)
print "飲食費 ", food, "円の場合: ", eng
food = food + 5000
}
while
文の書式は以下のとおりです。
while (条件式){
条件式が成り立つ場合に実行される文
}
同様の繰り返し処理を do for
文を使って行うこともできます。
do for
文の書式は以下のとおりです。
do for [ivar = istart: iend: istep] {
ivar が istart から iend までの間,実行される文
}
spending = 110000
print "消費支出 ", spending, "円に対するエンゲル係数の値"
do for [food = 30000: 50000: 5000] {
eng = calc(spending, food)
print "飲食費 ", food, "円の場合: ", eng
}
以下の値を求めるプログラムを作成せよ。
$$\sum_{n = 1}^{10} n^2$$array a[5] = [5, 4, 3, 2, 1]
print "全成分を一挙に表示"
print a
print "全成分を縦に表示"
do for [i=1:5]{
print a[i]
}
# 配列 b の5つの要素を全て 0 に
array b[5] = [0, 0, 0, 0, 0]
print b
# 3番目の要素の値を設定
b[3] = 3
print b
以下のようなテキストファイル mydata.txt
を用意します。
$ cat mydata.txt
1
2
3
4
5
6
ファイル mydata.txt
の数値を配列 a
に読み込みます。
# データの行数
stats "mydata.txt" nooutput
N = STATS_records
# 配列の宣言
array a[N]
# データファイルから配列へ代入
stats "mydata.txt" using (a[$0+1] = $1, 0) nooutput
# 配列の内容の表示
print a
# sprintf() で書式を設定できる
do for [i=1:N]{
print sprintf("%d",a[i])
}
# 要素の総和,平均,最大値,最小値を出力します。
stats "mydata.txt" using 1 nooutput
print "要素の総和は ", STATS_sum
print "要素の平均は ", STATS_mean
print "最大値は ", STATS_max
print "最小値は ", STATS_min
次は,以下のような「6行2列」のデータが入ったテキストファイル mydata2.txt
を読み込む例です。
$ cat mydata2.txt
1 1
2 4
3 9
4 16
5 25
6 36
# データの行数
stats "mydata2.txt" using 1 nooutput
N = STATS_records
# 配列の宣言。1次元配列のみ。
array bx[N]
array by[N]
# データファイルから配列へ代入
stats "mydata2.txt" using (bx[$0+1] = $1, 0) nooutput
stats "mydata2.txt" using (by[$0+1] = $2, 0) nooutput
# 配列の内容の表示
print bx
print by
# sprintf() で書式を設定できるが,
# なぜか Jupyter Notebook では行頭のスペースが無視される。
# 苦肉の策として全角スペースを入れてみた。
print "1234567890 桁数"
do for [i=1:N]{
print " ", sprintf("%3d",bx[i]), sprintf("%5d",by[i])
}
# 要素の総和,平均,最大値,最小値を出力します。
stats "mydata2.txt" using 1:2 nooutput
print "x 要素の総和は ", STATS_sum_x
print "x 要素の平均は ", STATS_mean_x
print "x 最大値は ", STATS_max_x
print "x 最小値は ", STATS_min_x
print ""
print "y 要素の総和は ", STATS_sum_y
print "y 要素の平均は ", STATS_mean_y
print "y 最大値は ", STATS_max_y
print "y 最小値は ", STATS_min_y
配列の要素の総和は以下のように sum
を使っても計算できます。
print sum [i = 1:N] by[i]
print sprintf("%d", sum [i = 1:N] by[i])
ファイルへの書き込み例です。この例では,$𝑥$ と $\sin 𝑥$ の値を $𝑥=0.1$ から $𝑥=1.0$ まで書き込んでいます。
set print "myoutput.txt"
do for [i=1:10]{
x = 0.1 * i
print x," ", sin(x)
}
set print
! cat myoutput.txt
実数を表示する際,全体の文字数や小数点以下の桁数を指定して表示させる例です。
例えば "%8.5f"
は全体で 8 桁の表示範囲をとり,小数点以下は 5 桁表示します。(するはずですが,Jupyter Notebook では行頭のスペースが無視されるようです。)
sqrtwo = sqrt(2)
print "12345678901234567890 桁数"
print sqrtwo
print sprintf("%f", sqrtwo)
print sprintf("%8.5f", sqrtwo)
print sprintf("%18.15f", sqrtwo)
# 小数点以下の桁数のみを"%.2f" のように指定することもできます。
print sprintf("%.2f", sqrtwo)
以下の出力を,エンゲル係数の値を(四捨五入して)小数点以下1桁まで表示するように変更しなさい。
spending = 110000
food= 37000
eng = food * 100.0 / spending
print "エンゲル係数は ", eng, " です。"
非常に大きい数や小さい数を表示するときには, $1.9891 \times 10^{30}$ や $6.67430 \times 10^{-11}$ などのような指数表示をします。
例えば "%9.2e"
では,全体で 9 桁の表示分を確保し,小数点以下は 2 桁表示します。(Jupyter Notebook の表示では行頭のスペースが無視されるかもしれません。)
c = 299792458
print sprintf("%9.2e", c)
print sprintf("%15.8e", c)
print sprintf("光速は%15.8e m/s です。", c)
# 小数点以下の桁数のみを"%.2e" のように指定することもできます。
print sprintf("%.2e", c)
文字列の書式指定は "%s"
です。(Jupyter Notebook では行頭のスペースが無視されるので,苦肉の策として全角スペースを入れてます。)
ten = "1234567890"
print ten
# 行頭に全角スペース
print sprintf(" %8s", ten[5:5])
print sprintf(" %8s", ten[4:5])
print sprintf(" %8s", ten[1:5])
整数の書式指定は "%d"
です。"%5d"
のように桁数を指定することもできます。
array v[4] = [1, 23, 456, 7890]
do for [i=1:4]{
print v[i]
}
せっかく桁数を揃えて右寄せで表示させようとしても,Jupyter Notebook では最初の行頭のスペースが無視されてしまいます。
do for [i=1:4]{
print sprintf("%5d", v[i])
}
苦肉の策として,表示のためだけに全角スペースを入れてみます。
do for [i=1:4]{
print sprintf(" %5d", v[i])
}
この Notebook では,プログラミング言語として共通の部分に焦点を絞った gnuplot の使い方について解説しました。
グラフ作成アプリケーション gnuplot 固有の機能の活用例については,以下のページを参考にしてください。
日本語のマニュアルについては,以下のサイトを参照してください。