3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PolarsAdvent Calendar 2024

Day 3

PolarsのList型とArray型を比較してみた

Last updated at Posted at 2024-12-02

Polarsに格納できるデータタイプ型にList型とArray型があります。
ドキュメントで一覧見てると集合に対して同じ結果が出る処理も多そう?
今回はList型とArray型のメソッド種類の比較をして各々利用できそうなものをピックアップと同じような処理をしているのであればどっちが早いかを検証していきたいと思います。

List型とArray型のメソッド比較

PolarsのAPIドキュメントのList型とArray型の対比は以下です
image.png

前半はほとんど同じ処理ですね。List型でもarg_max取れたりしていてArray型と違いないのでは?というのが最初の印象でした。正確に差分を知るためにList型とArray型のメソッドの差を確認してみます。せっかくなのでPorlasで。

all_df = (
    pl.DataFrame(
        {
            "list_text": list_text,
            "array_text": array_text,
        }
    )
    .with_columns(
        pl.col("list_text").str.extract_all(r"Series\.list\.(\w+)\(.*\)").alias("list_method_all"),
        pl.col("array_text").str.extract_all(r"Series\.arr\.(\w+)\(.*\)").alias("arr_method_all"),
    )
)

list_text, array_textには先ほどキャプチャで貼り付けたAPIドキュメントをブラウザからざっとコピーした文字列が格納されています。これを必要箇所をextract_allメソッドで取り出して別カラムへ格納しています。返り値はリスト型です。
image.png

続いて各々のリストを縦に展開してlistもしくはarr以降のメソッドを取り出します。

list_df = (
    all_df
    .select(
        pl.col("list_method_all").explode(),
    )
    .with_columns(
        pl.col("list_method_all").str.extract(r"Series\.list\.(\w+)").alias("list_method"),
    )
)


arr_df = (
    all_df
    .select(
        pl.col("arr_method_all").explode(),
    )
    .with_columns(
        pl.col("arr_method_all").str.extract(r"Series\.arr\.(\w+)").alias("arr_method"),
    )
)

explodeメソッドにてlistで格納していたメソッド名を縦に展開しています。その後に正規表現でlistもしくはarr以降のメソッドのみを取り出しています。
image.png
image.png

あとは抽出した列で差集合と積集合をとります。

list_methods = set(list_df.get_column("list_method").to_list())
arr_methods = set(arr_df.get_column("arr_method").to_list())

list_only_methods = list_methods - arr_methods
arr_only_methods = arr_methods - list_methods
common_methods = list_methods & arr_methods

print(f"list_only_methods: {list_only_methods}")
print(f"arr_only_methods: {arr_only_methods}")
print(f"common_methods: {common_methods}")

image.png

これでお互いの差分と共通部分が出ました。list型のメソッドがarr型のメソッドをほぼ含有していますね。arr側のみのメソッドはarr->listにキャストするto_listメソッドのみなので実質全てを含有しているといえそうです。
(集計メソッドのうちmeanだけlistにしかないのはなんでだろう...)

list型のみメソッド

調べてみると@_jintaさんがlist型のメソッドについて紹介している良記事が。
https://qiita.com/_jinta/items/62777087b4807b3cb301

本記事ではlist型のみのメソッドかつ@_jintaさん記事で紹介されてないメソッドの一部をピックアップしたいと思います。

set_difference, set_intersection

リスト型で格納しているもの同士の差集合と積集合を取ってくれるメソッドがありました。先ほどのlistとarrの比較でset型を別で作成していましたがPolarsでもそれが可能なようです。

list_s = pl.Series("list_method", [list_df.get_column("list_method").to_list()])
arr_s = pl.Series("arr_method", [arr_df.get_column("arr_method").to_list()])

display(list_s.list.set_difference(arr_s).alias("list_only_methods"))
display(arr_s.list.set_difference(list_s).alias("arr_only_methods"))

display(arr_s.list.set_intersection(list_s).alias("common_methods"))

image.png

結果も一致してますね。

set_union, set_symmetric_difference

setの処理は他にもset_union, set_symmentric_differenceもありました。list型なんだけどset型の集合演算も含めてPolars内で完結できるということですね。

list_s = pl.Series("list_method", [list_df.get_column("list_method").to_list()])
arr_s = pl.Series("arr_method", [arr_df.get_column("arr_method").to_list()])

gather, gather_every

メソッド名から処理が想像できなかったのでピックアップ。
gatherメソッドではリストで指定したindexに応じてその要素を抽出したリストを返します。
null_on_oob(out of bound)をTrueにするとoobのものは全てnullを返すようになります。

gather_everyメソッドではリストでなくてintで数値を指定してその数値ごとに要素を最後まで抽出してリストを返すメソッドです。

list_s = pl.Series("list_method", [list_df.get_column("list_method").to_list()])
arr_s = pl.Series("arr_method", [arr_df.get_column("arr_method").to_list()])

concat_s = pl.concat([list_s, arr_s])

display(concat_s)
display(concat_s.list.gather([0, 2]))
display(concat_s.list.gather([0, 2, 24], null_on_oob=True))

display(concat_s.list.gather_every(2), offset=0)

image.png

listとarrの速度評価

両方ともに存在したメソッドで速度評価をしてみます。

speed_list_df = pl.Series("random", [np.random.rand(100_000_000).tolist()], dtype=pl.List(pl.Float64))
speed_arr_df = pl.Series("random", [np.random.rand(100_000_000)], dtype=pl.Array(pl.Float64, 1))

display(speed_list_df)
display(speed_arr_df)

image.png

各々1億個のデータを作成しました。
各々sumメソッドで比較してみたらやってみたら以下の通り。

image.png

速度の差はなさそうでした。。。

まとめ

PolarsのList型とArray型を比較してみました。
結論、実質的に機能が含有されているかつ速度にも大きな影響はないというところからとりあえずList型を使えば良いかという印象です。またPolars内でデータ加工かできる幅が広がりました。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?