kengo700のナレッジベース

誰かの役に立つと思う情報を発信するブログ

gnuplotで実験データのアニメーションを作る

この記事では、gnuplot(グラフ作成ソフト)によって、実験データのアニメーションを作成する方法を紹介します。

f:id:kengo700:20160115211947g:plain

はじめに

gnuplotにはgifアニメを出力する機能があり、比較的簡単にアニメーションを作成することができます。gifアニメはPowerPointのスライドに張り付けると、スライドショーの時に自動的に再生されるので、作成したgifアニメは発表資料に使うことができます。

特に3次元データや時系列データの場合は、アニメーションで表現することで分かりやすくなると思います。

なお、この記事ではgnuplotの基本的な使い方は知っているものとして、説明します。

gnuplotによるgifアニメ作成の基本

gnuplotのgifアニメを出力する機能を利用しつつ、グラフを一コマずつプロットする事でgifアニメを作成できます。またグラフのプロットは、ifおよびreread関数を利用したループを用いることで簡単に行なうことができます。

以下にgifアニメを作成するスクリプトのサンプルを載せます。 両方のファイルを保存し、gpファイルの方をgnuplotから読み込む (gnuplotでload "gnuplot_sample1.gp")と、サンプルのgifアニメが出力されます。

[gnuplot_sample1.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey                 # 凡例の非表示
set xrange [-10:10]       # x軸方向の範囲の設定
set yrange [-3:3]         # y軸方向の範囲の設定

set term gif animate      # 出力をgifアニメに設定
set output "sample1.gif"  # 出力ファイル名の設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 0        # ループ変数の初期値
n1 = 2*pi     # ループ変数の最大値
dn = 2*pi/20  # ループ変数の増加間隔

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_sample1.plt" 

[gnuplot_sample1.plt(クリックで表示)]

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0  # ループ変数の初期化

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
plot sin(x-n), sin(x+n), sin(x-n)+sin(x+n)

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn            # ループ変数の増加
if ( n < n1 ) reread  # ループの評価
undefine n            # ループ変数の削除

[gifアニメサンプル、定常波(クリックで表示)]

f:id:kengo700:20160115212050g:plain

データファイルを利用したgifアニメの作成

ループごとに毎回データファイルを読み込むことによって、データファイルからアニメーションを作成する事ができます。読み込むデータファイルは、以下のようにデータを2行の空白によって塊に分けて記述します。このような形式のデータであれば、プロットする際に「index」コマンドによってプロットするデータの塊を選択する事ができます。

X Y
X Y
...


X Y
X Y
...

以下にgifアニメを作成するファイルのサンプルを載せます。 また例として2次元の反射壁ランダムウォークの計算プログラムとgifアニメを載せます.

[gnuplot_sample2.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey                 # 凡例の非表示
set xrange [-50:50]       # x軸方向の範囲の設定
set yrange [-50:50]       # y軸方向の範囲の設定
set size square           # 図を正方形にする

set term gif animate      # 出力をgifアニメに設定
set output "sample2.gif"  # 出力ファイル名の設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 1    # ループ変数の初期値
n1 = 99   # ループ変数の最大値
dn = 1    # ループ変数の増加間隔

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_sample2.plt" 

[gnuplot_sample2.plt(クリックで表示)]

# gnuplot_sample2.plt

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0  # ループ変数の初期化

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
plot "sample_data.dat"  index n using 1:2 with lines # n番目のデータのプロット

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn            # ループ変数の増加
if ( n < n1 ) reread  # ループの評価
undefine n            # ループ変数の削除

[プログラムサンプル、ランダムウォーク(クリックで表示)]

// 
// ランダムウォークのデータを出力する
//

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define n 50000         //最大繰り返し回数

int main()
{
    int i, j;
    int random_num;

    FILE *output;
    char str[100];
    double x[n],y[n];

    output = fopen("sample_data.dat","w");

    srand((unsigned) time(NULL));

    x[0] = 0.0;
    y[0] = 0.0;

    for( i = 1; i < n; i++) {

        x[i] = x[i-1];
        y[i] = y[i-1];

        //乱数の数字の範囲によって4つの方向のどちらかへ移動
        random_num = rand()%4; 
        switch(random_num){
            case 0:
                x[i] = x[i-1] - 1.0;
                break;
            case 1:
                x[i] = x[i-1] + 1.0;
                break;
            case 2:
                y[i] = y[i-1] - 1.0;
                break;
            case 3:
                y[i] = y[i-1] + 1.0;
                break;
        }

        // 反射壁
        if( x[i-1] == 40 ) {
            x[i] = 39;
            y[i] = y[i-1];
        } else if( x[i-1] == -40 ) {
            x[i] = -39;
            y[i] = y[i-1];
        } else if( y[i-1] == 40 ) {
            x[i] = x[i-1];
            y[i] = 39;
        } else if( y[i-1] == -40 ) {
            x[i] = x[i-1];
            y[i] = -39;
        }

        // 500回ごとにファイルへ出力
        if( i%500 == 0) {
            for(j = 0; j <= i; j++) {
                fprintf(output,"%f %f\n",x[j],y[j]);
            }

            // 2行空白(gnuplotでの解析用)
            fprintf(output,"\n\n");
        }
    }

    fclose(output);

    return 0;
}

[gifアニメサンプル、ランダムウォーク(クリックで表示)]

f:id:kengo700:20160116113858g:plain

データファイルが連番ファイルの場合

読み込むデータを前述したように2行の空白で分ける代わりに、別ファイルに分けた連番ファイルを利用する場合は、以下のサンプルのように、毎ループごとにその連番ファイルを読み込みプロットします。

[gnuplot_sample3.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey                # 凡例の非表示
set xrange [-50:50]      # x軸方向の範囲の設定
set yrange [-50:50]      # y軸方向の範囲の設定
set size square          # 図を正方形にする

set term gif animate     # 出力をgifアニメに設定
set output "sample3.gif" # 出力ファイル名の設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 1    # ループ変数の初期値
n1 = 99   # ループ変数の最大値
dn = 1    # ループ変数の増加間隔

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_sample3.plt" 

[gnuplot_sample3.plt(クリックで表示)]

# gnuplot_sample3.plt

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0 # ループ変数の初期化

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
filename = sprintf("sample_data_%03d.dat", n) # n番目のデータファイルの名前の生成
plot filename using 1:2 with lines            # n番目のデータのプロット

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn            # ループ変数の増加
if ( n < n1 ) reread  # ループの評価
undefine n            # ループ変数の削除

連番gifファイルへ出力する場合

前述した方法ではgnuplotのgifアニメ作成機能を用いていましたが、gnuplotでgifファイルを一枚ずつ出力し、そのgifファイルから別のソフトを用いてアニメーションを作成する事もできます。

以下に連番gifファイルを出力するスクリプトのサンプルを載せます。

[gnuplot_sample4.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey             # 凡例の非表示
set xrange [-50:50]   # x軸方向の範囲の設定
set yrange [-50:50]   # y軸方向の範囲の設定
set size square       # 図を正方形にする

set term gif          # 出力をgifに設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 1    # ループ変数の初期値
n1 = 99   # ループ変数の最大値
dn = 1    # ループ変数の増加間隔

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_sample4.plt" 

[gnuplot_sample4.plt(クリックで表示)]

# gnuplot_sample4.plt

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0  # ループ変数の初期化

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
filename = sprintf("sample_gif_%03d.gif", n)        # n番目の出力ファイルの名前の生成
set output filename                                 # 出力ファイル名の設定
plot "sample_data.dat" index n using 1:2 with lines # n番目のデータのプロット

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn                # ループ変数の増加
if ( n < n1 ) reread      # ループの評価
undefine n                # ループ変数の削除

視点の回転

視点を回転するアニメーションを作成すれば、3次元データなどは見やすくなると思います。ループごとに視点を少しずつずらすことによって、視点を回転させるアニメーションを作成する事ができます。

以下に視点を回転させるgifアニメを作成するスクリプトのサンプルを載せます。

[gnuplot_sample5.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey                 # 凡例の非表示
set xrange [-5:5]         # x軸方向の範囲の設定
set yrange [-5:5]         # y軸方向の範囲の設定
set ticslevel 0           # z軸の原点をxy平面に合せる

set isosamples 20, 20     # メッシュの間隔の設定
set hidden3d              # 隠線処理の設定

set term gif animate      # 出力をgifアニメに設定
set output "sample5.gif"  # 出力ファイル名の設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 0   # ループ変数の初期値
n1 = 89  # ループ変数の最大値
dn = 1   # ループ変数の増加間隔

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_sample5.plt" 

[gnuplot_sample5.plt(クリックで表示)]

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0  # ループ変数の初期化

#-------------------------------------------------------------------------------
# 視線の変更
#-------------------------------------------------------------------------------
set view 60, n;     # 視点の変更

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
splot (x**2)*(y**2)

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn            # ループ変数の増加
if ( n < n1 ) reread  # ループの評価
undefine n            # ループ変数の削除

[gifアニメサンプル、視点の回転(クリックで表示)]

f:id:kengo700:20160115212418g:plain

その他のサンプルスクリプト置き場

三次元正葉曲線の軌跡

このページの冒頭のアニメーションです.

[gnuplot_folium.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey                    # 凡例の非表示
set xrange [-1.2:1.2]        # x軸方向の範囲の設定
set yrange [-1.2:1.2]        # y軸方向の範囲の設定
set zrange [-1.2:1.2]        # z軸方向の範囲の設定
set ticslevel 0              # z軸の原点をxy平面に合せる
set grid xtics ytics ztics   # グリッドの表示
set border 127+256+512       # 軸の表示

set parametric               # 媒介変数表示の設定
set angles degrees           # 角度の単位を[deg]に変更
set style line 1 linewidth 3 # 線を太くする

set term gif animate         # 出力をgifアニメに設定
set output "folium.gif"      # 出力ファイル名設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 0     # ループ変数の初期値
n1 = 360   # ループ変数の最大値
dn = 4     # ループ変数の増加間隔

delay = 1  # 表示する残像の遅れ
a = 5      # 正葉曲線の係数

#-------------------------------------------------------------------------------
# 関数の定義
#-------------------------------------------------------------------------------
fx(t) = cos(a*t)*cos(2*t)
fy(t) = cos(a*t)*sin(2*t)
fz(t) = cos(a*t)

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_folium.plt" 

[gnuplot_folium.plt(クリックで表示)]

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0  # ループ変数の初期化

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
# 3次元の正葉曲線と残像のプロット
splot fx(n),fy(n),fz(n) linestyle 1,\
fx(n+delay), fy(n+delay),fz(n+delay) linestyle 1,\
fx(n+2*delay), fy(n+2*delay),fz(n+2*delay) linestyle 1,\
fx(n+3*delay), fy(n+3*delay),fz(n+3*delay) linestyle 1,\
fx(n+4*delay), fy(n+4*delay),fz(n+4*delay) linestyle 1,\
fx(n+5*delay), fy(n+5*delay),fz(n+5*delay) linestyle 1,\
fx(n+6*delay), fy(n+6*delay),fz(n+6*delay) linestyle 1,\
fx(n+7*delay), fy(n+7*delay),fz(n+7*delay) linestyle 1,\
fx(n+8*delay), fy(n+8*delay),fz(n+8*delay) linestyle 1,\
fx(n+9*delay), fy(n+9*delay),fz(n+9*delay) linestyle 1

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn                # ループ変数の増加
if ( n < n1 ) reread      # ループの評価
undefine n                # ループ変数の削除

球の回転

アイキャッチ画像の、球の回転アニメーションです。

[gnuplot_sphere.gp(クリックで表示)]

#-------------------------------------------------------------------------------
# gnuplotの設定
#-------------------------------------------------------------------------------
reset
set nokey                # 凡例の非表示
set noxtics              # 軸の非表示
set noytics
set noztics
set ticslevel 0          # z軸の原点をxy平面に合せる
set size 0.7,1.0         # サイズの設定
set border 0             # 軸の非表示

set parametric           # 媒介変数表示の設定
set urange [0:360]       # 媒介変数uの範囲の設定
set vrange [-90:90]      # 媒介変数vの範囲の設定
set angle degree         # 角度の単位を[deg]に変更
set isosample 18,18      # メッシュの間隔の設定

set terminal gif animate # 出力をgifアニメに設定
set output "sphere.gif"  # 出力ファイル名設定

#-------------------------------------------------------------------------------
# 変数の設定
#-------------------------------------------------------------------------------
n0 = 0      # ループ変数の初期値
n1 = 20     # ループ変数の最大値
dn = 1      # ループ変数の増加間隔

#-------------------------------------------------------------------------------
# ループの開始
#-------------------------------------------------------------------------------
load "gnuplot_sphere.plt" 

[gnuplot_sphere.plt(クリックで表示)]

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
if(exist("n")==0 || n<0) n = n0  # ループ変数の初期化

#-------------------------------------------------------------------------------
# 視線の変更
#-------------------------------------------------------------------------------
set view 60, n;     # 視線の変更

#-------------------------------------------------------------------------------
# プロット
#-------------------------------------------------------------------------------
# 球のプロット
splot cos(u)*cos(v),sin(u)*cos(v),sin(v)

#-------------------------------------------------------------------------------
# ループ処理
#-------------------------------------------------------------------------------
n = n + dn            # ループ変数の増加
if ( n < n1 ) reread  # ループの評価
undefine n            # ループ変数の削除

参考資料

おわりに

もっといい方法があれば教えてください。