2014年7月26日土曜日

Windowsのgnuplotで、plot sin(x)をしてみた

Javaは知らないけど、便利なライブラリがあるようなので、 Clojureを触ってみようということでやってみました。

数値を得たら、プロットしてみるということはしばしばあります。 pythonだとmatplotlibを使ったりしますが、 gnuplotを呼び出すことができれば、 グラフ描画をgnuplotにまかせることもできそうです。

gnuplotは、gp463-win32-setup.exeを使ってインストールしたも のを利用しています。(ver.4.6.5でも大丈夫です) コマンドラインでgnuplotが実行できるように、インストールされたgnuplot.exeがあるディレクトリへあらかじめPathを通しておきます。

Clojureは、1.6.0をダウンロードしました。 Getting Startedにあるように、

java -cp clojure-1.6.0.jar clojure.main

で、REPLを起動して利用しています。

以下が、そのスクリプトですが、 プロセスを開始するgpstart、gnuplotのコマンドを送るgpdo、 終了するgpstopという関数を作っています。 sin(x)をプロットするのは、demo関数で行います。
(defrecord GnuplotProc [proc out in])

(defn gpstart
  "Start gnuplot process"
  []
  (let [proc (.start (doto (ProcessBuilder. '("gnuplot" "--persist"))
                       (.redirectErrorStream true)))
        out (clojure.java.io/writer (.getOutputStream proc))
        in (clojure.java.io/reader (.getInputStream proc))]
    (GnuplotProc. proc out in)))

(defn gpstop
  "Stop gnuplot process"
  [proc]
  (.destroy (get proc :proc)))

(defn gpdo
  "send command string to gnuplot process"
  [proc s]
  (let [w (get proc :out)]
       (.write w (str s))
       (.newLine w)
       (.flush w)))

(defn demo
  "Demo function"
  []
  (def p (gpstart))
  (gpdo p "set xlabel \"x\"")
  (gpdo p "set ylabel \"y\"")
  (gpdo p "plot sin(x)")
  (read)
  (gpstop p))

(demo)
clispと、pythonでも書いてみました。
(defun gpstart 
  ;Start gnuplot process
  ()
  (make-pipe-io-stream "gnuplot --persist" :buffered t))

(defun gpstop
  ;Stop gnuplot process
  (proc)
  (let ()
    (gpdo proc "quit")
    (close proc)))

(defun gpdo
  ;send command string to gnuplot process
  (proc s)
  (let ()
    (format proc "~A~%" s)
    (force-output proc)))

(defun demo
  ;demo function
  ()
  (let ((p (gpstart)))
    (gpdo p "set xlabel \"x\"")
    (gpdo p "set ylabel \"y\"")
    (gpdo p "plot sin(x)")
    (read)
    (gpstop p)))

(demo)

Pythonで書いたスクリプトは以下の通り。
import subprocess

def gpstart():
    """ Start gnuplot process """
    proc = subprocess.Popen(["gnuplot", "--persist"], 
                            stdin = subprocess.PIPE,
                            stdout = subprocess.PIPE,
                            stderr = subprocess.PIPE)
    return proc

def gpstop(proc):
    """ Stop gnuplot process """
    gpdo(proc, "quit")
    proc.kill()

def gpdo(proc, s):
    """ send command string to gnuplot process """
    proc.stdin.write(s)
    proc.stdin.write("\n")
    proc.stdin.flush()

def demo():
    """ Demo function """
    p = gpstart()
    gpdo(p, 'set xlabel "x"')
    gpdo(p, 'set ylabel "y"')
    gpdo(p, 'plot sin(x)')
    raw_input()
    gpstop(p)

if __name__ == '__main__':
    demo()
この程度なら、行数はほぼ一緒ですね。
clispやClojureを使ってもgnuplotを操作できそうなので、ひと安心です。

Rubyでもやってみました。
def gpstart
    # Start gnuplot process
    IO.popen("gnuplot --persist", "r+")
end

def gpstop(io)
    # Stop gnuplot process
    io.close
end

def gpdo(io, s)
    # send command string to gnuplot process
    io.puts(s)
end

def demo
    io = gpstart
    gpdo(io, "set xlabel \"x\"")
    gpdo(io, "set ylabel \"y\"")
    gpdo(io, "plot sin(x)")
    gets
    gpstop(io)
end

if __FILE__ == $0
    demo
end
シンプルですね。
Windows環境でirbを利用する人は少ないのかな。 ActiveScriptRuby 2.1.2-p95を利用してみたのですが、 irbのインタラクティブな環境で、Backspaceがちゃんと動作しませんでした。 検索してみたら、1.9.2での同様な症状と対処法がこちらにありました。 irbに--noreadlineオプションをつけて起動すると、正常にBackspaceできるようになりました。


2016/07/24追記
同じものをC言語で。Windows Vista 32bit MinGWのgcc-4.9.3で確認。

#include <stdio.h>

FILE *gpstart(void);
void gpstop(FILE *p);
void gpdo(FILE *p, char *s);

FILE *gpstart(void) {
    /* start gnuplot process */
    return popen("gnuplot.exe --persist", "w");
}

void gpstop(FILE *p) {
    /* stop gnuplot process */
    gpdo(p, "quit");
    pclose(p);
}

void gpdo(FILE *p, char *s) {
    /* send command string to gnuplot process */
    fprintf(p, s);
    fprintf(p, "\n");
}

void demo(void) {
    /* demo function */
    FILE *p;
    p = gpstart();
    gpdo(p, "set xlabel \"x\"");
    gpdo(p, "set ylabel \"y\"");
    gpdo(p, "plot sin(x)");
    gpstop(p);
}

void main(void) {
    demo();
}


0 件のコメント: