【vgplot】
Common Lispでグラフを描く
(gnuplotのフロントエンド)

  • Common Lispからgnuplotをラップするvgplotライブラリを使うと、REPLから手軽にグラフを描ける。
  • インストールはHomebrewでgnuplotを入れ、Quicklispからvgplotをロードするだけで完了する。
  • plotで折れ線グラフ、surfで3Dグラフを描け、rangeでデータ生成から描画までをLispだけで完結できる。
  • タイトル・軸ラベル・対数スケール・複数ウィンドウなど、MATLABライクなAPIで一通りの可視化操作に対応している。

quicklispライブラリをロードしたら、すぐに使えました。
以前に、自分でgnuplotのコードを生成したやり方より、よっぽどかんたんです。

関連記事

1. gnuplotとvgplotのインストール

数値計算をしていると、途中経過や結果をグラフで確認したくなる場面が必ずあります。
PythonならMatplotlibがありますが、Common Lispにも同じような使い心地のライブラリがあります。
vgplot です。

1. gnuplotとvgplotのインストール

vgplot はバックエンドに gnuplot を使い、Common Lisp 側から Octave や MATLAB に似た API でグラフを操作できます。
REPLでインタラクティブに試しながら作業できるのが特徴です。

gnuplot と vgplot のインストール gnuplot Terminal brew install gnuplot Homebrew でインストール vgplot Quicklisp (ql:quickload :vgplot) Quicklisp で取得 依存ライブラリ(ltk, cl-fad, cl-ppcre)も自動ダウンロード (vgplot:demo) で動作確認できる

gnuplot がシステムにインストールされている必要があります。
macOS なら Homebrew で入ります1

brew install gnuplotCode language: Bash (bash)

vgplot 自体は Quicklisp から取得します2
SBCLのREPLで読み込みます。

(ql:quickload :vgplot)Code language: Lisp (lisp)

初回実行時に ltk3・cl-fad4・cl-ppcre5 といった依存ライブラリもあわせてダウンロードされます。

1.1. 最初のグラフ

ロードが終わったらすぐ試せます。

(vgplot:plot '(1 2 3) '(0 -2 17))Code language: Lisp (lisp)

第一引数が x 座標のリスト、第二引数が y 座標のリストです。
これだけで gnuplot のウィンドウが開き、折れ線グラフが表示されます6

また、ライブラリ自体にデモが含まれています。
動作確認を兼ねて最初に実行してみると、主要な機能をひと通り確認できます。

(vgplot:demo)Code language: Lisp (lisp)

ENTER キーで次のデモに進み、他のキーで終了します。

2. 関数のグラフを描く(plot)

sin 曲線を描いてみましょう。

関数のグラフを描く sin 1 range で x を生成 (vgplot:range 0.0 (* 2 pi) 0.05) 2 map で y を計算 (map ‘vector #’sin x) 3 plot に渡す (vgplot:plot x y “b;sin;”)

vgplot:plotでは、x, y 2つのベクタを生成して与えることでグラフにして、gnuplotで描画します。

(let* ((x (vgplot:range 0.0 (* 2 pi) 0.05))
       (y (map 'vector #'sin x)))
  (vgplot:plot x y "sin(x)"))Code language: Lisp (lisp)
2. 関数のグラフを描く(plot)

xの定義域は vgplot:range で、simple-vectorの配列を生成しています。
Python の numpy.arange に相当します7

これに、map で x の各要素に sin を適用して y ベクタを作り、vgplot:plotにそのまま渡しています。
第三引数の文字列がラベルになります。

2.1. range で範囲配列を作る

範囲と刻みを指定できる vgplot:range が用意されています。

;; 0 から 9 までの整数ベクタ
(vgplot:range 10)
; => #(0 1 2 3 4 5 6 7 8 9)

;; 1 から 5 未満
(vgplot:range 1 5)
; => #(1 2 3 4)

;; 0.0 から 2π を 0.1 刻みで
(vgplot:range 0.0 (* 2 pi) 0.1)Code language: Lisp (lisp)

vgplot:plotに引数を続けて並べると、複数の曲線を重ねられます。

(let* ((x (vgplot:range 0.0 (* 2 pi) 0.05))
       (s (map 'vector #'sin x))
       (c (map 'vector #'cos x)))
  (vgplot:plot x s "b;sin;" x c "r;cos;"))Code language: Lisp (lisp)

ラベル文字列の書式は "スタイル;凡例テキスト;" です。
b が青、r が赤を指定していて、他にも g で緑、k で黒、- で実線、. で点、+ でマーカーを組み合わせられます8

2.2. グラフをファイルに保存する(print-plot)

(vgplot:print-plot #p"output.png")
(vgplot:print-plot #p"output.svg")Code language: Lisp (lisp)

拡張子から出力形式を自動判別します。
EPS など明示したいときは :terminal キーワードで指定できます9

(vgplot:print-plot #p"output.eps" :terminal "epscairo")Code language: Lisp (lisp)

2.3. 3D プロット(vgplot:surf)

曲面を描くには meshgrid-xmeshgrid-ymeshgrid-map で格子点データを作り、surf に渡します。

(let* ((x (vgplot:range -3.0 3.0 0.2))
       (y (vgplot:range -3.0 3.0 0.2))
       (xx (vgplot:meshgrid-x x y))
       (yy (vgplot:meshgrid-y x y))
       (zz (vgplot:meshgrid-map
             (lambda (xi yi) (+ (* xi xi) (* yi yi)))
             xx yy)))
  (vgplot:surf xx yy zz))Code language: Lisp (lisp)
2.3. 3D プロット(vgplot:surf)

meshgrid-x は x の値を列方向に並べた 2D 配列を返し、meshgrid-y は y の値を行方向に並べた 2D 配列を返します。
meshgrid-map はその各要素のペアに関数を適用して z 値の配列を作ります。

3. タイトルと軸ラベル

タイトルと軸ラベル・表示設定 タイトル xlabel ylabel title xlabel ylabel grid t / nil semilogx / loglog subplot 2 1 0 subplot 2 1 1 複数パネル format-plot で gnuplot コマンドを直接送信できる
(vgplot:title "sin と cos の比較")
(vgplot:xlabel "x [rad]")
(vgplot:ylabel "振幅")Code language: Lisp (lisp)

これらを呼ぶと自動的に replot が走り、ウィンドウが更新されます。
グリッドの表示と非表示は grid で切り替えます。

(vgplot:grid t)   ; 表示
(vgplot:grid nil) ; 非表示Code language: Lisp (lisp)

3.1. 対数スケール

両対数・片対数のグラフも関数一つで切り替わります。

;; x 軸だけ対数
(vgplot:semilogx x y)

;; y 軸だけ対数
(vgplot:semilogy x y)

;; 両軸とも対数
(vgplot:loglog x y)Code language: Lisp (lisp)

3.2. 複数ウィンドウとサブプロット

新しいウィンドウを開くには new-plot を呼びます。
figurenew-plot の別名で、同じ動作をします。

(vgplot:plot x s "sin")
(vgplot:new-plot)
(vgplot:plot x c "cos")Code language: Lisp (lisp)

一つのウィンドウに複数のパネルを並べるなら subplot を使います。

(vgplot:subplot 2 1 0)  ; 2行1列の上のパネル
(vgplot:plot x s "sin")

(vgplot:subplot 2 1 1)  ; 2行1列の下のパネル
(vgplot:plot x c "cos")Code language: Lisp (lisp)

インデックスは左上から行方向に 0 始まりで振られます10

4. まとめ

vgplot の基本的な流れは、range でデータを作り、plotsurf で描き、titlexlabel などで見た目を整えるというサイクルです。
gnuplot に直接コマンドを送りたいときは format-plot を使えば任意のオプションも設定できます11
REPLとの相性がよく、試行錯誤しながら数学の可視化を進めるのに向いています。

  1. gnuplot は1986年から開発が続くグラフ描画ツールで、現在の安定版は 6.0.x です。名前に “gnu” が含まれますが GNU プロジェクトとは無関係で、独自ライセンスのもとソースコードは自由に配布できます。 – gnuplot homepage
  2. Quicklisp は Common Lisp 向けのライブラリマネージャで、1,500以上のライブラリをコマンド一つでダウンロード・ロードできます。SBCL・CLISP・Clozure CL など主要な処理系に対応しています。 – Quicklisp beta
  3. ltk は Tcl/Tk の Common Lisp バインディングです。vgplot はグラフウィンドウを表示するためにこのライブラリを利用しています。 – LTK – The Lisp Toolkit
  4. cl-fad(Files and Directories)は Common Lisp のパス操作を各 OS 間で統一するユーティリティライブラリです。vgplot が一時ファイルを管理するために使用しています。 – cl-fad – Portable pathname library for Common Lisp
  5. cl-ppcre は Perl 互換の正規表現ライブラリで、BSD ライセンスのもと公開されています。vgplot はラベル文字列のパースに内部で使用しています。 – CL-PPCRE – Portable Perl-compatible regular expressions for Common Lisp
  6. vgplot のソースコードと詳細なサンプルは GitHub で公開されています。GPL ライセンスのもとで配布されています。 – vgplot – GitHub
  7. numpy.arange と同様に終端値を含まない半開区間で動作します。ただし戻り値は numpy の ndarray ではなく Common Lisp の simple-vector です。 – The vgplot Reference Manual
  8. スタイル文字列の第3フィールド(add-styles)に gnuplot の with 構文をそのまま書くこともできます。例えば ";カスタム;with linespoints pt 7 ps 2 lc 'red'" のように指定すると、他のスタイル指定を無視してこの文字列がそのまま gnuplot へ渡されます。 – vgplot API documentation
  9. gnuplot が対応する出力形式(terminal)は gif・pdf・png のほか、eps・svg・LaTeX・metafont など多数あります。print-plot でサイズ指定もでき、例えば :terminal "pdfcairo size \"5cm\",\"5cm\"" のようにサイズを文字列に含める書き方も使えます。 – vgplot Reference Manual
  10. gnuplot の multiplot モード中はマウスによるインタラクティブ操作が無効になります。これは vgplot の制約ではなく gnuplot 本体の仕様で、subplot 使用時は注意が必要です。 – vgplot Reference Manual
  11. format-plot の第一引数は print? フラグで、t を渡すと gnuplot のレスポンスを標準出力にも表示します。3D プロットで set hidden3dset pm3d を有効にする場合など、vgplot の関数に対応がない設定を行うときに活用できます。 – vgplot GitHub