Tips
ループやapplyメソッドを避けるといったpandasでのノウハウはFireDucksでも有効です.ここではFireDucksで性能を引き出すためのTipsを紹介します.
ループを利用しない
DataFrameやSeriesからループでデータを取り出しながら処理を行うと大きなオーバーヘッドが発生し,余計な時間がかかってしまいます.できる限りDataFrameやSeriesのAPIを組み合わせて記述しましょう(これはpandasでも同様です).
例えば以下のループではDataFrameの要素をひとつずつ処理しています.
s = 0
for i in range(len(df)):
if df["A"][i] > 2:
s += df["B"][i]
これはDataFrameのAPIを用いると以下のように書き換えることができます.
s = df[df["A"] > 2]["B"].sum()
DataFrameから一行ずつ取り出すDataframe.iterrows
を用いた処理も同様です.
applyメソッドを利用しない
DataFrame.apply
などのユーザ定義関数を実行する処理は,FireDucksで中間言語を生成して実行時コンパイルを行うという最適化機能が現在対応できていません.今後の機能改善をお楽しみに.
属性形式の列参照をしない
列の参照はdf["A"]
やdf.A
と書くこともできますが,後者はDataFrameに元々備わっている属性と競合する可能性があるため,df["A"]
とカッコ形式で書いたほうが良いでしょう.
FireDucksでは,df.A
形式の場合はA
が列名かどうかを判断する処理が必要となり,コンパイラ最適化が働かなくなる可能性があります.
pandasの未定義動作に依存しない
以下のような場合にdf["A"]
が参照を返すかコピーを返すかはpandasでは未定義ですが,もしコピーが返ってきている場合はdf
は更新されません.多くの場合,これは意図した動作ではないでしょう.
df["A"][1] = 2
FireDucksでは,このような未定義動作はpandasとは異なる結果となる可能性があるため,pandasでうまくいった場合でもFireDucksではうまくいかない場合があります.未定義動作に依存した処理を書くことはお薦めしません.
この例では以下のように書くと安全です.ただし,上で述べたように要素毎のアクセスは非効率なため,他の実装が可能であればそちらのほうが良いでしょう.
df.iloc[1, 0] = 2 # Aが最初の列の場合
フォールバックを避ける
FireDucksはフォールバックという内部的にpandasを呼び出す機能を持っています.これは現在FireDucksがサポートしていない機能をpandasを使って実行するもので,pandasとの互換性を高めるための機能です.しかしフォールバックが発生すると,FireDucksのデータ構造を一度pandasのものに変換し,pandasのメソッドを実行して,再度FireDucksのデータ構造に変換するという処理が行われるため,実行時間やメモリ使用量の観点ではデメリットになります.
フォールバックの削減は継続的に行っていますが,ユーザープログラム側の工夫でフォールバックを回避することで性能向上を行うことも有効です.環境変数FIREDUCKS_FLAGS="-Wfallback"
を設定することで,フォールバックが発生した場合にログを出力することが可能です.