Polars は Rust で実装された列指向のデータフレームライブラリで、近年 pandas の代替候補として広く名前を聞くようになりました。大量データで速いだけでなく、API の設計思想も pandas と異なるため、「どんな場面で嬉しいのか」「pandas のコードをどう書き換えるか」を理解しておくと選択肢が広がります。

本記事では、Polars の特徴を概観し、株価データを使って pandas との書き方の違いを並べて確認します。

目次

  1. インストール
  2. Polars の 3 つの特徴
  3. サンプルデータ
  4. 列の選択 — 式が中心
  5. 行の絞り込み
  6. 列の追加 — with_columns
  7. groupby
  8. 並べ替え・トップ N
  9. 遅延評価(LazyFrame)
  10. NULL の扱い
  11. pandas との相互変換
  12. いつ Polars を選ぶか

インストール

Terminal window
pip install polars pyarrow

pyarrow は CSV・Parquet 入出力で内部的に使われます。Polars 単体でも動きますが、入れておくと選択肢が増えます。

検証バージョン: Python 3.12.5 / Polars 1.7.1

Polars の 3 つの特徴

Polars が pandas と違う点を、性質で整理します。

特徴内容
列指向(columnar)列ごとに連続メモリで保持する。SIMD・キャッシュ効率が良い
遅延評価(lazy)クエリプランを作ってから一括実行できる。最適化の余地が大きい
マルチスレッドgroupby や filter が複数 CPU で並列に走る
Rust 実装Python のオーバーヘッドが少ない。型・所有権の安全性も継承
式(Expression)中心 APIpl.col("x") * 2 のように列を式として扱う

実用上の効果として、数千万行レベルのデータでも groupby が数秒で終わる、ファイル読み込みが pandas の数倍速い、といった違いが体感できます。

サンプルデータ

pandas と並べて書き方を比較するために、共通の DataFrame を用意します。

import polars as pl
df = pl.DataFrame({
"Date": ["2026-04-01", "2026-04-02", "2026-04-03",
"2026-04-01", "2026-04-02", "2026-04-03"],
"Code": [7203, 7203, 7203, 9984, 9984, 9984],
"C": [2900, 2925, 2880, 9800, 9750, 9900],
"Vo": [11_000_000, 9_500_000, 12_500_000,
4_200_000, 5_100_000, 4_800_000],
})
print(df)
shape: (6, 4)
┌────────────┬──────┬───────┬──────────┐
│ Date ┆ Code ┆ C ┆ Vo │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 ┆ i64 │
╞════════════╪══════╪═══════╪══════════╡
│ 2026-04-01 ┆ 7203 ┆ 2900 ┆ 11000000 │
│ ... ┆ ... ┆ ... ┆ ... │
└────────────┴──────┴───────┴──────────┘

表の上部に shape と各列の データ型 が表示されるのが Polars の特徴です。型が常に明示されるため、object のような曖昧な型に悩まされることが減ります。

列の選択 — 式が中心

pandas は df["C"] で列を取り出しますが、Polars では 式(pl.col("C")) を使った組み立てが中心です。

# pandas:
# df["C"] * 2
# Polars:
df.select(pl.col("C") * 2)

「列を 2 倍する処理を、まだ実行せずに記述だけしておく」のが式の発想です。select / with_columns / filter などのメソッドに渡すことで、はじめて値が確定します。

複数列の選択や型変換は次の通りです。

df.select([
pl.col("Date").str.to_date("%Y-%m-%d").alias("Date"),
pl.col("Code"),
pl.col("C").alias("close_price"),
(pl.col("C") * pl.col("Vo")).alias("amount"),
])

alias は SQL の AS と同じで、式の結果に名前を付けます。

行の絞り込み

pandas の真偽値マスクと似ていますが、ここでも式を使います。

df.filter(pl.col("Code") == 7203)
df.filter((pl.col("Code") == 7203) & (pl.col("Vo") > 10_000_000))

カッコと & | の使い方は pandas と同じです。

列の追加 — with_columns

pandas の代入(df["x"] = ...)に当たるのが with_columns です。

df2 = df.with_columns([
pl.col("Date").str.to_date("%Y-%m-%d").alias("Date"),
(pl.col("C") * pl.col("Vo")).alias("amount"),
])
print(df2)

新しい列を追加した 新しい DataFrame が返ります。Polars では原則として元の DataFrame を破壊的に変更せず、メソッドチェーンで処理を組み上げます。

groupby

Polars では group_by です(groupby は古い名前)。pandas の集計と書き味を比較します。

# pandas:
# df.groupby("Code").agg(
# mean_close=("C", "mean"),
# sum_volume=("Vo", "sum"),
# )
# Polars:
df.group_by("Code").agg([
pl.col("C").mean().alias("mean_close"),
pl.col("Vo").sum().alias("sum_volume"),
])

集計関数は式に対するメソッドとして定義されており、.mean() / .sum() / .std() / .min() / .max() がそのまま使えます。

並べ替え・トップ N

並べ替えは sort、上位 N 件は head で書けます。

df.sort(["Code", "Date"])
df.sort("C", descending=True).head(3)

descending=True は pandas の ascending=False と同じ意味です。

遅延評価(LazyFrame)

Polars の真価は 遅延評価 モードです。scan_csvlazy() で LazyFrame に変換し、すべての処理を collect() するまで実行を遅らせます。

import polars as pl
q = (
pl.scan_csv("prices.csv")
.filter(pl.col("Code").is_in([7203, 9984, 8306]))
.with_columns(pl.col("Date").str.to_date("%Y-%m-%d"))
.group_by("Code")
.agg([
pl.col("C").mean().alias("mean_close"),
pl.col("C").std(ddof=1).alias("std_close"),
])
)
print(q.explain()) # クエリプランを表示
result = q.collect() # ここで実行
print(result)

explain() で、Polars がどのような最適化を行うかを確認できます。scan_csv を使うと、必要な列だけを CSV から読み込むなど、クエリ全体を見渡した最適化が走ります。

NULL の扱い

Polars は欠損値を null として明示的に持ちます。pandas の NaN(浮動小数の特殊値) と異なり、整数列でも文字列列でも欠損を表現できます。

df_with_null = pl.DataFrame({"x": [1, None, 3]})
df_with_null.select(pl.col("x").is_null())
df_with_null.fill_null(0)

集計関数(mean / sum など)はデフォルトで null をスキップします。pandas に近い感覚で扱えます。

pandas との相互変換

既存の pandas コード資産を活かしつつ Polars に移行できます。

import pandas as pd
# pandas → Polars
pdf = pd.DataFrame({"a": [1, 2, 3]})
pol = pl.from_pandas(pdf)
# Polars → pandas
pdf2 = pol.to_pandas()

NumPy / PyArrow との変換も用意されています。「読み込みと前処理を Polars で速くやり、可視化や ML は既存の pandas / scikit-learn に渡す」といった分業がしやすい設計です。

いつ Polars を選ぶか

選択基準を表にまとめます。

状況候補
数十万行までの分析、可視化中心pandas
数百万〜数千万行の集計・前処理Polars
既存資産が pandas で多いpandas(必要に応じて Polars に部分移行)
列指向ファイル(Parquet)を高速に読みたいPolars
厳密な型管理を重視するPolars

実データでの差は#4-12「pandas vs Polars — どちらを使うか / ベンチマーク」 でベンチマークします。

生成AI へのプロンプト例

「pandas の処理を Polars に書き換える」依頼は、対比を明示すると安定します。

次の pandas コードを Polars 1.7 系で書き換えてください。
[pandas コード]
df["return"] = df.groupby("Code")["C"].pct_change()
result = (
df.dropna(subset=["return"])
.groupby("Code")
.agg(mean=("return","mean"), std=("return","std"))
)
要件:
- group_by + agg + alias を使う
- 列の追加は with_columns で
- DataFrame は遅延評価 (lazy) を使わず、通常の DataFrame で書く

API 名 / バージョンを明記すると、新旧 API の混在を避けられます。

まとめ

  • Polars は列指向・遅延評価・Rust 実装で、大量データでの高速化に強い
  • API は 式中心(pl.col(...))。select / with_columns / filter で組み立てる
  • groupby は group_by(...).agg([...]) の形
  • LazyFrame(scan_csv / lazy)を使うと、クエリ全体の最適化が走る
  • pandas / NumPy との相互変換が容易で、段階的な移行が可能