🙆‍♀️

Gonum Plotを使ってGoでプロットを作ってみる

に公開

今回はGoでプロットグラフを作れるGonum Plotを使ってみようと思います。

Gonum Plotとは?

Gonum Plotはcode.google.com/p/plotinumの新しい公式フォークとのことです。Goを利用してプロットのビルド・描画ができるAPIを提供しており、現時点ではまだ変動しており仕様が変更される可能性があるとのことです。詳しくは以下の公式ドキュメントを参照ください。

https://pkg.go.dev/gonum.org/v1/plot#section-readme

実際に使ってみる

インストール

まず検証環境を作成します。今回はmiseを使って環境を作成します。miseについてはこちらの記事で簡単ではありますが触れておりますので、ぜひご覧ください。

https://zenn.dev/akasan/articles/2aa8031a03871c

まず、Goのバージョンは1.24.3を利用します。私の環境ではすでにインストール済みですので、以下のようにして作業環境を作成しました。

mkdir gonum_plot_tutorial
cd gonum_plot_tutorial
mise use go@1.24.3
go mod init gonum_plot_tutorial

次にGonum Plotをインストールします。公式ドキュメントにある通り、以下のコマンドでインストールできます。

go get gonum.org/v1/plot/...

一つの折れ線グラフを表示する

一つの折れ線グラフを表示するには以下のようなコードで実現できます。

one_line_plot.go
package main

import (
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/plotutil"
	"gonum.org/v1/plot/vg"
	"math/rand"
)

func main() {
	p := plot.New()
	p.Title.Text = "One Line Plot"
	p.X.Label.Text = "X"
	p.Y.Label.Text = "Y"

	pts := make(plotter.XYs, 10)

	for i := range pts {
		pts[i].X = float64(i)
		pts[i].Y = rand.Float64()
	}

	plotutil.AddLinePoints(p, pts)
	if err := p.Save(4*vg.Inch, 4*vg.Inch, "one_line_plot.png"); err != nil {
		panic(err)
	}
}

まず必要なライブラリをインポートします。プロット座標を決めるためにmath/randを利用します。また、プロットの作成に必要なものはgonum~を指定します。

次に、プロットを新規作成します。以下ではプロットの作成からタイトル、軸ラベルを設定しています。

p := plot.New()
p.Title.Text = "One Line Plot"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"

そして、プロットする座標についてはplotter.XYsを利用して作成します。ptsを格納する変数を定義した後に、Y座標を乱数で指定するように書いています。

pts := make(plotter.XYs, 10)

for i := range pts {
    pts[i].X = float64(i)
    pts[i].Y = rand.Float64()
}

最後に、プロットにデータを追加して、one_line_plot.pngというファイル名で画像として保存しています。

plotutil.AddLinePoints(p, pts)
if err := p.Save(4*vg.Inch, 4*vg.Inch, "one_line_plot.png"); err != nil {
    panic(err)
}

これを実行すると以下のような画像が生成されました。

複数ラインのプロット

次は複数ラインをプロットしてみましょう。先ほどのコードを次のように変更しました。

multi_line_plot.go
package main

import (
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/plotutil"
	"gonum.org/v1/plot/vg"
	"math/rand"
)

func main() {
	p := plot.New()
	p.Title.Text = "Multi Line Plot"
	p.X.Label.Text = "X"
	p.Y.Label.Text = "Y"

	pts1 := generatePts(10)
	pts2 := generatePts(10)
	pts3 := generatePts(10)

	plotutil.AddLinePoints(p, "First", pts1, "Second", pts2, "Third", pts3)
	if err := p.Save(4*vg.Inch, 4*vg.Inch, "multi_line_plot.png"); err != nil {
		panic(err)
	}
}

func generatePts(pointNum int) plotter.XYs {
	pts := make(plotter.XYs, pointNum)
	for i := range pts {
		pts[i].X = float64(i)
		pts[i].Y = rand.Float64()
	}
	return pts
}

今回は以下のようにしてプロットするための関数を別途定義しています。

func generatePts(pointNum int) plotter.XYs {
	pts := make(plotter.XYs, pointNum)
	for i := range pts {
		pts[i].X = float64(i)
		pts[i].Y = rand.Float64()
	}
	return pts
}

そして、プロットの追加ですが、プロットに対してラベルもつけております。以下のようにしてラベルを追加するとグラフ化したときにラベルが表示されます。

pts1 := generatePts(10)
pts2 := generatePts(10)
pts3 := generatePts(10)

plotutil.AddLinePoints(p, "First", pts1, "Second", pts2, "Third", pts3)

これを実行すると以下のようなグラフが表示されます。

ちなみに、Firstなどのラベルをとると以下のようになります。

散布図の表示

最後に散布図も表示してみましょう。以下のようにすると散布図を表示できます。

scatter_plot.go
package main

import (
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/vg"
	"image/color"
	"math/rand"
)

func main() {
	p := plot.New()
	p.Title.Text = "Scatter Plot"
	p.X.Label.Text = "X"
	p.Y.Label.Text = "Y"

	pts := make(plotter.XYs, 10)

	for i := range pts {
		pts[i].X = float64(i)
		pts[i].Y = rand.Float64()
	}
	s, err := plotter.NewScatter(pts)
	if err != nil {
		panic(err)
	}
	s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255}
	p.Add(s)
	if err := p.Save(4*vg.Inch, 4*vg.Inch, "scatter_plot.png"); err != nil {
		panic(err)
	}
}

散布図を作成するにはplotter.NewScatterを利用します。コンストラクタに点群を指定することで散布図が追加できます。また、以下のようにすることでプロットの色を変更できます。

s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255}

散布図を作成した後は、オリジナルのプロットに追加します。

p.Add(s)

後は実行すると以下のように散布図が作成できます。

複数種類の同時描画

先ほどの例を応用して、折れ線グラフと散布図を同時に表示することができます。

muilti_line_scatter_plot.go
package main

import (
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/plotutil"
	"gonum.org/v1/plot/vg"
	"image/color"
	"math/rand"
)

func main() {
	p := plot.New()
	p.Title.Text = "Multi Line Plot"
	p.X.Label.Text = "X"
	p.Y.Label.Text = "Y"

	pts1 := generatePts(10)
	pts2 := generatePts(10)
	pts3 := generatePts(10)

	plotutil.AddLinePoints(p, "First", pts1, "Second", pts2, "Third", pts3)

	scatterPts := generatePts(10)
	s, err := plotter.NewScatter(scatterPts)
	if err != nil {
		panic(err)
	}
	s.GlyphStyle.Color = color.RGBA{R: 0, B: 0, A: 255}
	p.Legend.Add("Scatter", s)
	p.Add(s)

	if err := p.Save(4*vg.Inch, 4*vg.Inch, "multi_line_scatter_plot.png"); err != nil {
		panic(err)
	}
}

func generatePts(pointNum int) plotter.XYs {
	pts := make(plotter.XYs, pointNum)
	for i := range pts {
		pts[i].X = float64(i)
		pts[i].Y = rand.Float64()
	}
	return pts
}

実行すると以下のような結果を得られます。

まとめ

今回はGonum Plotを使ってGoでグラフを作ってみました。MLエンジニアはPythonを使ったプロットをすることが基本的に多いかと思いますが、Goを使って今後開発をやってみようと思っているところもあり、今回試してみました。ぜひご興味があればドキュメントなどをみつつご自身でも試してもらえたらと思います。

Discussion