はじめての gnuplot プログラミング

この Notebook は,

のプログラミング部分に相当する内容を JavaScript のかわりに gnuplot を使って書いてみたものです。

章番号はオリジナルの従来テキストに対応するように8章からはじめていますので,従来テキストを使用して授業を展開してきた教員や受講学生にも対応がわかりやすいかと思います。

また,テキストや授業と関係なく,初めて Jupyter Notebook で gnuplot を利用する学生や教員にとっても参考になるかもしれません。

gnuplot はグラフ作成アプリケーションですが,条件分岐や繰り返し処理などを利用することで,プログラミング言語としても利用できます。この Notebook では,プログラミング言語としての共通部分に焦点を絞って解説します。gnuplot 本来の機能を利用したさらなる活用例については,最終節のリンクを参照してください。

では,はじめましょう。

はじめてのプログラミング

エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。

消費支出 100,000円,飲食費 40,000円の人のエンゲル係数を求める例。Maxima では以下のような電卓的な使い方ですぐ答えが出ます。

本稿のように Jupyter Notebook 環境では,以下の1行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。 (パーセントにするのに 100 ではなく,100.0 をかける理由は後で説明します。)

In [1]:
print 40000 * 100.0/100000
Out[1]:
40.0

ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。

以下の例では,

  1. 1行目で変数 s1 に消費支出である 100000(円)という数値を代入し,
  2. 2行目で変数 s2 に飲食費である 40000(円)という数値を代入し,
  3. 3行目でエンゲル係数を計算して,変数 x1 に代入し,表示させています。
In [2]:
s1 = 100000
s2= 40000
x1 = s2*100.0/s1
print x1
Out[2]:
40.0

エラーとデバグ

意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。

掛け算を示す演算子は *,割り算を示す演算子は / であるところを誤って,×÷ と書くと,これらの記号は gnuplot では演算子として定義されていないのでエラーとなります。このようなエラーは文法エラー と呼ばれ,Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。

In [9]:
s1 = 100000
s2= 40000
x1 = s2× 100.0 ÷ s1
print x1
Out[9]:
undefined variable: s2×

400

別の種類の誤りもあります。たとえば,「100.0」を「1000」と入力した場合...

In [10]:
s1 = 100000
s2= 40000
x1 = s2*1000/s1
print x1
Out[10]:
400

上記の例では文法エラーの警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)

このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。

プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Shift + Enter)します。この作業をデバグといいます。

データの型と変数

上記の例の 1行目では,s1 という名前の変数に 100000 という 数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。

変数 ss に文字列を代入する場合は,以下のように " で囲みます。

In [11]:
ss = "エンゲル係数"
print ss
Out[11]:
エンゲル係数

式と演算

掛け算を示す演算子は *,割り算を示す演算子は /。足し算は +,引き算は - です。

文字列の連結演算は . を使います。以下の例を参照。

In [12]:
x0 = "エンゲル"
y0 = "係数"
z0 = x0.y0
print z0
Out[12]:
エンゲル係数

文字列と数値を直接連結することはできません。

In [13]:
s1 = 100000
s2 = 40000
x1 = s2*100.0/s1
z1 = "エンゲル係数は " . x1
print z1
Out[13]:
internal error : STRING operator applied to undefined or non-STRING variable

undefined variable: z1

そんなときは,sprintf() 関数で数値を文字列に変換して連結します。

In [14]:
s1 = 100000
s2 = 40000
x1 = s2*100.0/s1
z1 = "エンゲル係数は" . sprintf("%6.1f", x1)
print z1
Out[14]:
エンゲル係数は  40.0

print のところは,文字列にして連結しなくても,以下のようにも書くことができます。 print 文は,カンマ (,) で区切ることによって複数の項目(変数)を表示することができます。

In [15]:
s1 = 100000
s2 = 40000
x1 = s2*100.0/s1
z1 = "エンゲル係数は "
print z1, x1, " です。"
Out[15]:
エンゲル係数は 40.0 です。

主な演算子

加減乗除の演算子 +, -, *, /

優先的に計算したい箇所は丸括弧 ( )で グループ化します。

In [16]:
print 2 + (50 - 5 * 6)/4
Out[16]:
7

除算 / は常に浮動小数点数を返します。 // 演算子は 整数除算を行い、(小数部を切り捨てた) 整数値を返します。剰余は、% で求めます。 冪乗は ** です。

除算 / の例を以下に示します。

  • 1行目では,整数を整数で割るので,小数分を切り捨てた整数値を返します。

  • 2行目では,17. と最後に小数点をつけたので,実数値を返します。

  • なお,3行目のように,剰余は、% で求めます。

In [17]:
print 17/3
print 17./3
print 17 % 3 
# print 17 - 17/3*3 のこと
Out[17]:
5
5.66666666666667
2

冪乗は ** です。(^ は別の意味になりますから注意。)

In [18]:
print 2**4
Out[18]:
16

対話型プログラム(もどき)

上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみたいところですが,gnuplot にはキーボードからの入力を受け付けるような適当な命令文がないようです。代替案の例をあげてみます。

まず,以下のようなスクリプトファイル engel1.gpl をあらかじめ作成しておきます。

In [19]:
!cat "engel1.gpl"
x1 = s2*100.0/s1
z1 = "エンゲル係数は "
print z1, x1, " です。"

以下のように,コマンドラインから直接このスクリプトファイルを指定して実行させようとしても,変数 s1s2 が定義されていないのでエラーで実行できません。

$ gnuplot "engel1.gpl" 
"engel1.gpl" line 1: undefined variable: s2

そこで,以下のようにまず実行させたい文を -e に続けて書いてファイル名を指定します。

$ gnuplot -e "s1=100000; s2=34000" "engel1.gpl" 
エンゲル係数は 34.0 です。

関数の定義と呼び出し

(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。

以下の例では,1行目でエンゲル係数を計算する関数 calc() を定義し,5行目でその関数を呼び出しています。

In [20]:
calc(s1, s2) = s2*100.0/s1

s1 = 100000
s2 = 40000
print "エンゲル係数は ", calc(s1, s2)
Out[20]:
エンゲル係数は 40.0

いったん定義された関数は,以下のように何回でも利用できます。

In [21]:
print calc(120000, 53000)
Out[21]:
44.1666666666667

(練習)

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。

組み込み関数を使う

関数は上記の例のように自分で定義することもできますが,あらかじめ用意されている「組み込み関数」を使うこともできます。

以下では,組み込み関数の例として,

  • それ以下の整数を返す floor()
  • 整数部分を返す int()

を使用しています。

  • ちなみに,小数点以下を切り上げる関数は ceil() です。
In [22]:
print floor(12.3), int(12.3)
print floor(-12.3), int(-12.3)
Out[22]:
12 12
-13 -12

また,現在のところ gnuplot には四捨五入する関数がありませんので,sprintf() でフォーマットを %6.0f(全部で6桁,小数点以下は四捨五入して0桁)に変換して表示します。

In [23]:
s1 = 130000
s2 = 53000
x1 = s2 * 100.0 / s1
print x1, floor(x1), sprintf("%6.0f", x1)
Out[23]:
40.7692307692308 40    41

gnuplot で使える組み込み関数

gnuplot で使える組み込み関数については,以下のサイトが参考になります。

(練習)

小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。

条件分岐

条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。

以下のようなスクリプトファイル engel2.gpl を作成します。

In [24]:
! cat engel2.gpl
x1 = s2*100.0/s1
z1 = "エンゲル係数は "
if (x1 >= 80) {
    print z1, x1, " です。飲食費を使いすぎです。"}
  else {
    print z1, x1, " です。"}
    

コマンドラインでの実行例です。

$ gnuplot -e "s1=100000; s2=80000" "engel2.gpl" 
エンゲル係数は 80.0 です。飲食費を使いすぎです。
$ gnuplot -e "s1=100000; s2=37000" "engel2.gpl" 
エンゲル係数は 37.0 です。

if 文の書式は以下の通りです。

if (条件式) { 
    条件式が満たされた場合に実行する文
    } else {
    それ以外の場合に実行する文
}

if 文は入れ子にもできます。

(練習)

上記の条件分岐を,エンゲル係数が

  1. 80 以上の場合には「飲食費を使いすぎです。」
  2. 80 未満 40 以上の場合には「正常です。」
  3. 40 未満の場合には「もっと食べましょう。」

と表示させるように変更しなさい。(ヒント:if 文は入れ子にもできます。)

繰り返し処理

以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。

1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000 円)から,5行目の条件式(ここでは 80000 円以下)が成り立つ場合に,8行目にあるように 5000 円ずつ増やしながらエンゲル係数を表示します。

In [25]:
s1 = 100000
s2 = 40000
print "消費支出 ",s1,"円に対するエンゲル係数の値" 

while (s2 <= 80000){
    x1 = calc(s1, s2)
    print "飲食費 ", s2, "円の場合: ", x1
    s2 = s2 + 5000
}
Out[25]:
消費支出 100000円に対するエンゲル係数の値
飲食費 40000円の場合: 40.0
飲食費 45000円の場合: 45.0
飲食費 50000円の場合: 50.0
飲食費 55000円の場合: 55.0
飲食費 60000円の場合: 60.0
飲食費 65000円の場合: 65.0
飲食費 70000円の場合: 70.0
飲食費 75000円の場合: 75.0
飲食費 80000円の場合: 80.0

while 文の書式は以下のとおりです。

while (条件式){
    条件式が成り立つ場合に実行される文
}

同様の繰り返し処理を do for 文を使って行うこともできます。

do for 文の書式は以下のとおりです。

do for [ivar = istart: iend: istep] {
    ivar が istart から iend までの間,実行される文
    }
In [26]:
s1 = 100000
print "消費支出 ",s1,"円に対するエンゲル係数の値" 

do for [s2 = 40000: 80000: 5000] {
    x1 = calc(s1, s2)
    print "飲食費 ", s2, "円の場合: ", x1
}
Out[26]:
消費支出 100000円に対するエンゲル係数の値
飲食費 40000円の場合: 40.0
飲食費 45000円の場合: 45.0
飲食費 50000円の場合: 50.0
飲食費 55000円の場合: 55.0
飲食費 60000円の場合: 60.0
飲食費 65000円の場合: 65.0
飲食費 70000円の場合: 70.0
飲食費 75000円の場合: 75.0
飲食費 80000円の場合: 80.0

(練習)

以下の値を求めるプログラムを作成せよ。

$$\sum_{n = 1}^{10} n^2$$

さらに gnuplot を活用するために

この Notebook では,プログラミング言語として共通の部分に焦点を絞った gnuplot の使い方について解説しました。

グラフ作成アプリケーション gnuplot 固有の機能の活用例については,以下のページを参考にしてください。

日本語のマニュアルについては,以下のサイトを参照してください。