GoでIrisDataSetの散布図を描く

# GoでIrisDatasetの散布図を作成する
この記事は [Go4 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/go4) の4日目の記事です。

## モチベーション
「機械学習と言えばPython、Pythonと言えば機械学習。」と言っても過言では無いくらいです。
沢山の書籍と、沢山のWeb上の記事、Kaggleでのノートやディスカッションの情報、沢山あります。
しかし、ライブラリは揃いきって無いかも知れませんが足りない物は自分で作りながら、Goで機械学習をしたいと思います。
とりあえずの、目標は、ゼロから作るディープラーニングをGoで実装して、Kaggleのチュートリアルをいくつか挑戦する事です。
今回はIrisのデータセットを散布図にしてみたいと思います。
[今回のコードです](https://github.com/yujiteshima/plot_test)：https://github.com/yujiteshima/plot_test
## 散布図を作る
使うデータ：https://gist.github.com/netj/8836201
がく片の長さと幅、花びらの長さと幅、アヤメの種類の名前という、５つの値が入ったデータが１５１個あります。
アヤメの種類は３種類です。このデータはとても有名なデータです。

まず、使うライブラリですが、`gonum`というライブライを使います。
`gonum`には、Pythonで言う、行列計算の提供する`numpy`、表や図の出力を行う`matplotlib`や、統計の機能を提供する、`scipy`の機能を提供するライブラリです。あまり、使い方のサンプルも多く無いので、使いながら足らないところがあるのか等しらべて行きます。

ディレクトリ構成は以下のようにしました。
DRYでは無いので、今後直していきます。

```bash
.
├── 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

```go:main.go
package main

import (
	"github.com/yujiteshima/plot_test/plot"
)

func main() {
	plot.PlotSepal()
	plot.PlotPetal()
}
```
mainでは、PlotSepalというがく片の幅と長さの散布図とPlotPetalという花びらの幅と長さの散布図の２つの散布図をpngファイルとして出力するだけの内容になっています。

### PlotSepal.go

```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を使ってみたいです。

## 出力した散布図

![plotSepal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252625/fd594249-f4fe-0714-f0d0-6f8286ec8d8f.png)
![plotPetal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252625/bbf54b64-23f9-1511-42c8-43ce989f5c58.png)

見ただけでアヤメの種類ががく片のデータと花びらのデータから分類が可能な感じがすると思います。

## まとめ
がく片と花びらの散布図を作るのにほぼ同じ関数を二つ定義して使っているので、そこをDRYにしたい。また、データ数を150と自分が決めた外部パラメータのようにコードの中に入れてしまっているので、そこも直して、使い回しが出来る形に直す工夫をしていきたい。
次は、実際に分類をする部分を実装していきます。