Rubyプログラミング

Rubyプログラミング(4)


1.Windows版gnuplotを使用してグラフを描く

下記のページではPythonを使って数値計算を行い、その結果を外部コマンドとしてgnuplotに渡すことによりグラフの作成を簡単に行っています。ページの例を見てわかるように、これだけの結果を出すのに数行のプログラムで実現していることに驚かされます。

http://www.otacky.jp/python.html
このページの”数値計算とプロット”というパートを見てください。

このままPythonに流れて気楽にプログラミングしていこうかなと思いましたが、Rubyでも似たようなことができないかいろいろ調べてみました。

ここでいろいろ試すのですが、Windows版gnuplotとの連携がなかなかうまくいかなくて、今のところ癖みたいなものを見つけてそれを利用して何とか連携しています。

まず下記のサイトを参考にしてgnuplotのWin32版をダウンロードしてインストールしておきます。このサイトではgnuplotについての使い方なども公開されています。インストール後、環境変数のPATHにpgnuplot.exeまでのパスを追加しておきます。pgnuplot.exeを利用して連携を行います。

http://t16web.lanl.gov/Kawano/gnuplot/

Pythonの例では拡張ライブラリGnuplotを取り込んでいます。Rubyでも拡張ライブラリRuby/Gnuplotがすでにあるので、これを使用してみましょう。

下記のサイトよりダウンロードし、作業用のデイレクトリに展開しておきます。gnuplot.rbが必要とするファイルです。よく使うようでしたら適宜、Rubyのlibフォルダ内の適当なフォルダに移動しておくとよいでしょう。

http://rubyforge.org/projects/rgplot

下記のサイトではRuby/Gnuplotについて紹介しています。サンプルプログラムが掲載されています。

http://rgplot.rubyforge.org/

このページより二つ目のサンプルをhowto2.rbとして保存します。require "Gnuplot"の一行をプログラムの先頭に追加しておきます。

require "Gnuplot"
 
Gnuplot.open do |gp|
  Gnuplot::Plot.new( gp ) do |plot|
  
    plot.title  "Array Plot Example"
    plot.ylabel "x"
    plot.xlabel "x^2"
    
    x = (0..50).collect { |v| v.to_f }
    y = x.collect { |v| v ** 2 }
 
    plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
      ds.with = "linespoints"
      ds.notitle
    end
  end
end

これをコマンドプロンプトから試してみます。

>ruby howto2.rb

ところが様々なエラーが発生します。これはライブラリGnuplotがUNIXをターゲットとしているためで、Windowsで動かすためには次のような工夫が必要です。

・gnuplotではなくpgnuplot.exeを利用する。
・Windows版ではpersistに対応していない。

また、そのほかに

・gnuplotのコマンドとしてpauseを使用して画面を一時的に表示させるようにした。
・pgnuplot.exeに対しRuby側からデータを出力する際にデータの終わりにeという文字と改行を出力しているが、改行が無視されることがある。

以上のことを踏まえて以下のようなラッパー用のプログラムを用意しました。ダウンロードする時はpGnuplot.rbを右クリックし”名前をつけて保存”を使って保存してください。

require "pGnuplot"
 
  def Gnuplot.gnuplot2( persist = true )
    cmd = which( ENV['RB_GNUPLOT'] || 'pgnuplot.exe' )
    cmd += " -persist" if persist
    cmd
  end
 
  def Gnuplot.open2( persist=false )
    cmd = Gnuplot.gnuplot2( persist ) or raise 'gnuplot not found'
    IO::popen( cmd, "w") { |io| yield io }
  end 
 
  def Gnuplot.pause(io = nil)
    io << "\n"
    io << "pause -1"
    io << "\n"
  end
 
  def Gnuplot.enter(io = nil)
    io << "\n"
  end

もとのプログラムhowto2.rbにこれを使ってプログラムを書きます。

require "pGnuplot"
 
Gnuplot.open2 do |gp|
  Gnuplot::Plot.new( gp ) do |plot|
  
    plot.title  "Array Plot Example"
    plot.ylabel "x"
    plot.xlabel "x^2"
    
    x = (0..50).collect { |v| v.to_f }
    y = x.collect { |v| v ** 2 }
    plot.data << Gnuplot::DataSet.new( [x, y] ) do |ds|
      ds.with = "linespoints"
      ds.linewidth = 4
    end
  end
  
  #Gnuplot.pause(gp)
  Gnuplot.enter(gp)
  sleep(5)
end

このプログラムを試してみてください。以下のようなグラフが描かれます。


requireでpGnuplotを読み込み、その中のGnuplot.open2やGnuplot.enterを利用しています。sleep(5)で5秒間、グラフを表示させます。Gnuplot.pauseを使ってダイアログウィンドウを表示させて、画面を表示させ続けることもできますが、画面上の文字が乱れることがあります。

そして最初に参考にしたPythonによるプログラム例をこれを使って書き直してみます。

require "pGnuplot"
 
Gnuplot.open2 do |gp|
  Gnuplot::Plot.new( gp ) do |plot|
  
    plot.title  "Array Plot Example"
    plot.ylabel "x"
    plot.xlabel "x^2"
    
    z = (0..16*4).collect { |v| [v.to_f/16.0, 
              Math.cos(2.0*Math::PI*1.5*v.to_f/16.0)] }
    plot.data << Gnuplot::DataSet.new( [z] ) do |ds|
      ds.with = "linespoints"
      ds.linewidth = 1
    end
  end
  
  Gnuplot.enter(gp)
  sleep(5)
end

このプログラムを試してみると以下のようなグラフが描かれます。


細かいgnuplotの設定に関してはgnuplotのマニュアルを読んで試してみてください。

RubyがUNIXをベースにしている以上、Windows版でデータの連携を取ることは難しい場合があります。しかし、なんとかごまかしながら使っていくことはできそうです。

これからRubyとpgnuplot.exeを連携させていろいろグラフを描いてみたいと考えています。