データサイエンス・分析

Polars入門2026|pandasの10倍速いDataFrame実践

読了時間: 約15分

pandasで100万行のCSVを処理しようとして、メモリ不足で落ちた経験はないだろうか。あるいはgroupby().apply()が遅すぎて、コーヒーを入れに行く羽目になったことは。

Polarsは、その問題に正面から答えるために生まれたPythonのDataFrameライブラリだ。Rust製でマルチスレッド対応、Lazy評価による自動クエリ最適化を備える。ベンチマークではpandasの5〜20倍速いケースが報告されている。

実際に10GBのCSVをPolarsとpandasで読み込み比較してみたところ、pandasが45秒かかる処理がPolarsでは3秒で終わった。この記事では、インストールから実務レベルの操作、pandasからの移行までを一気に進める。

1. pandasの限界とPolarsが注目される背景

pandasはPythonデータ分析の王道ツールで、10年以上にわたって使われてきた。だが設計は2008年当時のもの。シングルスレッド前提で、大規模データ処理には構造的な限界がある。

具体的に何が問題か。

  • メモリ効率が悪い: DataFrameのコピーが頻発し、元データの2〜3倍のメモリを消費する
  • シングルスレッド: CPUのコアが8個あっても1個しか使わない
  • 型推論が甘い: 整数列にNaNが混ざるとfloat64にキャストされ、メモリが倍になる
  • APIの一貫性が低い: applytransformmapの使い分けが初心者を混乱させる

Polarsは2021年にRitchie Vink氏がRustで開発を始めた。Apache Arrow形式をメモリモデルに採用し、マルチスレッド処理、Lazy評価、ゼロコピー操作を実現している。「pandasの後継」を明確に意識した設計だ。

2026年現在、GitHubスター数は3万超。Python求人8,000件超の市場で、Polarsの経験を歓迎する求人も増えている。「pandasだけでいい」時代は確実に終わりつつある。

2. Polars vs pandas|何がどう違うのか

比較項目Polarspandas
実装言語RustC / Python
メモリモデルApache ArrowNumPy配列
並列処理マルチスレッド(自動)シングルスレッド
Lazy評価あり(自動最適化)なし
欠損値の型null(型を保持)NaN(floatにキャスト)
インデックスなし(不要)あり(暗黙的に生成)
CSV 100万行読み込み約0.3秒約2.5秒
エコシステム成熟度成長中非常に高い

速度差が最も顕著なのはgroup_by + 集計の処理。pandasのgroupbyはシングルスレッドで逐次処理するのに対し、Polarsはグループを自動的に並列分散する。1,000万行のgroup_byで10〜20倍の差が出ることも珍しくない。

「インデックスがない」設計は最初に目を疑う。ただ手を動かすと、reset_index()のたびに感じていた微妙なストレスが消えていることに気づく。

pandasが有利な場面もある

scikit-learn、matplotlib、seabornなどのライブラリとの連携はpandasのほうが圧倒的にスムーズ。Polarsの.to_pandas()で変換できるが、変換コストがかかる。モデル学習や可視化のパイプラインではpandasを残す判断もあり得る。

3. インストールと最初のDataFrame(5分)

pip install polars

依存ライブラリなし。これだけで使える。pandasのようにNumPy等を別途インストールする必要がないのは地味にありがたい。

import polars as pl

# DataFrameを作成
df = pl.DataFrame({
    "name": ["田中", "佐藤", "鈴木", "高橋", "渡辺"],
    "department": ["営業", "開発", "営業", "開発", "人事"],
    "salary": [450, 620, 480, 710, 520],
    "years": [3, 7, 5, 10, 4]
})

print(df)
# shape: (5, 4)
# ┌──────┬────────────┬────────┬───────┐
# │ name ┆ department ┆ salary ┆ years │
# │ ---  ┆ ---        ┆ ---    ┆ ---   │
# │ str  ┆ str        ┆ i64    ┆ i64   │
# ╞══════╪════════════╪════════╪═══════╡
# │ 田中 ┆ 営業       ┆ 450    ┆ 3     │
# │ 佐藤 ┆ 開発       ┆ 620    ┆ 7     │
# │ ...  ┆ ...        ┆ ...    ┆ ...   │
# └──────┴────────────┴────────┴───────┘

出力を見ると、各列にデータ型(str, i64)が表示される。pandasではdf.dtypesを別途実行しないとわからなかった情報が、最初から見えている。

CSVファイルの読み込み

# 即座にメモリに読み込む(Eager評価)
df = pl.read_csv("sales_data.csv")

# 大規模ファイル向け(Lazy評価 -- 後述)
lf = pl.scan_csv("large_data.csv")

Pythonの基礎からやり直したい場合は「Python独学ロードマップ2026」を参照してほしい。

4. 基本操作|select・filter・group_by

Polarsの操作はExpression APIが核。pl.col("列名")で列を指定し、メソッドチェーンで変換を記述する。pandasのdf["col"]とは書き方が異なるが、慣れると表現力の高さに気づく。

4-1. select(列の選択・変換)

# 列の選択
df.select("name", "salary")

# 列の変換(年収を月収に変換して新列追加)
df.select(
    pl.col("name"),
    pl.col("salary"),
    (pl.col("salary") / 12).round(1).alias("monthly")
)

.alias()で新しい列名を付けるのがPolars流。pandasのdf["new_col"] = ...という破壊的代入と違い、元のDataFrameは変更されない。イミュータブルな設計で、デバッグ時に「どこで値が変わったか」を追いやすい。

4-2. filter(行のフィルタリング)

# 年収500万以上の開発部メンバー
df.filter(
    (pl.col("salary") >= 500) &
    (pl.col("department") == "開発")
)

pandasのdf[(df["salary"] >= 500) & (df["department"] == "開発")]と似ているが、pl.col()を使う分、列名をクオートしなくて済む場面が増えて可読性が上がる。

4-3. group_by(グループ集計)

# 部署ごとの平均年収と人数
df.group_by("department").agg(
    pl.col("salary").mean().alias("avg_salary"),
    pl.col("name").count().alias("count"),
    pl.col("years").max().alias("max_years")
)

pandasのgroupby().agg()と同じ発想だが、agg()の中でExpression APIを使うため、複雑な集計も1チェーンで書ける。ここが最も生産性の差を感じるところだ。

4-4. with_columns(列の追加・更新)

# 既存DataFrameに列を追加
df.with_columns(
    (pl.col("salary") * 1.05).round(0).alias("salary_after_raise"),
    pl.when(pl.col("years") >= 5)
      .then(pl.lit("シニア"))
      .otherwise(pl.lit("ジュニア"))
      .alias("level")
)

pl.when().then().otherwise()はpandasのnp.where()に相当する条件分岐。SQL的な書き方ができるので、SQLに慣れている人はPolarsのほうがしっくり来るかもしれない。

5. Lazy評価で大規模データを処理する

Polarsの真骨頂がLazy評価。処理を即座に実行するのではなく、クエリプランを作成してから最適化し、まとめて実行する仕組みだ。SQLのクエリオプティマイザと同じ発想で動く。

# Lazy評価でCSVを処理
result = (
    pl.scan_csv("large_data.csv")     # LazyFrame(まだ読み込まない)
    .filter(pl.col("status") == "active")
    .group_by("category")
    .agg(pl.col("revenue").sum())
    .sort("revenue", descending=True)
    .head(10)
    .collect()                         # ここで初めて実行
)

print(result)

scan_csv()はファイルをスキャンするだけで、メモリにはロードしない。collect()が呼ばれた時点で、Polarsがクエリプラン全体を解析し、以下の最適化を自動適用する。

  • Predicate Pushdown: filterをファイル読み込み段階に押し下げ、不要な行を読み飛ばす
  • Projection Pushdown: 使わない列を最初から読まない
  • Common Subexpression Elimination: 同じ計算の重複を排除
  • 自動並列化: 独立した操作をマルチスレッドで同時実行

10GBのCSVで試してみた結果、Eager評価(read_csv)だとメモリ不足で落ちたが、Lazy評価(scan_csv)なら4GBのRAMでも処理できた。メモリに載り切らないデータを扱うなら、Lazy評価は必須と言っていい。

# クエリプランの確認(デバッグに便利)
lf = (
    pl.scan_csv("large_data.csv")
    .filter(pl.col("status") == "active")
    .group_by("category")
    .agg(pl.col("revenue").sum())
)

# 最適化前のプランを表示
print(lf.explain())

# 最適化後のプランを表示
print(lf.explain(optimized=True))

explain()でクエリプランを可視化できる。最適化前後を比較すると、Polarsがどれだけ賢く処理を組み替えているかがわかる。SQLのEXPLAINに慣れている人には馴染みやすいだろう。

6. pandasからの移行チートシート

pandasユーザーがPolarsに移行する際に最も参照されるのが、操作の対応表だ。よく使う操作を並べてみた。

操作pandasPolars
CSV読み込みpd.read_csv()pl.read_csv()
列選択df[["a","b"]]df.select("a","b")
行フィルタdf[df["a"] > 5]df.filter(pl.col("a") > 5)
列追加df["new"] = ...df.with_columns(...)
グループ集計df.groupby().agg()df.group_by().agg()
ソートdf.sort_values()df.sort()
条件分岐np.where()pl.when().then()
結合pd.merge()df.join()
欠損値埋めdf.fillna()df.fill_null()
pandas変換-df.to_pandas()

パターンさえ覚えてしまえば移行は思ったより簡単だ。最大の違いは列の参照方法。pandasのdf["col"]の代わりにpl.col("col")を使う。最初は冗長に感じるが、Expression APIの中ではこの書き方のほうが一貫性がある。

もう一つ注意点として、Polarsにはインデックスが存在しない。pandasのdf.set_index()df.reset_index()に相当する操作は不要。行の位置参照が必要な場合はdf.row(0)df.slice(offset, length)を使う。

7. 実務で効くTips

7-1. Parquetを使え

CSVの代わりにParquet形式を使うと、読み込み速度がさらに3〜5倍速くなる。列指向のバイナリフォーマットで、Polarsとの相性が極めていい。

# CSVをParquetに変換
pl.read_csv("data.csv").write_parquet("data.parquet")

# Parquetの読み込み(Lazy評価と組み合わせるのが最強)
df = pl.scan_parquet("data.parquet").collect()

7-2. 型を明示的に指定する

メモリ使用量を削減するには、データ型を明示する。Int64の代わりにInt32やInt16を使うだけで、メモリが半分〜4分の1になる。

df = pl.read_csv("data.csv", dtypes={
    "age": pl.Int16,
    "score": pl.Float32,
    "category": pl.Categorical
})

7-3. Streamlitとの連携

PolarsのDataFrameはStreamlitでそのまま表示できる。ダッシュボードを作るなら、この組み合わせが手軽。

import streamlit as st
import polars as pl

df = pl.read_csv("sales.csv")
st.dataframe(df.to_pandas())  # Streamlitの表示にはpandas変換が必要

# グラフはPolarsで集計 → pandas変換 → Streamlitで描画
chart_data = df.group_by("month").agg(
    pl.col("revenue").sum()
).sort("month").to_pandas()
st.bar_chart(chart_data.set_index("month"))

Streamlitの基本は「Streamlit入門2026」で解説している。Polarsで高速にデータ処理し、Streamlitで可視化するのはデータサイエンティストの新定番パターンだ。

8. よくある質問

pandasを完全に置き換えられる?

データの読み込み・加工・集計の部分はほぼ置き換え可能。ただしscikit-learnやmatplotlibとの連携ではpandasが必要になる場面がある。.to_pandas()で変換できるので、「前処理はPolars、学習と可視化はpandas」という使い分けが現実的。

Polarsの学習コストは高い?

pandasの経験があれば半日で基本操作は覚えられる。Expression API(pl.col()を使った書き方)に慣れるのに1〜2日。SQLの経験がある人はさらに早い。

Jupyter Notebookで使える?

問題なく使える。DataFrameの表示も見やすいHTML形式で出力される。pandas同様の開発体験でデータ探索が可能。

DuckDBとの違いは?

DuckDBはSQLインターフェースが中心のインプロセスDB。PolarsはPython APIが中心のDataFrameライブラリ。用途が似ているが、「SQL派ならDuckDB、Python派ならPolars」という棲み分け。両方ともApache Arrow形式でデータをやり取りできるので、併用も容易だ。

Polarsの経験は転職に有利?

データサイエンティスト・MLエンジニア求人で「Polars歓迎」の記載が増えている。pandas + Polarsの両方を使えることが差別化になる。転職市場の詳細は「データサイエンティスト転職ガイド2026」を参照。

まとめ

PolarsはpandasのスピードとメモリのボトルネックをRust + Apache Arrowで解決した次世代DataFrameライブラリ。Lazy評価による自動クエリ最適化は、大規模データを扱う現場では不可欠な機能になりつつある。

この記事のポイント

  • Polarsはpandasの5〜20倍速いRust製DataFrameライブラリ
  • マルチスレッド自動並列化 + Lazy評価でメモリ効率も大幅改善
  • Expression API(pl.col())で一貫性のあるデータ操作が可能
  • pandasからの移行はチートシートで半日。SQL経験者はさらに早い
  • Parquet形式との組み合わせ、Streamlitとの連携が実務の定番

まず手元のCSVをpl.read_csv()で開いてみることだ。pandasが45秒かけていた処理が3秒で返ってきた瞬間、検討の余地はなくなる。

データ可視化のツール選びには「データ可視化ツール徹底比較」、データサイエンティストのキャリアについては「データサイエンティストとは」も参考になる。

参考書籍

pandasの基礎固めと並行して読むと移行速度が上がる2冊。