はじめに
Goのデータフレームパッケージを作り、OSSとして公開しました。この記事では、このOSSをつくるまでの経緯と、今回作ったGobanの基本的な仕組みの解説をしていきます。
こんな人に読んでほしい
- OSSを作ってみたいが、何をしたらいいのかわからない
- Goを使ったデータフレームパッケージを探している
- Gobanにコントリビュートをしてみたい
データフレームとはなにか
そもそも、データフレームについて聞いたことのない方も多いと思います。データフレームについては、以下の説明がわかりやすいです。
データフレームとは、スプレッドシートのような、行と列からなる二次元のデータ構造です。データフレームは、柔軟かつ直感的にデータを格納し操作できるため、現代のデータ分析において最も一般的なデータ構造です。
引用元:DataFrames – Databricksより筆者訳
この説明の通り、二次元の表を扱い、様々な集計処理を行うのがデータフレームライブラリの役割です。データフレームライブラリとしては、Pythonのpandasが有名で、データ分析や機械学習の世界で非常によく使われています。
開発の経緯
弊社HRBrainではタレントマネジメントシステムの一つのサービスとして、分析ダッシュボードを提供しています。その中で、ユーザーが任意のグラフを作成する機能を開発してきました。この機能を実装する上で、データフレームパッケージの必要性を認識し、このパッケージを自ら実装するにいたりました。
1.ドメイン知識の整理
グラフ作成機能を実装するにあたって、まず、必要なドメイン知識を整理しました。特に、記述統計やデータの視覚化に関する知識が必要だったので、それらの分野の学習をして、情報を集めました。ドメイン知識を整理していく中で、グラフ作成機能のうち、表の集計処理の部分を、データフレームパッケージを導入することで整理できることがわかりました。
また、RDBを使って集計を行うことも検討しました。しかし、動的なスキーマの表に対する集計なので、RDBの基本的な機能では対応できず、アプリケーションで集計を行うのが望ましいと判断しました。
2.既存のOSSの検討
データフレームパッケージが有用であるとわかったところで、既存のOSSの採用を検討しました。検討にあたって、「Go言語で扱えるデータフレーム厳選4つ」などを参考にしました。
既存のパッケージの採用を検討する上で、以下の点を調査しました
- アクティブにメンテナンスされていること
- グループ化の操作ができること
しかし、多くのデータフレームパッケージは、十分にメンテナンスされていないか、必要だったグループ化の操作に対応しておらず、検討から外れました。
唯一、Gotaの採用は最後まで検討しましたが、
- 元の開発者が開発をやめてしまい、2021年11月以降リリースされていないこと1
- 複数カラムでのソートのような、基本的なイシューが放置されていること2
- interface{}を多用するなど、型定義が不適切な箇所があること3
といった理由から採用を見送りました。
データフレームの基本的な機能であれば、実装はそれほど難しくなく、チーム内に知識を蓄積できることや、会社としてコントロールできる範囲にソースをおけるメリットがあると判断し、自ら実装することにしました。
3.実装
Gotaのコードを読みつつ、pandasの仕様を参考に実装をしていきました。特に、実装の初期段階においては、Gotaと同じようにつくれば、最低限の実装ができる保証があったので、とても安心感がありました。一方で、開発を進めていく中で、Gotaの実装が不適切な部分を見つけ、それを改善する形でGoban独自の実装をしていきました。
Gobanのデータフレームの仕組み
今回実装したGobanは、主に2つのオブジェクトによって構成されています。
- Series
- 同じ型の要素のリスト
- Dataframe
- 同じ長さのSeriesのリスト
Seriesは、一種類の変数4の値のリストで、平均値や中央値、標準偏差といった基本的な統計量の計算ができます。5
Dataframeは、複数のSeriesのリストまとめて、表として取り扱えるようにしたものです。現状では、グループごとの集計などの操作に対応しています。
サンプルコード:フルーツ名ごとの平均価格
例えば、フルーツ名と価格の表があるときに、フルーツ名ごとの平均価格を求める処理を考えます。
図示すると、以下のようになります。
処理前
fruits | prices |
---|---|
apple | 2 |
orange | 3 |
orange | 2 |
処理後
fruits | prices |
---|---|
apple | 2 |
orange | 2.5 |
これをGobanを使って求めるコードは以下のようになります。
df := dataframe.DataFrame{
Columns: []series.Series{
{
Name: "fruits",
Elements: element.StringElements{
{
Value: "apple",
IsNull: false,
},
{
Value: "orange",
IsNull: false,
},
{
Value: "orange",
IsNull: false,
},
},
},
{
Name: "prices",
Elements: element.NumericElements{
{
Value: 2,
IsNull: false,
},
{
Value: 3,
IsNull: false,
},
{
Value: 2,
IsNull: false,
},
},
},
},
RecordCount: 3,
}
groups, _ := df.GroupBy("fruits")
newDf, _ := groups.Aggregate(dataframe.AggregationConditions{
{
ColumnName: "fruits",
Method: series.None,
},
{
ColumnName: "prices",
Method: series.Mean,
},
})
log.Printf("[DEBUG] newDf: %+v", newDf)
出力
[DEBUG] newDf: {Columns:[{Name:fruits Elements:[{Value:apple IsNull:false} {Value:orange IsNull:false}] AggregatedMethod:} {Name:prices Elements:[{Value:2 IsNull:false} {Value:2.5 IsNull:false}] AggregatedMethod:Mean}] RecordCount:2}
今後の開発
GobanをOSSとして公開したものの、まだ汎用的なデータフレームパッケージには程遠い状態です。多くの基本的なメソッドが未実装である上、データ構造としても大いに改善の余地があると思っています。
一方で、Goのデータフレームパッケージに対するニーズは、これからさらに高まっていくと考えています。アプリケーションが扱うデータが大きくなるにつれて、Pythonではなく、Goのようなパフォーマンスの優れた言語でデータを処理することが合理的になると思うからです。
コントリビューターの募集
基本的な機能が未実装だからこそ、ジュニアレベルからシニアレベルまで、多くの開発者の方にコントリビュートいただける余地があると考えています。少しでも興味のお持ちの方は、お気軽にイシューやプルリクエスト、ディスカッションへの投稿をいただければと思います。また、開発者向けのSlackワークスペースもありますので、興味のあるかたはご参加ください。
なお、Gobanをご利用される場合は、マイナーバージョンのリリース前であり、後方互換性の無い変更がありうる旨をご理解ください。
おわりに
このOSSをつくることで、記述統計やGoのデータ型についての知見を深めることができ、とても勉強になりました。
最後に、自由に開発させてもらった会社と、レビューとコントリビュートしてくださった同僚に感謝します。ありがとうございました。
参考
- hrbrain/goban: Fast and robust Dataframe package for Go
- go-gota/gota: Gota: DataFrames and data wrangling in Go (Golang)
- pandas - Python Data Analysis Library
- Go言語で扱えるデータフレーム厳選4つ