Polarsの贅沢な使い方

こんにちは!FLINTERS BASEの梶山です。
今回はPolarsの贅沢な使い方について知ってもらおうと思います。

結論

PolarsはRust製でPandasに比べて処理が速いというイメージがありますよね。 そのPolarsの贅沢な使い方。それは、、、csvリーダーとして使うことです!
もっと具体的にいうとNULLが,,で空文字が""で表現されているcsvファイルで空文字とNULLを区別したいときです。(下記のようなcsv)

col1,col2,col3
"",,1

Pandas

Pandasで普通にread_csvで読み込んだ時はどちらもNanで読み込まれます。

keep_default_na=Falseにしても両方とも空文字扱いになってしまいます。

Polars

しかしPolarsだとちゃんとNULLと空文字を判別してくれます!

missing_utf8_is_empty_string=Trueにすれば両方とも空文字になります。

NULLと空文字の区別が特に重要じゃない場合は一緒として扱ってもいいかもしれないですが、それぞれが重要な情報を持っている場合もありますのでなるべくそのままで持ちたいですよね。(lightgbmとかはNULLはNULLのまま扱える)
ということでこのようなcsvでNULLと空文字を区別したい場合はPolarsを使いましょう!
なぜできるか気になる人は下の続きを読んでください。

Nullと空文字をなぜ区別できるか

ここからはなぜPolarsがなぜNULLと空文字を区別できるかについて書きます。
なぜかというとPolarsは内部でApach Arrowが使用されているからです。そしてそのApach Arrowで決められている列のデータの持ち方に工夫があります。

Apach Arrowは列に対してValidity bitmap buffer、Offsets buffer、Values bufferの情報を持っています。

それぞれ下記のようなものになります。

  • Validity bitmap buffer:各要素がNULLかどうか(※ビットなので右から左の順)
  • Offsets buffer:Values buffer内の各文字列の開始位置(最後の値は終了位置なので必ず要素数より1個多くなる。)
  • Values buffer:実際の文字列のバイト列を連続して格納

arrow.apache.org

公式の画像の例では空文字がなかったので空文字がどうやって保持されているか確認してみましょう。 まだ表示が少しわかりづらいですがValidity bitmap bufferが00001011、Offsets bufferが01112、Values bufferがabになります。
これからわかるように空文字はValues bufferに何か値を持っているわけではなく、NULLとの違いはValidity bitmap bufferが1かどうかということですね。
さらにどうやってcsvのparseとかしているんだとか気になった人は公式のリポジトリのコードを読んでみてください。
(自分はコードがCで書いてあってわかんなかったのでLLMにぶん投げて雰囲気だけなぞって深掘りするのを辞めました笑)

最後に

今までApach Arrowについて列指向で処理が早いぐらいの認識しか持ってなかったんですが、内部では色々工夫されていることが知れて面白かったです。
どうしても今回の例のようなcsvを処理しないといけない時はこの記事を思い出して貰えばと思います。

おまけ

PolarsはApach Arrowを内部的に使用しているのでpyarrowでも同じくNULLと空文字で区別できます。