第9回(2011年12月14日)


今日の演習

今日は以下の内容で演習を行います.


今日の目標

gnuplot の使い方の基本を身につける.


gnuplot 入門

今回は、まず gnuplot と呼ばれるアプリケーションを用いて、関数やデータの可視化を行います.この gnuplot は、手軽に使える上に様々な便利な機能があるため、学術論文等に掲載されるグラフ等の作成にも、使われています。今回はこの gnuplot を用いて、簡単なグラフや図形の描画を実践してみます.


まず幾つかの関数をgnuplotで描画してみます.

ターミナルで,

gnuplot

と打ち込み,改行キーを押すことで gnuplot を起動することができます.起動すると,ターミナル上のプロンプトは gnuplot のものとなり,gnuplot のコマンドを入力できる状態となります.そこで,

plot x*x - 2

とすることにより,グラフを得ることができます.x の範囲は自動的に決められますが,こちらで設定することもできます.x2は x**2 と表記します.

plot [0:2] x**2 - 2

gnuplot は,

quit

で終了できます.

たとえば,次のようなものを試してみてください.

plot [-6:6][-1.5:1.5] sin(x)

plot [-6:6][-1.5:1.5] cos(x)

plot x**3 - x**2 + 5



グラフの横軸(x)、縦軸(y)の表示範囲は

plot [(min_x):(max_x)][(min_y):(max_y)] F(x)

として、min_x、 max_x、min_y、max_y を与えることで決ります。縦軸(y)のみ範囲を与えたい場合は

plot [][(min_y):(max_y)] F(x)

とすると出来ます。



ファイル内のデータの描画

次に、ファイルに書き込まれているデータからグラフを作成します。まずdata-ex1というファイルを作成し、その中に以下のような形で数値を書き込んで下さい。

- - - - - - - - - - - - - - - -
2.0 1.0 -3.0
-2.0 1.0 1.0
-2.0 -1.0 2.0
2.0 -1.0 -3.5
- - - - - - - - - - - - - - - -

(注意)横に並ぶ数値の間には、必ずスペースを入れてください。

次にターミナルで、gnuplotとして gnuplot を起動し、

plot [-4.0:4.0][-4.0:4.0] "data-ex1"

とすると(改行キーを押すと)、以下のように、(2,1),(-2,1),(-2,-1),(2,-1)に4つ点が表示されるはずです。ここでは見易くするために、横軸、縦軸の範囲を-4〜4にしました。

gnuplotは、横に並ぶ数字の組をある一点の座標とし、表示します. このデータファイルには、4つの点の座標が記述されていることになります. 何も指示しなければ,横軸(x軸)に一列目、縦軸(y軸)に2列目が用いられます。


ではここで、

plot [-4.0:4.0][-4.0:4.0] "data-ex1" using 2:1

とします。すると先程と縦と横が入れ替わります。また

plot [-4.0:4.0][-4.0:4.0] "data-ex1" using 2:3

としますと(1,-3),(1,1),(-1,-2),(-1,-3.5)の4点が表示されます(下図)。この場合、横軸に 2 列目、縦軸に 3 列目 の値が使われています. 「using a:b」というのが「横軸に a 列目、縦軸に b 列目を用いる」というコマンドになっています。



次に線を描きます.例えば

plot [-4.0:4.0][-4.0:4.0] "data-ex1" with lines

とすると、(2,1),(-2,1),(-2,-1),(2,-1)が直線で結ばれます.この「with lines」は、点をデータファイルに書かれている順に直線で結ぶコマンドです。また

plot [-4.0:4.0][-4.0:4.0] "data-ex1" with linespoints

とすると、4つの点とそれをつなぐ直線が描かれます.


今度は以下のように一行スペースを入れます。

- - - - - - - - - - - - - - - -
2.0 1.0 -3.0
-2.0 1.0 1.0

-2.0 -1.0 2.0
2.0 -1.0 -3.5
- - - - - - - - - - - - - - - -

同じように gnuplot を起動し、

plot [-4.0:4.0][-4.0:4.0] "data-ex1" with lines

とか

plot "data-ex1" using 3:1 with lines

とすると今度は、2本の直線が現れます(下図)。このように行を空けることで、複数の図形を描画できます。



描画のコマンドをまとめますと、(データファイル中の a 列目をx 座標(横軸)、 b 列を y 座標(縦軸)として、それぞれ min_x 〜 max_x、min_y 〜 max_y の範囲で線描画(点描画)させたいとき)

plot [(min_x):(max_x)][(min_y):(max_y)] "データファイル名" using a:b with lines (points)

という順番で指定していきます.


(参考までに)

複数のファイルや関数のグラフを同時に表示させたい場合は、

plot x**2 - 2, 0, "data-ex1" w lp

というふうにカンマで区切って並べます。

ちなみに、with lines は w l、with linespoints は w lp と略せます。


y = sin(x) のグラフの描画

これから、gnuplot を使って y = sin(x) のグラフを描きます.もちろん,gnuplot では,

plot sin(x)

とすれば y = sin(x) のグラフが得られるのですが,gnuplot がどのようにしてそれを描いているかわかりません.そこで少々遠回りをします.

実際には y = sin(x) のグラフは滑らかな曲線ですが、コンピュータではそのような滑らかな曲線を直接は扱えませんので折れ線で近似して描画します.つまり、変数 x は、実際には連続的に変化するものですが、とびとびの値を使います.後の例で見るように、とびとびとはいえ、十分に細かく取れば、折れ線で曲線をうまく近似することができます.

図のように [0, L] 区間を N-1 等分して、その等分した小区間の幅を dx とします.N-1 等分すると図のように N 個の区切りが現れますが、それらに 0,1, ...., i, ...., N-1 と番号をつけることにします.また、 N を分割数、 dx を分割幅と呼びます.このとき、

Xi = i*dx

の部分についてそれぞれ y = sin(Xi) を計算し、それらを折れ線で結ぶことを考えます.C言語風に書けば

X[i] = dx*i;
Y[i] = sin(X[i]);

をそれぞれ求め、(X[0], Y[0]), (X[1],Y[1]), ...., (X[N-1], Y[N-1]) を結ぶ折れ線を描くことになります.これをプログラムにすると次のようになります.(上記の説明にあわせるためにかなり無駄なことをしています.)ファイル名を 1214-1.c として打ち込み、コンパイルして実行してみてください.

コンパイルするときには

cc 1214-1.c -o 1214-1 -lm

というふうに,-lm を付け加えて下さい.(下の補足2を参照)


- - - - - - - - - - - - - - - -

#include <stdio.h>
/* 次の文は数学関数を使う場合に必要 */
#include <math.h>

/*** 定数の定義 ***/
#define N (5) /*** 空間分割数 ***/
#define PI (3.1415926) /*** 円周率 ***/
#define L (2*PI) /*** 区間 [0,L] ***/

main()
{
  int i;
  float X[N], Y[N], dx;

  /* 分割幅 dx を求める */
  dx = L/(N - 1);

  /* X[i] を求める */
  for(i = 0; i < N; i++)
  {
    X[i] = i*dx;
  }

  /* Y[i] を求める */
  for(i = 0; i < N; i++)
  {
    Y[i] = sin(X[i]);
  }

  /* 結果を数値として表示 */
  for(i = 0; i < N; i++)
  {
    printf("%f %f\n", X[i], Y[i]);
  }
}

- - - - - - - - - - - - - - - -

補足1:定数(#define 文の使い方)

#define 文を使うと定数を定義することができます.通常、#include 文の後に書きます.例えば、

#define  N  (5)

と書いた場合、後のプログラム中の N は全て (5) に置き換わります.例えば、次のように使えます.

- - - - - - - - - - - - - - - -

#include <stdio.h>

#define A (5)
#define PI (3.1415926)

main()
{
    printf("%d %f\n", A, PI);
}

- - - - - - - - - - - - - - - -

#define 文は、実は単に文字列を置き換えるだけです.つまり、printf 関数部分に A および PI がありますが、それらはコンパイル時に次のように書き換えられます.

printf("%d, %f\n", (5), (3.1415926));

#define 文を上手に使うと、プラグラムが大変見通しのよいものとなります.


補足2:数学関数(三角関数等)の使い方

C言語では上記プログラムのように、三角関数等の様々な数学関数を用いることができます.数学関数を用いる場合には、

#include <math.h> をプログラムの先頭付近に書き入れることと、コンパイル時に、ファイル名を 1214-1.c とすると、

cc 1214-1.c -o 1214-1 -lm

というふうに、最後に -lm を付け足す必要があります.

sin()の他にcos(), tan(), log(), exp()などいろいろ用意されています。


データファイルの作成(本題に戻ります)

上記プログラムを使って、N とグラフの関係を見てみましょう.まず,プログラムをコンパイルします.

cc 1214-1.c -o 1214-1 -lm

続いて実行してみましょう.

./1214-1

とすると、数値の列が表示されます.これら数値をgnuplotでグラフ化しましょう.この数値の列からなる、データファイル作成するには、次のようにします.

./1214-1 > N=5.data

上記のようにすることで、数値列は画面に表示されず、N=5.data というファイル名のファイルが作成され、そこに書き込まれます.

(ターミナル上で、more N=5.data などとすると、確認できます. なお今回は、N=5なのでこのような名前にしましたが、ファイル名は基本的に自由です.).

gnuplot を起動し,

plot [0:2*pi] "N=5.data" with linespoints, sin(x)

緑色で書かれたグラフが描きたいグラフ (y = sin(x) )です.赤色が描こうとしている折れ線です.N=5 の場合は上図のようになります(赤線).これでは、まったく y = sin(x) のグラフには見えません.

つづいて、プログラムを変更しNを10にして同様に N=10.data を作成しグラフを描いてみます.曲線っぽくみえてきました.

N=50 の場合.見た目はほぼ y=sin(x) のグラフに見えます.

 


さて,曲線を折れ線で近似することができました.実は gnuplot も内部で同じことをやっています.標準状態で100本の折れ線で描いています.

例えば.

set sample 10
plot [0:2*pi] sin(x)

等とすると、その指定された数(ここでは 10本)の折線で曲線を描きます.(試してみてください.)


先のプログラム例では、 説明の為,少々冗長なプログラムとなっていました.実際には以下のプログラムでも同様の結果が得られます.各人確認してください.

- - - - - - - - - - - - - - - -

#include <stdio.h>
#include <math.h>

/* 定数の定義 */
#define N (5)
#define PI (3.1415926)
#define L (2*PI)

main()
{
  int i;
  float dx;

  /* dx を求める */
  dx = L/(N - 1);

  /* 結果を画面に表示 */
  for(i = 0; i < N; i++)
  {
    printf("%f %f\n", i*dx, sin(i*dx));
  }
}

- - - - - - - - - - - - - - - -

課題1

1214-1.cを改造して、楕円を描写するプログラムを作成せよ.(N = 100程度で)例えばデータファイルを daen-data とし、gnuplot で

plot 'daen-data' w l

としたときに、閉じた楕円が描かれている事.

(付け足し)gnuplot 内で

gnuplot> set size square
gnuplot> plot [-4:4][-4:4]'(データファイル名)' with lines

とすると、縦軸、横軸の縮尺が同じグラフが描けます.(試してみて下さい.)


課題2

1214-1.cを改造して、正三角形、正方形、正五角形、正六角形を描いてください.(N を適度に変化させて)


課題3(自由課題)

上の課題で使用する三角関数を、C言語用のライブラリー (math.h) で与えられた sin(), cos() ではなく、次のような仕様を満たす関数として自前で作って、上の課題で使用してみましょう. (前回の関数の定義と利用の復習です.)

関数名:float my_sin(float w)

仕様: 実数型の数を受け取り、-π ≦ w < π でないならばそうそうなるまで 2π足し算 or 引き算し, -π ≦ w < π を満たすとき

  w - w3/3! + w5/5! - w7/7! + w9/9! - w11/11! を戻り値として返す

同様に float my_cos(float w) も作成せよ.


課題4(次回)

以下の様な、6個の独立した(線等で繋がっていない)直径1の円が、正六角形状に並んでいる絵を描くプログラムを作成し、gnuplotで描画してください. 円一つにつき50個程度の点(N=50)を使い、例えばデータファイルを en6-data とし

plot 'en6-data' w l

としたときに、以下と同様の絵が描かれること。

(ヒント)変数は円の中心座標、円周の各点の座標、およびそれぞれの位相。また下記の例のように、改行コマンド printf("\n") をうまく使ってデータファイルに空白行をつくる


データに空白行を作る例

例えば次のように printf("\n")(何も書かずに改行のみする)を使うと、下記のような空白行のあるデータが作成できます。

- - - - - - - - - - - - - - - -

#include <stdio.h>

main()
{

  int i,j;

  /** 3回繰り返し **/
  for(i=0; i<3; i++)
    {

    /** 0 1 2 を表示させる部分 **/
     for(j=0; j<3; j++)
       {

       printf("%d\n", j);

     }

  /** j についての繰り返し処理後に何も書かずに改行(空白行をつくる) **/
  printf("\n");

  }

}

- - - - - - - - - - - - - - - -

これをコンパイルして実行すると

0
1
2

0
1
2

0
1
2

と、空行付きで表示されます。課題4ではこれを応用してください。


今日学んだ事

  • gnuplot とCプログラムでグラフ、図形を描画しました。

戻る