はじめに
初投稿になります。最近使っているPolarsについての備忘録で、自己学習のアウトプットとしてまとめています。読みやすさ、内容については度外視しているので、ご了承ください。
やったこと
Pandasで行っていた機械学習モデル構築を、Polarsに切り替えて実装を行いました。
基本的な流れは以下の通りです。
- データ読み込み
- データ分布確認
- データ前処理(ワンホット化)
- モデル構築および予測
Polarsとは
Pandasとの比較
Pandasと同様、データ処理に特化したライブラリで、記述方法やAPIも似ているため、Pandasと同じような感覚で使うことができます。Pandasと比較して、ほぼすべての処理が、高速かつ省メモリで実行できるため、作業の効率化、マシンスペックを抑えた大規模データ処理が可能です。詳しい性能比較は、こちらを参照していただければと思いますが、Pandasとの性能差は圧倒的です。
私自身も使用していて、Pandasに比べて5倍~10倍ほどの処理速度であると感じます。特に、データ読み込みの速度は顕著に差が表れ、数ギガ単位のデータでも数秒程度で読み込めてしまいますし、メモリも食わないので、大規模データを扱う際も、快適に作業できます。
余談ですが、Google colabでは、デフォルトでPolarsがインストールされているので、天下のGoogleさんも認知していますね。
記述方法やAPIについて
記述については、前述したとおり、基本的にPandasと似ていますが、独特の記述もそこそこあるため、慣れるまでにやや時間を要するかと思います。自分の場合は、普段の業務でコードを書くことが多く、使い始めてから1~2週間ほどで、使用感に慣れました。自分が参考にした記事や公式ドキュメントで、すごく丁寧にまとめてくれているので、具体的な処理については、以下の記事を参考にしてください。
<参考記事>
自分も分からないときは、まとめ記事⇒公式ドキュメント⇒ChatGPTという順で調べて、今のところ100%解決してるので、適宜使い分けてもらえればと思います。
Polarsを用いたデータ処理及びモデル構築
前置きが長くなりましたが、本題になります。コード実行はJupyter notebook上で行いました。python, polarsのバージョンは以下の通りです。
- python:3.10.14
- polars:1.2.1
0. 事前準備、環境確認
Polarsを使用する際に注意したいのが、バージョンによって、引数の名称やAPI名が異なる場合がある点です。自分は業務で古いバージョンのPolars(0.18)を使用していたのですが、バージョン違いによる記述エラーに気づけず、混乱しました。。。
ネット記事を調べると、ほぼすべて1.0以上のバージョンで記述されており、コピペしてもエラーで動作しないことも幾度かありましたので、古いバージョンを利用する方は、公式ドキュメントを確認するのが一番正確です(公式ドキュメントもデフォルトで1.0のページが表示されるので、ページ内で古いドキュメントに切り替えてください)
polarsのバージョンは、pythonのバージョンに依存するっぽい(python 3.7.12で、polars 0.18.2でした)ので、polarsをインストールしたら、バージョン確認をおすすめします。
!pip install polars
import sys
import polars as pl
#polarsバージョン確認
print("polars", pl.__version__)
# Pythonバージョン確認
sys.version
1. データ読み込み
Pandasと同様、csv,tsv等のデータを読み込めます。
今回使用するデータは以下になります。
https://www.kaggle.com/datasets/shubhambathwal/flight-price-prediction
3つのcsvファイルのうち、Clean_Dataset.csvを使用してます。
df = pl.read_csv("Clean_Dataset.csv")
データフレームは以下のように表示されます。列の型も一緒に確認できるため、df.info()より見やすいですね。
上記の通り、polarsでは各カラムごとに、データ型が固定されます。そのため、1つのカラムに文字列と数値が混在するようなデータを読み込むとエラーが発生します。人によっては、面倒に思うかもしれませんが、事前に型混在に気付けるので、個人的にはありがたい仕様です。
読み込み時にエラーが出る際は、引数オプションで型指定をしたり、エラーが出る箇所を無視するなどの処理が必要です。自分がよく使用する引数は以下になります。
引数 | 型 | 説明 |
---|---|---|
infer_schema_length | int | 読み込み時のデータ型推定に使用する行数。デフォルト値は100で、先頭から100行のデータを先に読み込んで型推定を行う。 |
schema_overrides(旧:dtypes) | dict | 列ごとに読み込むデータ型を指定可能。指定してない列に関しては、infer_schema_lengthをもとに型推定を行う。polars 0.18, 0.19では、引数名が"dtypes"なので注意。 |
columns | list | 使用する列を指定。リストはインデックスもしくは列名で指定 |
ignore_errors | bool | Trueにすると、エラーが出る箇所をnullに置き換える。例えば、intを指定して読み込んだ列に、文字列が混じっていた場合、文字列がnullに置換されます。置換前の値を知りたい場合は、schema_overridesにて、該当列をstrで読み込み、個別に処理する必要があります。 |
他にも便利な引数があるので、詳しくは公式ドキュメントや前述の参考記事を参照してください。
df = pl.read_csv("data.csv",
infer_schema_length = 100, #カラムの型推定に使用する行数
schema_overrides = {"column_1": pl.Int64, #Int型で読み込み
"column_2": pl.Float64, #Float型で読み込み
"column_3": pl.String, #Str型で読み込み
},
columns = ["column_1","column_2","column_3","column_4"],
# columns = [0,1,2,3], #インデックス指定も可能
ignore_errors = True
)
2. データ分布確認
2.1 基本統計量の確認
基本統計量は、pandasと同様の記述で確認できます。欠損値(null_count)も確認できるので便利です。
df.describe()
2.2 データの可視化
polarsでは、df.hist()のような可視化メソッドはないですが、以下のようにpandasに変換することでpandasの機能も利用できます。polarsの処理で困ったら、とりあえずpandasに変換すれば何とかなります。
df.to_pandas().hist()
一方、matplotlib, seabornでは、polarsのデータ型のままで動くので、pandasへの変換は不要で、pandasと同じコードで可視化できます。
import matplotlib.pyplot as plt
# matplotlibでのでのヒストグラム出力
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.hist(df["price"], bins=50)
plt.show()
import seaborn as sns
# seabornでのヒストグラム出力
sns.histplot(df['price'], bins = 50)
2.3 半自動可視化ライブラリへの対応(sweetviz)
こちら余談ですが、自分の場合は、sweetvizのような半自動化ライブラリで可視化することが多いので、試してみました。結論、sweetvizではpolarsのデータ型のままだとエラーになるので、pandasに変換してから渡す必要があります。
import sweetviz as sv
# Buisness,Economyのデータを比較
report = sv.compare([df.filter(pl.col("class") == "Business").to_pandas(),'Business'],
[df.filter(pl.col("class") == "Economy").to_pandas(),'Economy'],
target_feat='price')
report.show_html("Business_Economy_compare.html")
ちなみに、sweetvizを使用すると以下のようなレポートをhtmlファイルで出力してくれます。
分析初期段階で網羅的に特徴を把握したいときに便利なので、気になる方はぜひ使用してみてください!
参考:個人的に最もおすすめなEDAツール Sweetvizの使い方
3. データ前処理
今回は、機械学習モデル構築の確認が目的ですので、最低限の処理のみ実施してます。
データ抽出、集計、文字列処理などの、データ加工に関する処理は、以下の記事ですごく丁寧にまとめられているので、こちらを確認してください。
今回使用するLightGBMは、欠損値があっても大丈夫な機械学習モデルなので、ワンホット化のみ実施します。
import polars.selectors as cs # int,strなどのデータ型で、列選択が可能
# チケット情報を削除
df = df.drop("flight")
# ワンホット化
df_dummy = df.to_dummies(cs.string())
df_dummy
polarsのデータフレームでは、データ型の指定されているため、データ抽出や、列選択時に、データ型で指定できます。pandasで同じことをやろうとすると、少し手間ですし、データ型が混同しているとエラーを吐かれたりもするので、データ型の指定により、処理が安定するのは、個人的に気に入っている点です。
特徴量が増えてしまうので、flight(チケット情報)を削除してますが、番号部分は特徴量として有効に働き、精度が上がるので、興味ある方は試してみてください。
4. モデル構築および予測
4.1 データ分割
scikit-learnのtrain_test_splitは普通に使えます。
import lightgbm as lgb
from sklearn.model_selection import train_test_split
# train, validに分割
train, valid = train_test_split(df_dummy, test_size=0.2, random_state=1234)
print("train_size:{}, valid_size:{},".format(len(train), len(valid)))
display(train,valid)
4.2 LightGBMでの学習モデル作成
LightGBMでの学習で、データを渡す際にエラーが出てしまうので、pandasに変換してからLightGBMに渡しています。pandasに変換してからは、今まで通りモデルを定義して学習するだけです。polarsからpandasへの変換も、ほとんど時間はかかりません。
# polars -> pandasに変換
train_pd = train.to_pandas()
valid_pd = valid.to_pandas()
#LightGBM用にデータセットを作成
lgb_train = lgb.Dataset(train_pd.drop("price",axis=1), train_pd["price"])
lgb_valid = lgb.Dataset(valid_pd.drop("price",axis=1), valid_pd["price"])
#ハイパーパラメタの設定
hyper_params = {
'boosting_type':'gbdt',#ブースティングの種類。ここでは勾配ブースティング
'objective':'regression',#機械学習タスクの種類。ここでは回帰分類
'metric':'rmse',#モデルの評価指標
'num_leaves':100,#葉(ノード)の数。大きいほど複雑になる
'max_depth':-1, #木の深さ、デフォルト-1で、負の数は制限なしを意味する
'learning_rate':0.1,#学習率
'random_state':1234, #乱数のシード値
}
# この数字を1にすると学習スコア推移が表示される、表示しない場合は0にする
verbose_eval = 1
#モデル作成、学習回数、学習打ち切り回数の設定
model = lgb.train(params = hyper_params,
train_set = lgb_train,
valid_sets = [lgb_train, lgb_valid],
valid_names=['train','val'], # 学習経過で表示する名称
#学習数と学習打ち切り回数のみ自分で自由に決める。
num_boost_round=200, #学習数
callbacks=[lgb.early_stopping(stopping_rounds=10, #学習打ち切り回数
verbose=True),
lgb.log_evaluation(verbose_eval)]
)
せっかくなので、Feature importanceも確認してみましたが、class_Businessが非常に高い値になってました。お試しでのモデル作成なので、特に深く言及しません。
# 特徴量の重要度を(Feature importance)を確認する
lgb.plot_importance(model, figsize=(8,4), max_num_features=10, importance_type='gain')
以上でpolarsを使用した機械学習モデル構築のパイプラインが組めたので、無事に目標達成できました!!
まとめ
Pandasからpolarsへの切り替えということで、可視化、データ処理、機械学習モデル作成までを実践してみました。とにかく処理が早くて快適です(特にデータ読み込み)。今回使用したデータ以外にも、5,000,000×17ほどのデータで、集計・加工を試していますが、pandasだと数十秒かかる処理も、「えっ、もう終わったの?」というくらい一瞬で終わるので、びっくりしてます。
はじめは、polarsの記述に戸惑う部分もありましたが、pandasと似ているので、Pandasを触ったことがあれば、すんなり移行できる印象です。
pandasにしか対応してないpythonライブラリもあるため、その場合は、to_pandas()でpandasに変換すれば問題ないかと思います。
個人的には、polarsでの記述の方がしっくりきますし、処理も早く快適なので、今後もデータ処理においては、polarsメインで使用していきたいと思います。