🐻‍❄️

生成AI時代のPolarsの勝ち筋、またはPolarsへの鎮魂歌

に公開

生成AI時代のPolarsの勝ち筋、またはPolarsへの鎮魂歌

はじめに

テーブルデータの分析といえばPandas。これは長らく不動の構図でした。Pythonを使う多くの人々が、最初に触れるデータ処理ライブラリとしてPandasを選び、豊富なチュートリアルとStack Overflowの投稿に助けられながら育ってきました。

そんなPandasに対して、様々な対抗ライブラリが誕生してきました。その中でも、有力なのがPolarsでした。
2023年半ばから急に日本語のTech Blogでもその名目を多く見るようになり、Pandasの50倍、いや100倍速いといった記述が多く目立ちました。
そして、24年7月にはバージョン1.0.0がリリースされPolarsは一気に時代の寵児となる。と個人的に思っていたんですが、実際は最早忘れ去られていますかね。

Zennの記事でPolarsの一覧を出すと、60件に満たない状態で、最新のものが2か月以上前。
https://zenn.dev/topics/polars?order=latest

同じくQiitaで見ても、記事は120件に満たず、最新は今月のものがありましたが、大体月に1~2件あるくらいです。
https://qiita.com/tags/polars

別にこの記事で、Polarsが流行らない要因分析をするつもりはないですが、やはりPandasと比べて書き方が違う部分が敬遠される要因ではないかなと、結局最近はPandasを使っている自分自身を振り返って考えています。

ただ、本当にPolarsをこれで終わりにしてしまっていいのか。

  • VHSに敗れたベータ
  • プレイステーションに敗れたセガサターン
  • QWERTY配列のキーボードの牙城を崩せなかったDvorak配列
    などなど、性能が良いものが勝つわけではないことは、歴史が証明しています。

これまでの歴史では、結局は、ソフトウェアやコンテンツが優れていた(PandasとPolarsで言えば、正にZennやQiitaで記事が大量にあるPandasに軍配)ことや、移行することに心理的・物理的なハードルがあった(PandasとPolarsで言えば、同じ処理で書き方が違うので、元々使い慣れたPandasがいい)ことが、
性能の良し悪しよりも重要でした。

しかし、もしかしたら生成AI時代には、違う歴史が紡げるのではないかという希望的観測から、Polarsの勝ち筋を考えていきたいと思います。

書き方の違いが問題でなくなる時代

PandasからPolarsへ――多くのデータサイエンティストが最初につまずくのは「書き方が違う」という点でしょう。
df['col'].mean()と書けたものが、df.select(pl.col('col').mean())になる。
グルーピング処理も、複雑なネストやLazyFrameの概念など、慣れないうちは違和感だらけです。

けれど、ここで登場するのが、生成AIという新しい道具です。

ChatGPTやGitHub Copilotのようなツールが浸透した今、
「どう書くか」は自分で覚えるものから、「書かせるもの」へと変わりつつあります。

今では「このPandasのコードをPolarsに書き換えて」と投げかければ、
一瞬でほぼ正しい変換結果を返してくれる。細かなチューニングや意図の補足は必要かもしれませんが、
少なくとも「慣れていないから書けない」「違いを調べるのが面倒」という移行コストは激減しました。

つまり、書き方が違うから敬遠されるという、かつての最大の弱点は、
生成AIの時代にはもはや「言い訳にならない」状況になりつつあるのです。

書き方よりも、「待たされる時間」が損失になる

生成AIの活用が一般的になると、ローカルでのデータ処理はますます増えます。

  • OpenAIやAnthropicに投げる前に、社内のデータを加工したい

  • チャットで指示を出しながら、リアルタイムに集計・可視化したい

  • PythonコードをGPTに生成させて、毎回異なる視点での集計を何度も試す

こうなると、処理速度がボトルネックになるのではないでしょうか。

Pandasで5秒かかる処理が、Polarsでは0.2秒で終わる。
1回だけなら誤差ですが、これが日常的に繰り返されると、「待つ時間」そのものが生産性の損失になります。

さらに言えば、生成AIとのインタラクションは「試行回数の多さ」が価値を生む世界です。
「このKPIで良かったっけ?」「やっぱり週次で見たい」「あの条件をもう一度加えて」――
そうした繰り返しの中で、処理が遅ければ、そのたびに集中力は削がれ、思考は分断されます。

Polarsの真価は、まさにこの「AIによる即時実行・即時修正」スタイルの中でこそ発揮されます。

生成AI時代の本質は「処理速度 × 試行回数」

生成AIと付き合っていく上で、何より大事なのは、「試して、間違えて、また試す」サイクルの速さです。
従来の人間中心の開発サイクルでは、Pandasでも十分だったかもしれません。

しかし、今後は「一人で考え、一人で回す」プロセスが加速する。
そこでは、人間の思考速度にボトルネックがあるのではなく、ツールの応答速度がボトルネックになる世界が待っています。

Pandasの書きやすさは今も魅力ですが、書きやすさすらAIに委ねられるなら、
私たちは、どのくらい速く、何回思考を回せるかを主軸に、道具を選ぶべきではないでしょうか。

実際にPandas vs Polars

というわけで、プログラミングというよりも”機械に対する処理の命令”が生成AIでできるんだから、Pandasである必要がないじゃんという論調で来たので、特にプログラムを載せなくてもいいかなと思ったのですが、
この内容だと生成AIにプログラムを作らせて比較することが趣旨にあうのではないかと思って、

使ったデータは、私が好んで使う小売り業のデータで、Online Retail Datasetです。
50MBいかないデータなので、処理スピードにはほぼほぼ差はでなそうですが、実際どうなんでしょうか。

生成AIは、Colabのコード生成機能を使っています(たぶん、裏側Gemniですよね)
まずは、Plandas。というか、特に指定しなくても次のようにプロンプトを書くと、勝手にPlandasにしてくれました。

online_retail.csvのデータをPandasでロードして、2011年1~6月の期間における、商品(StockCode, Description)ごとの合計売上金額を算出し、金額が高い順に順位をつけてください

生成されたコードに、timeを使って時間を計測する部分を追加して次の通りです。

start = time.time()

# prompt: online_retail.csvのデータをPandasでロードして、2011年1~6月の期間における、商品(StockCode, Description)ごとの合計売上金額を算出し、金額が高い順に順位をつけてください

import pandas as pd

# データのロード
df = pd.read_csv('/content/online_retail.csv')

# InvoiceDateをdatetime型に変換
df['InvoiceDate'] = pd.to_datetime(df['InvoiceDate'])

# 2011年1月から6月までのデータを抽出
df_2011_h1 = df[(df['InvoiceDate'] >= '2011-01-01') & (df['InvoiceDate'] < '2011-07-01')]

# 各行の売上金額を計算
df_2011_h1['Sales'] = df_2011_h1['Quantity'] * df_2011_h1['UnitPrice']

# 商品(StockCode, Description)ごとに合計売上金額を算出
sales_by_product = df_2011_h1.groupby(['StockCode', 'Description'])['Sales'].sum().reset_index()

# 金額が高い順にソート
sales_by_product_sorted = sales_by_product.sort_values(by='Sales', ascending=False)

# 順位をつける
sales_by_product_sorted['Rank'] = sales_by_product_sorted['Sales'].rank(method='min', ascending=False)

# 結果を表示
display(sales_by_product_sorted)

end = time.time()
print(f"{end - start:.5f} sec")

結果は、4.44699 sec。まあ、気にするほどの速度ではないですね。

次に、Polars。Pandasと違って、プロンプトを何度か試行錯誤しました。読み取り時のデータ型指定が上手くできなかったんですよね。

online_retail.csvのデータを次のデータ型でPolarsでロード
InvoiceDateは、datetime形式の%Y-%m-%d %H:%M:%S
Quantityは、int
UnitPriceは、float
それ以外は、str
2011年1~6月の期間における、商品(StockCode, Description)ごとの合計売上金額を算出し、金額が高い順に順位をつけてください

こちらも、生成されたコードに、時間計測部分を付加して次のコード。ちなみに、pip install polars は、事前にやっています。

import polars as pl

start = time.time()

# データのロードと型指定
df_pl = pl.read_csv(
    '/content/online_retail.csv',
    schema_overrides={
        'InvoiceNo': pl.Utf8,
        'StockCode': pl.Utf8,
        'Description': pl.Utf8,
        'Quantity': pl.Int64,
        'InvoiceDate': pl.Datetime(),
        'UnitPrice': pl.Float64,
        'CustomerID': pl.Utf8,
        'Country': pl.Utf8,
    },
    try_parse_dates=False # 手動で日付型に変換するためFalse
)

# InvoiceDateを datetime形式の%Y-%m-%d %H:%M:%S に変換
# read_csvのdtype指定でDatetimeを指定すると、自動的に適切な形式にパースされるため、
# 明示的なstrptimeは不要。ここでは念のためタイムゾーンをUTCとして指定しています。
# データによってはタイムゾーン情報の有無を確認してください。

# 2011年1月から6月までのデータを抽出

df_pl_2011_h1 = df_pl.filter(
    (pl.col('InvoiceDate') >= pl.datetime(2011, 1, 1)) &
    (pl.col('InvoiceDate') < pl.datetime(2011, 7, 1))
)

# 各行の売上金額を計算し、商品ごとに合計売上金額を算出
sales_by_product_pl = df_pl_2011_h1.with_columns(
    (pl.col('Quantity') * pl.col('UnitPrice')).alias('Sales')
).group_by(['StockCode', 'Description']).agg(
    pl.sum('Sales').alias('TotalSales')
)

# 金額が高い順にソートし、順位をつける
sales_by_product_pl_ranked = sales_by_product_pl.sort('TotalSales', descending=True).with_columns(
    pl.arange(1, pl.count() + 1).alias('Rank') # 1からの連番で順位を生成
)

# 結果を表示
display(sales_by_product_pl_ranked)

end = time.time()
print(f"{end - start:.5f} sec")

この速度は、1.00640 sec。
データが小さすぎたので、流石に50倍とかはいかなかったですが、5倍近いスピードです。

これ1回だと、それこそプロンプトを修正している手間の方が多いんですが、先に書いた通り、これを「試して、間違えて、また試す」としていくと、それなりの時間の差になりそうです。
もっと現実的な話をすると、50MB未満のデータだったので、この差ですが、これが数GBくらいになると、違いが結構強くでてくるのではないでしょうか。

おわりに

ということで、最近は全然話題にならなくなってしまったPolarsに対して、逆張りする感じで記事にしてみました。

ただ、決して単なるネタではなく、待たされる時間の削減というのは、これからの時代により強く求められていくことではないかと思います。

ユーザーに対して、リアルタイムな分析を提供する場面ばもちろんですし、自分自身が分析作業をしている際に思考が中断されるということは大きなデメリットとなるのではないでしょうか。
(その時間に別の作業すればいいじゃんという発想もありますが、個人的に最近マルチタスクを減らそうと努めており、分析処理を待っている時間に他のことをするというのも、やりたくないことだったりします。)

ということで、Polarsが生成AIでデータサイエンスをやる時代に、改めて活用されればと思い記事にしてみました。

あまりにも使われなくなってしまうと、Polarsがアップデートされなくなり、歴史の狭間に消えていくことになるので、そうならないために、個人的にもPolarsを使うプロンプトをより洗練していき、普段の業務で使っていきたいと思います。

Discussion