Golangのデータフレームライブラリ QFrameを扱っています。
高速でメソッドチェーンができるのが気に入っているのですが、込み入ったことをしようとすると複雑なコードを書かなければならなくなり、割と大変でした。
この記事では2つのデータフレームの結合を行いたい場合のなんとかやってみたコードを書きました。
サンプルデータのtestpidとteststockを左結合したいです。
要するに、teststockに品名と型式の情報をtestpidから持ってきたいのです。
package main
import (
"fmt"
"github.com/tobgu/qframe"
)
var (
testpid = qframe.New(map[string]interface{}{
"品番": []string{"AAA-100", "AAA-200", "AAA-300", "BBB-100", "BBB-210"},
"品名": []string{"製品1", "製品2", "製品3", "製品4", "製品5"},
"型式": []string{"型式A", "型式B", "型式C", "型式D", "型式E"},
})
teststock = qframe.New(map[string]interface{}{
"品番": []string{"AAA-100", "AAA-200", "BBB-210"},
"在庫数": []float64{1, 2, 3},
"在庫単価": []float64{100, 1000, 10000},
})
)
type (
// Parts : 品番マスタに登録されている
// 品名と型式情報を品番で管理する
Parts struct{ Name, Type string }
// PartsMap : 一意な品番をキーにした品名と型式のマップ
// PartsMap[品番] で品名と型式が返される。
PartsMap map[string]Parts
)
// convertToNonPointerSlice : []*string -> []string
func convertToNonPointerSlice(ptrSlice []*string) []string {
nonPtrSlice := make([]string, len(ptrSlice))
for i, ptr := range ptrSlice {
nonPtrSlice[i] = *ptr
}
return nonPtrSlice
}
func main() {
// 品番をキーにしたのマップを作る
pmap := make(PartsMap, testpid.Len())
pids := testpid.MustStringView("品番")
names := testpid.MustStringView("品名")
types := testpid.MustStringView("型式")
for i := 0; i < pids.Len(); i++ {
// ItemAtが返すのはポインタであることに注意
pid := pids.ItemAt(i)
pmap[*pid] = Parts{*names.ItemAt(i), *types.ItemAt(i)}
}
// 品番に紐づいた品名、型式を追加
ss := teststock.MustStringView("品番").Slice()
// stockPidsの型が[]*stringであることに注意
// Slice()はポインタのスライスを返す。
stockPids := convertToNonPointerSlice(ss)
// QFrameを作るためのmapを作成
newData := map[string]interface{}{"品番": stockPids}
// 品番をキーにしたマップから品名と型式を引く
stockNames := make([]string, len(stockPids))
stockTypes := make([]string, len(stockPids))
// 品名と型式を在庫品番と同じ長さの配列に入れる
for i, pid := range stockPids {
stockNames[i] = pmap[pid].Name
stockTypes[i] = pmap[pid].Type
}
// newDataへ品名, 型式のスライスを追加
newData["品名"] = stockNames
newData["型式"] = stockTypes
// newDataへ在庫情報を追加
for _, name := range []string{"在庫数", "在庫単価"} {
f := teststock.MustFloatView(name)
newData[name] = f.Slice()
}
qf := qframe.New(newData)
fmt.Println(qf)
/*
品名(s) 品番(s) 在庫単価(f) 在庫数(f) 型式(s)
--------- --------- --------------- ------------ ---------
製品1 AAA-100 100 1 型式A
製品2 AAA-200 1000 2 型式B
製品5 BBB-210 10000 3 型式E
Dims = 5 x 3
*/
}
コードの流れは、
- 品番をキーにしたマップを作ります。
- 在庫データの品番だけを抜きます。
- 在庫データの品番に基づいた品名と型式を「品番をキーにしたマップ」から引きます。
- 在庫データの品番列と、品名、型式をmapにします。
- mapに在庫の数量と単価の列を加えます。
- mapからQFrameを作成します。
ただの左結合なのにmapを作ったり、しかもそのマップは一般化できなかったりと、扱いづらさを感じるのでした。
フォローになりますが、QFrameの簡単な操作自体は列ごとに型がきちんと決まっていて、軽量で扱いやすいことは間違いないです。
しかしながら、グループ化や結合といった処理は複雑になりがちなので、pandasを使いたい、と思いました。