GoでIrisDatasetの散布図を作成する
この記事は Go4 Advent Calendar 2019 の4日目の記事です。
モチベーション
「機械学習と言えばPython、Pythonと言えば機械学習。」と言っても過言では無いくらいです。
沢山の書籍と、沢山のWeb上の記事、Kaggleでのノートやディスカッションの情報、沢山あります。
しかし、ライブラリは揃いきって無いかも知れませんが足りない物は自分で作りながら、Goで機械学習をしたいと思います。
とりあえずの、目標は、ゼロから作るディープラーニングをGoで実装して、Kaggleのチュートリアルをいくつか挑戦する事です。
今回はIrisのデータセットを散布図にしてみたいと思います。
今回のコードです:https://github.com/yujiteshima/plot_test
散布図を作る
使うデータ:https://gist.github.com/netj/8836201
がく片の長さと幅、花びらの長さと幅、アヤメの種類の名前という、5つの値が入ったデータが151個あります。
アヤメの種類は3種類です。このデータはとても有名なデータです。
まず、使うライブラリですが、gonum
というライブライを使います。
gonum
には、Pythonで言う、行列計算の提供するnumpy
、表や図の出力を行うmatplotlib
や、統計の機能を提供する、scipy
の機能を提供するライブラリです。あまり、使い方のサンプルも多く無いので、使いながら足らないところがあるのか等しらべて行きます。
ディレクトリ構成は以下のようにしました。
DRYでは無いので、今後直していきます。
.
├── go.mod
├── go.sum
├── iris.csv
├── main.go
├── main.go_bk
├── plot
│ ├── plotPetal.go
│ └── plotSepal.go
├── plotPetal.png
└── plotSepal.png
main.go
"github.com/yujiteshima/plot_test/plot"の部分はご自身の環境に合わせてモジュールのパスを入れて下さい。
相対パスではgo moduleを使っている際はgo run
go build
通りません。
参考:https://qiita.com/yujiteshima/items/8dc2f782f27f147a1e3e
package main
import (
"github.com/yujiteshima/plot_test/plot"
)
func main() {
plot.PlotSepal()
plot.PlotPetal()
}
mainでは、PlotSepalというがく片の幅と長さの散布図とPlotPetalという花びらの幅と長さの散布図の2つの散布図をpngファイルとして出力するだけの内容になっています。
PlotSepal.go
package plot
import (
"encoding/csv"
"image/color"
"os"
"strconv"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
)
func plotPointsSepal(x string) plotter.XYs {
pts := make(plotter.XYs, 150)
file, err := os.Open("./iris.csv")
if err != nil {
panic(err)
}
defer file.Close()
reader := csv.NewReader(file)
var line []string
for i := range pts {
line, err = reader.Read()
if err != nil {
panic(err)
}
if line[4] == x {
pts[i].X, _ = strconv.ParseFloat(line[0], 64)
pts[i].Y, _ = strconv.ParseFloat(line[1], 64)
}
}
return pts
}
func PlotSepal() {
// 図の生成
p, err := plot.New()
if err != nil {
panic(err)
}
//label
p.Title.Text = "Sepal"
p.X.Label.Text = "length"
p.Y.Label.Text = "width"
// 補助線
p.Add(plotter.NewGrid())
x1 := "Setosa"
x2 := "Versicolor"
x3 := "Virginica"
// 散布図の作成
plot1, err := plotter.NewScatter(plotPointsSepal(x1))
if err != nil {
panic(err)
}
plot2, err := plotter.NewScatter(plotPointsSepal(x2))
if err != nil {
panic(err)
}
plot3, err := plotter.NewScatter(plotPointsSepal(x3))
if err != nil {
panic(err)
}
//色を指定する.
plot1.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 55}
plot2.GlyphStyle.Color = color.RGBA{R: 155, B: 128, A: 255}
plot3.GlyphStyle.Color = color.RGBA{R: 55, B: 255, A: 128}
//plot1,plot2をplot
p.Add(plot1)
p.Add(plot2)
p.Add(plot3)
//label
p.Legend.Add("Seotsa", plot1)
p.Legend.Add("Versicolor", plot2)
p.Legend.Add("Virginica", plot3)
// 座標範囲
p.X.Min = 0
p.X.Max = 10
p.Y.Min = 0
p.Y.Max = 10
// plotSepal.pngに保存
if err := p.Save(6*vg.Inch, 6*vg.Inch, "plotSepal.png"); err != nil {
panic(err)
}
}
このように使いますが、今回CSVファイルをos.Open
で開き順番に読み込んで、if文で花の種類を分けてプロットしました。
goの外部ライブラリに、pythonでいうpandasと同じようにdataframeを扱える、その名もdataframeというライブラリがあります。
データのnull値を調べたり、型の違うデータが入り混じっていたりした時のデータハンドリングを簡単に行えるライブラリを用いていかないと他のデータでは使えないと感じました。次はdataframeを使ってgonum.plotを使ってみたいです。
出力した散布図
見ただけでアヤメの種類ががく片のデータと花びらのデータから分類が可能な感じがすると思います。
まとめ
がく片と花びらの散布図を作るのにほぼ同じ関数を二つ定義して使っているので、そこをDRYにしたい。また、データ数を150と自分が決めた外部パラメータのようにコードの中に入れてしまっているので、そこも直して、使い回しが出来る形に直す工夫をしていきたい。
次は、実際に分類をする部分を実装していきます。