Goでもpandasっぽいことできる！

# ことの発端
* バッチ処理とかで`DataFrame`でcsvファイルを処理したい
* `DataFrame`と言ったら`pandas`
* でもPythonは遅いので他の言語で扱えるものはないものか?

___

# そこでこいつ！
[GoのDataFrame](https://godoc.org/github.com/kniren/gota/dataframe)

* 使って人いたらどのように使っているか教えて欲しい
* とりあえず今回は使えそうなものを紹介します

___


# まずは`go get`

```
go get github.com/kniren/gota/dataframe
```

___

# DataFrameを作る
DataFrameの作り方は6つあります
使用できる型は`Int`, `Float`, `String`, `Bool`です

___

## DataFrameを作成する関数

|関数名|説明|型を指定する|
|:---:|:---:|:---:|
|New|列から作成する|できる|
|LoadRecords|行から作成する| できる |
|LoadStruct|Strauctから作成|  できる |
|LoadMaps| Mapから作成| できない |
|ReadCSV|CSVから作成| できない |
|ReadJson|Jsonから作成| できない |

型指定しなくても、ある程度自動で判定してくれます

`ReadJson`はあんまり使えないので紹介しません(詳しくはGitHubの[example](https://github.com/kniren/gota/blob/master/dataframe/examples_test.go)を)

___

## New(列から作成)
```go
df := dataframe.New(
		series.New([]string{"shimizu", "saito", "kondo"}, series.String, "Name"),
		series.New([]int{23, 23, 22}, series.Int, "Age"),
		series.New([]float64{23.4, 30.4, 22.7}, series.Float, "Power"),
)
```
若干面倒そうだがまぁまぁ

___

## LoadRecords(行から作成)
```go
users := [][]string{
	[]string{"Name", "Age", "Power"},
	[]string{"shimizu", "23", "23.4"},
	[]string{"saito", "23", "30.4"},
	[]string{"kondo", "22", "22.7"},
}

df := dataframe.LoadRecords(users,
		dataframe.WithTypes(map[string]series.Type{ 
			"Name":  series.String,
			"Age":   series.Int,
			"Power": series.Float,
		}))
df := dataframe.LoadRecords(users)
```
`dataframe.WithTypes`がなくてもDataFrameを作成してくれます(型は指定できませんが)

___

## LoadStruct(Structから作成)

```go
type User struct {
	Name  string
	Age   int
	Power float64
}

users := []User{
	User{"shimizu", 23, 23.4},
	User{"saito", 23, 30.4},
	User{"kondo", 22, 22.7},
}
df := dataframe.LoadStructs(users)
```
個人的におすすめ、おそらくファイルから読み取る方法以外ではこの方法が一番スマートな方法だと思います

___

## LoadMaps(Mapから作成)

```go
users := []map[string]interface{}{
	map[string]interface{}{
		"Name":  "shimizu",
		"Age":   23,
		"Power": 23.4,
	},
	map[string]interface{}{
		"Name":  "saito",
		"Age":   23,
		"Power": 30.4,
	},
	map[string]interface{}{
		"Name":  "kondo",
		"Age":   22,
		"Power": 22.7,
	},
}
df := dataframe.LoadMaps(users)
```
これはちょっとないかな

___

## ReadCSV(CSVファイルから作成)


```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)
```
TSVやスペース区切りもオプションを使用すれば読み込めます
___

## 出力はこんな感じ
```go
fmt.Println(df)
```
```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>
```
Goには標準でPrint関数がありますが、`fmt`パッケージのPrint関数でしか出力できないので注意

___

# データ操作

___

## Select(列を選択)

```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)

select := df.Select([]string{"Name", "Power"})
// select := df.Select([]int{0, 2}) indexでも指定できる
fmt.Println(select)
```
```
[3x2] DataFrame

    Name     Power
 0: shimizu  23.400000
 1: saito    30.400000
 2: kondo    22.700000
    <string> <float>
```

___

## SubSet(指定した行を取得)
```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)

row := df.Subset([]int{0}) // もちろん複数指定可能
fmt.Println(row)
```
```
[1x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
    <string> <int> <float>
```
___

## Mutate(列を追加)
```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)

mut := df.Mutate(
	series.New([]string{"blue", "red", "green"}, series.String, "Color"),
)

fmt.Println(mut)
```

___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[3x4] DataFrame

    Name     Age   Power     Color
 0: shimizu  23    23.400000 blue
 1: saito    23    30.400000 red
 2: kondo    22    22.700000 green
    <string> <int> <float>   <string>
```
列を追加する際、すでに存在するカラム名を指定すればカラムの更新になります

___

## RBind(行を結合)
```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)
fmt.Println(df)

df2 := dataframe.LoadRecords(
	[][]string{
		[]string{"Name", "Age", "Power"},
		[]string{"kojima", "22", "20.1"},
	},
)
df3 := df.RBind(df2)
fmt.Println(df3)
```
___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[4x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
 3: kojima   22    20.100000
    <string> <int> <float>
```
DataFrameの行を結合します。ただしカラム名とカラム数が一致していることが前提です

___

## CBind(列を結合)
```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)
fmt.Println(df)

df2 := dataframe.LoadRecords(
	[][]string{
		[]string{"From", "Language"},
		[]string{"nagano", "Python"},
		[]string{"kanagawa", "Java"},
		[]string{"kanagawa", "Kotlin"},
	},
)

df3 := df.CBind(df2)
fmt.Println(df3)
```

___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[3x5] DataFrame

    Name     Age   Power     From     Language
 0: shimizu  23    23.400000 nagano   Python
 1: saito    23    30.400000 kanagawa Java
 2: kondo    22    22.700000 kanagawa Kotlin
    <string> <int> <float>   <string> <string>
```
DataFrameの列を結合します。ただしレコード数が一致していることが前提です

___

## Set(データの更新)
```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)

seted := df.Set(
	series.Ints([]int{0, 2}), // indexが0と2を更新
	dataframe.LoadRecords( // DetaFrame型でカラムが一致しているならなんでもOK
		[][]string{
			[]string{"Name", "Age", "Power"},
			[]string{"hara", "25", "25.6"},
			[]string{"daisaku", "23", "24.0"},
		},
	),
)

fmt.Println(seted)
```

___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[3x3] DataFrame

    Name     Age   Power
 0: hara     25    25.600000
 1: saito    23    30.400000
 2: daisaku  23    24.000000
    <string> <int> <float>
```

___

## Arrange(ソート)
複数キー指定できる

```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)

sorted := df.Arrange(
	dataframe.RevSort("Power"), // Powerの降順
	dataframe.Sort("Age"), // Ageの昇順
)
fmt.Println(sorted)
```

___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[3x3] DataFrame

    Name     Age   Power
 0: saito    23    30.400000
 1: shimizu  23    23.400000
 2: kondo    22    22.700000
    <string> <int> <float>
```

___

## Filter(条件に一致した行を取得)
```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)

filter := df.Filter(
	dataframe.F{
		Colname:    "Age",
		Comparator: series.LessEq, // "<="でもok
		Comparando: 22,
	},
)
// filter := df.Filter(dataframe.F{"Age", "<=", 22}) // こんな感じもok
fmt.Println(filter)
```
```
[1x3] DataFrame

    Name     Age   Power
 0: kondo    22    22.700000
    <string> <int> <float>
```

___

### dataframe.F

* Colname: カラム名
* Comparator: 比較
* Comparando: 比較値  
省略も可能(私はvscodeで書いて、省略をすると警告が出ます)

|比較|比較演算子|
|:---:|:---:|
|series.Eq|==|
|series.Neq|!=|
|series.Greater|>|
|series.GreaterEq| >=|
|series.Less|<|
|series.LessEq|<=|

___

### **<font color="Red">注意点</font>**
`Filter`内に`dataframe.F`は何個でも条件として指定できるが`and`ではなく`or`なので注意

```go
filter := df.Filter(
		dataframe.F{"Age", ">", 22},
		dataframe.F{"Power", ">", 22.0},
)
fmt.Println(filter)
```
```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>
```

___

### andしたい

`and`は現状このようにやるしかない

```go
filter := df.Filter(dataframe.F{"Age", ">", 22}).
		Filter(dataframe.F{"Power", ">=", 22.0})

fmt.Println(filter)
```
```
[2x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
    <string> <int> <float>
```
面倒臭い!(時間があればプルリクを投げたい)。

___

## InnerJoin(内部結合)

```go
content, _ := ioutil.ReadFile("user.csv")
ioContent := strings.NewReader(string(content))

df := dataframe.ReadCSV(ioContent)
fmt.Println(df)

df2 := dataframe.LoadRecords(
	[][]string{
		[]string{"Name", "From"},
		[]string{"shimizu", "Nagano"},
		[]string{"saito", "Yokohama"},
		[]string{"kojima", "Aichi"},
	},
)
fmt.Println(df2)

innerJoin := df.InnerJoin(df2, "Name") // NameでJoin
fmt.Println(innerJoin)
```

___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[3x2] DataFrame

    Name     From
 0: shimizu  Nagano
 1: saito    Yokohama
 2: kojima   Aichi
    <string> <string>

[2x4] DataFrame

    Name     Age   Power     From
 0: shimizu  23    23.400000 Nagano
 1: saito    23    30.400000 Yokohama
    <string> <int> <float>   <string>
```
割と簡単

___

## LeftJoin(外部結合)
とりあえず`LeftJoin`だけ紹介
もちろん`OuterJoin`と`RightJoin`もあります

```go
// -------------
// InnerJoinと同じ
// -------------

leftJoin := df.LeftJoin(df2, "Name")
fmt.Println(leftJoin)
```

___

```
[3x3] DataFrame

    Name     Age   Power
 0: shimizu  23    23.400000
 1: saito    23    30.400000
 2: kondo    22    22.700000
    <string> <int> <float>

[3x2] DataFrame

    Name     From
 0: shimizu  Nagano
 1: saito    Yokohama
 2: kojima   Aichi
    <string> <string>

[3x4] DataFrame

    Name     Age   Power     From
 0: shimizu  23    23.400000 Nagano
 1: saito    23    30.400000 Yokohama
 2: kondo    22    22.700000 NaN   // nilではないので注意
    <string> <int> <float>   <string>
```

___

# 番外 DataFrameを扱えるライブラリ

* pandas(Python): https://pandas.pydata.org/ 
* daru(Ruby): https://github.com/SciRuby/daru
* krangl(Ktolin): https://github.com/holgerbrandl/krangl
* R: https://www.rdocumentation.org/packages/base/versions/3.5.1/topics/data.frame
* PHP: https://packagist.org/packages/archon/dataframe
* Spark(Python, R, Java, Scala): https://spark.apache.org/docs/latest/sql-programming-guide.html

他にもあるなら教えて欲しい!

[ハンズラボ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handslab) 明日2日目は @watarukura です！