Polars 入門 — Pandas より高速な DataFrame ライブラリ
Polars は Rust 製の DataFrame ライブラリで、Pandas と比べてメモリ効率・処理速度の面で大幅な改善が見込めます。この記事では基本操作から Lazy API・DuckDB 連携まで実際のコードで解説します。
Polars とは
Polars は 2020 年に登場した Rust 製の DataFrame ライブラリです。Apache Arrow をメモリフォーマットとして採用し、SIMD 最適化・並列処理により Pandas より高速に動作します。
Pandas との比較
| 項目 | Pandas | Polars |
|---|---|---|
| 実装言語 | Python / C | Rust |
| メモリフォーマット | 独自 | Apache Arrow |
| 並列処理 | 限定的(GIL の制約) | ネイティブマルチスレッド |
| Lazy 評価 | なし | あり(クエリ最適化) |
| 速度(大規模データ) | 普通 | 2〜10 倍高速(ベンチマーク依存) |
インストール
pip install polars
# 追加機能(Excel 読み込み・Parquet 等)
pip install polars[all]
基本操作
DataFrame の作成
import polars as pl
df = pl.DataFrame({
"name": ["Alice", "Bob", "Charlie", "David"],
"age": [25, 30, 35, 28],
"sales": [120.5, 85.0, 200.3, 150.7],
"region": ["East", "West", "East", "North"],
})
CSV / Parquet の読み込み
# CSV
df = pl.read_csv("data/sales.csv")
# CSV(型を明示)
df = pl.read_csv(
"data/sales.csv",
schema_overrides={"date": pl.Date, "amount": pl.Float64},
)
# Parquet(glob も使える)
df = pl.read_parquet("data/*.parquet")
基本的な操作
# 行数・列数
print(df.shape) # (4, 4)
print(df.schema) # Schema({'name': String, 'age': Int64, ...})
# 列の選択
df.select("name", "sales")
# 列の追加
df = df.with_columns(
(pl.col("sales") * 1.1).alias("sales_with_tax")
)
フィルタリング
# 条件フィルタ
df.filter(pl.col("age") > 28)
# 複数条件(AND)
df.filter(
(pl.col("age") > 25) & (pl.col("region") == "East")
)
# in 条件
df.filter(pl.col("region").is_in(["East", "North"]))
# 文字列の部分一致
df.filter(pl.col("name").str.starts_with("A"))
集計
# グループ集計
df.group_by("region").agg(
pl.col("sales").sum().alias("total_sales"),
pl.col("sales").mean().alias("avg_sales"),
pl.col("sales").count().alias("count"),
)
# 全列の基本統計
df.describe()
Pandas との文法比較
# ---- フィルタリング ----
# Pandas
pd_df[pd_df["age"] > 28]
# Polars
pl_df.filter(pl.col("age") > 28)
# ---- 列の追加 ----
# Pandas(ミュータブル)
pd_df["tax"] = pd_df["sales"] * 0.1
# Polars(イミュータブル)
pl_df = pl_df.with_columns(
(pl.col("sales") * 0.1).alias("tax")
)
# ---- グループ集計 ----
# Pandas
pd_df.groupby("region")["sales"].sum().reset_index()
# Polars
pl_df.group_by("region").agg(pl.col("sales").sum())
# ---- 欠損値の埋め ----
# Pandas
pd_df["sales"].fillna(0)
# Polars
pl_df.with_columns(pl.col("sales").fill_null(0))
Lazy API — クエリ最適化で大規模データを効率処理
Polars の Lazy API は SQL のクエリプランナーのように、実際に計算する前にクエリ全体を最適化します。大規模データでは特に効果的です。
Eager vs Lazy
# Eager: 各ステップで即座に計算(小〜中規模データ向け)
result = (
df
.filter(pl.col("sales") > 100)
.group_by("region")
.agg(pl.col("sales").sum())
)
# Lazy: クエリを構築してから一括実行(大規模データ向け)
result = (
df.lazy()
.filter(pl.col("sales") > 100)
.group_by("region")
.agg(pl.col("sales").sum())
.collect() # ここで初めて計算実行
)
ファイルから直接 Lazy 読み込み
# scan_* はファイルを Lazy で読み込む(メモリに全部載せない)
result = (
pl.scan_csv("data/large_sales.csv") # 数 GB のファイルも OK
.filter(pl.col("region") == "East")
.group_by("date")
.agg(pl.col("sales").sum())
.sort("date")
.collect()
)
クエリプランの確認
lf = pl.scan_csv("data/large_sales.csv").filter(pl.col("sales") > 100)
print(lf.explain()) # 最適化後
print(lf.explain(optimized=False)) # 最適化前
DuckDB との連携
Polars と DuckDB は互いにデータを渡し合えます。DuckDB の SQL 表現力と Polars の高速処理を組み合わせるのが強力なパターンです。
import polars as pl
import duckdb
df = pl.read_parquet("data/sales.parquet")
# Polars DataFrame を直接 SQL で参照
result = duckdb.sql("""
SELECT
region,
DATE_TRUNC('month', sale_date) AS month,
SUM(sales) AS total_sales
FROM df
WHERE sale_date >= '2025-01-01'
GROUP BY region, month
ORDER BY month, total_sales DESC
""").pl() # .pl() で Polars DataFrame として返す
ハマりやすいポイント
Polars の DataFrame はイミュータブル
# NG: Polars では動かない
df["tax"] = df["sales"] * 0.1
# OK
df = df.with_columns(
(pl.col("sales") * 0.1).alias("tax")
)
group_by の結果は順序が不定
並列処理のため、結果の行順が毎回異なります。順序を保証したい場合は sort を明示します。
result = (
df
.group_by("region")
.agg(pl.col("sales").sum())
.sort("region")
)
Lazy API で .collect() を忘れる
result = pl.scan_csv("data.csv").filter(pl.col("age") > 25)
print(type(result)) # <class 'polars.LazyFrame'> ← collect() が必要
result = result.collect()
print(type(result)) # <class 'polars.DataFrame'>
まとめ
- Polars は Rust 製・Apache Arrow ベースの高速 DataFrame ライブラリ
- 基本 API は Pandas に似ているが、イミュータブル・Lazy 評価などの違いがある
scan_*+ Lazy API で数 GB のファイルをメモリ効率よく処理できる- DuckDB と Arrow 経由でゼロコピー連携でき、SQL と DataFrame 処理を使い分けられる
- Pandas からの移行は段階的に行える。まず小さなデータで試して感覚をつかむのがおすすめ