2022-12-29に更新

Ruby + ruby_gnuplot(gnuplot gem)で散布図を描く

https://rubygems.org/gems/gnuplot

https://github.com/rdp/ruby_gnuplot

準備

  • gnuplot コマンドをインストール
    • Ubuntu 18.04 の場合は sudo apt install gnuplot
  • ruby_gnuplot をインストール
    • gem install gnuplot

これだけ。

ただし、 Ruby 3.1.0 で matrix が bundled gem になったようなので、3.1.0 以降の場合は gem install matrix も必要です。
参考: [Ruby] Bundled gemsはGemfileに指定して使おう

最低限の例

PNG ファイルに出力する最低限の例です。

# sample.rb

require "gnuplot"

Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "png"
    plot.output "output.png"

    x = [1, 2, 3, 4, 5]
    y = [11, 22, 33, 44, 55]
    plot.data << Gnuplot::DataSet.new([x, y])
  end
end
  # 実行 ... output.png が生成される
ruby sample.rb

image

以上。簡単ですね。
ちょっと見えにくいですが、 両端の点は枠線に重なっています。

PNG 以外にも、 SVG や PDF として出力することもできます。

環境

Ubuntu Linux 18.04
gnuplot -V #=> gnuplot 5.2 patchlevel 2
ruby -v    #=> ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
gem:
  gnuplot 2.6.2
  matrix 0.4.2

メモ

  • とにかくインストールが楽なのが良い。gnuplot コマンドのインストールさえ済んでしまえば、他にインストール絡みでトラブルが起きる余地がなさそう。気分的に楽 👌
  • gnuplot でできることは大体何でもできるはず。たぶん。
  • gnuplot.rb ( https://github.com/rdp/ruby_gnuplot/blob/master/lib/gnuplot.rb ) はコメントを除くと300行くらいなので、サッと読める
    • gnuplot 用のスクリプトに変換して gnuplot コマンドに渡しているだけ
  • 「○○がやりたいけどどうすればいい?」となった場合は gnuplot 向けの情報を探す
    • gnuplot は枯れたプロダクトなので、情報はたくさんある
    • すでに gnuplot に習熟している人は楽そう
  • Charty のバックエンドとして使える?(まだ試していません)
    • (追記) 2022-05-29 現在では未対応でした

非常に薄いラッパーなので、素の gnuplot をそのまま使うのと実はそんなに大差なくて、そこを踏まえた上で使う分には便利。
「gnuplot の使い方を覚えたいわけではないんだけどなあ……」という人には向かないかもしれません。

自分が使いそうな機能についてメモ

  • グラフのタイトル指定
  • x軸, y軸の範囲指定、軸タイトル指定
  • 複数の系列の描画
  • 数式のプロット

などは README にサンプルがあるので、そっちを見てください。

系列名の凡例

DataSet.new にブロックを付けると、ブロックパラメータとして DataSet のインスタンスが渡されるので、それを使って指定できる。

plot.data << Gnuplot::DataSet.new([x, y]) { |ds|
  ds.title = "dataset"
}

image

日本語フォント

無指定だと日本語が豆腐になりました。
gnuplot フォント 日本語 set terminal のような検索ワードで調べると、 Plot#terminal で指定できることが分かります。

Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "png font 'VL Gothic,20'"
    plot.output "output.png"

    x = [1, 2, 3]
    y1 = [11, 25, 32]

    plot.data << Gnuplot::DataSet.new([x, y1]) { |ds|
      ds.title = "系列1"
    }
  end
end

image

点の代わりにラベルを表示する

Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "png"
    plot.output "output.png"

    x = [1, 2, 3]
    y = [11, 22, 33]
    label = ["A", "B", "C"]
    plot.data << Gnuplot::DataSet.new([x, y, label]) { |ds|
      ds.with = "labels"
    }
  end
end

image


点とラベルを両方表示するのは若干面倒そう。

In gnuplot, how to label each point in the plot with its coordinates? - Stack Overflow
https://stackoverflow.com/questions/23177716/in-gnuplot-how-to-label-each-point-in-the-plot-with-its-coordinates

生成されたスクリプトだけ欲しい

以下のように Gnuplot.open にモンキーパッチを当てると、グラフの描画は行わずに gnuplot 用に生成されたスクリプトの出力だけを行うことができます。

module Gnuplot
  def self.open(persist = true)
    io = $stdout
    yield io
  end
end

Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    x = [1, 2, 3]
    y = [11, 22, 33]
    plot.data << Gnuplot::DataSet.new([x, y])
  end
end
$ ruby sample_print_script.rb > sample.gs

$ cat sample.gs
plot '-'
1 11
2 22
3 33
e

  # スクリプトを gnuplot コマンドに渡して実行
$ gnuplot -persist sample.gs

まとめたもの

せっかくなのでいろいろ含めたものも貼ってみます。

require "gnuplot"

Gnuplot.open do |gp|
  Gnuplot::Plot.new(gp) do |plot|
    plot.terminal "png font 'Noto Sans CJK JP,12'"
    plot.output "output.png"
    plot.title "グラフのタイトル"
    plot.xrange "[0:6]"
    plot.yrange "[0:60]"
    plot.xlabel "x軸のラベル"
    plot.ylabel "y軸のラベル"

    x = [1, 2, 3]
    y1 = [11, 25, 32]
    y2 = [21, 35, 42]
    y3 = [31, 45, 52]

    plot.data << Gnuplot::DataSet.new([x, y1]) { |ds|
      ds.title = "系列1"
    }

    plot.data << Gnuplot::DataSet.new([x, y2]) { |ds|
      ds.title = "系列2(線のみ)"
      ds.with = "lines"
    }

    plot.data << Gnuplot::DataSet.new([x, y3]) { |ds|
      ds.title = "系列3(点と線)"
      ds.with = "linespoints"
    }

    # 数式
    plot.data << Gnuplot::DataSet.new("x * 10") { |ds|
      ds.title = "y = x * 10"
    }
  end
end

image

ds.with = "lines" を使うか、または数式を使えば任意の直線が描画できるので、回帰直線もこれでいけますね(フィッティングの話については割愛)。

参考

Rubyのgnuplotで時系列データをプロット | Lifelog
https://ytkyk.info/blog/2016/07/21/rubyのgnuplotで時系列データをプロット/


gnuplot documentation
http://gnuplot.info/documentation.html

日本語のPDFがあります。


Demos for gnuplot version 5.4
http://gnuplot.sourceforge.net/demo_5.4/

豊富なサンプル。gnuplot マジ何でもできるな、という気持ちになります。

この記事を読んだ人は(ひょっとしたら)こちらも読んでいます

Originally published at qiita.com
ツイッターでシェア
みんなに共有、忘れないようにメモ

sonota486

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント