polars について
DataFrameライブラリPolarsでデータを処理・変換するためのスキルです。即時・遅延クエリ、式による操作、クエリ最適化、大容量ファイル向けのメモリ管理を扱います。
主な機能
- 01即時・遅延DataFrame API
- 02式ベースのデータ変換
- 03クエリプランの最適化
- 04大規模データのストリーミング処理
- 05Parquet、CSV、Avroのワークフロー
活用シーン
- 01大容量ファイルのデータクリーニングパイプライン構築
- 02表形式データの変換と集計
- 03時系列特徴量の作成
- 04JSONログの解析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tondevrel/ scientific-agent-skills --skill polarsインストール後、Claude Code で/polarsを入力して読み込まれたか確認します。
詳細
Polars - 高性能データフレーム
Polars は速度を重視して設計されています。データを単一の CPU コアで逐次処理する pandas とは異なり、Polars は利用可能なすべてのコアを使って処理を並列化します。「Lazy API」では実行前にクエリを最適化できるため、メモリーのオーバーヘッドと処理時間を大幅に削減できます。
使用する場面
- pandas では処理が難しい大規模なデータセット(1GB~100GB 以上)の処理。
- 実行速度を優先する場合(Polars は pandas よりも 10~100 倍高速なことがよくあります)。
- 複雑なデータ変換パイプラインを扱う場合(遅延評価)。
- RAM が限られているシステム(Polars は pandas よりもメモリー効率に優れています)。
- 厳密な型安全性と一貫した null の処理が必要な場合。
- 大きな Parquet、CSV、または Avro ファイルの読み書き。
リファレンスドキュメント
公式ドキュメント:https://docs.pola.rs/
ユーザーガイド:https://docs.pola.rs/user-guide/
検索パターン:pl.DataFrame、pl.LazyFrame、pl.col、df.select、df.filter、df.group_by
基本原則
Eager API と Lazy API
- Eager:処理が即座に実行されます(pandas と同様です)。
- Lazy:処理がクエリプランに登録されます。Polars はプランを最適化し(述語プッシュダウン、プロジェクションプッシュダウンなど)、呼び出されたときにのみ実行します。
Expression API
Polars は宣言的な構文を使用します。ループや複雑なラムダを書く代わりに、pl.col() を使って式を記述します。これらの式は高度に最適化され、並列に実行されます。
Apache Arrow
Polars は Apache Arrow 形式でデータを保存するため、PyArrow や DuckDB などのツールとデータをコピーせずにやり取りできます。
クイックリファレンス
インストール
pip install polars
# For Excel/Cloud support
pip install 'polars[all]'
標準インポート
import polars as pl
import numpy as np
基本パターン - Lazy ワークフロー(「Polars の流儀」)
import polars as pl
# 1. Scan (Lazy) - doesn't load data yet
lf = pl.scan_csv("massive_data.csv")
# 2. Build Query Plan
query = (
lf.filter(pl.col("age") > 25)
.group_by("city")
.agg([
pl.col("salary").mean().alias("avg_salary"),
pl.col("name").count().alias("count")
])
.sort("avg_salary", descending=True)
)
# 3. Collect (Execute)
df = query.collect()
重要なルール
✅ 推奨事項
- Lazy API(scan_*)を優先する - Polars がメモリーを最適化し、不要なデータをスキップできるようになります。
- 式を使用する - 文字列やインデックスで列を選択するのではなく、常に
pl.col("name")を使用してください。 - メソッドチェーンを使用する - Polars は簡潔で読みやすいパイプラインを作成できるよう設計されています。
- スキーマを指定する - CSV を読み込む際にスキーマを指定すると、型推論エラーを防ぎ、読み込みを高速化できます。
- collect(streaming=True) を使用する - RAM よりも大きなデータセットの場合、ストリーミングによってデータをチャンク単位で処理できます。
- CSV より Parquet を選ぶ - 永続的な保存には Parquet を使用してください。大幅に高速で、型情報も保存されます。
❌ 禁止事項
- .apply() を避ける - 独自の Python 関数は Rust による並列最適化を妨げるため、処理が遅くなります。組み込みの式を使用してください。
- inplace=True を使用しない - Polars は JAX と同様に不変性を重視しており、変換すると新しい DataFrame が返されます。
- 早い段階で pandas に変換しない - 速度を維持するため、できるだけ長くデータを Polars のまま扱ってください。
- 行の反復処理を避ける -
for row in dfはアンチパターンです。ベクトル化された式を使用してください。
アンチパターン(絶対に避ける)
import polars as pl
# ❌ BAD: Using Python lambdas for simple math
# df.select(pl.col("val").map_elements(lambda x: x * 2)) # Slow!
# ✅ GOOD: Use expressions
df.select(pl.col("val") * 2) # Fast, parallelized in Rust
# ❌ BAD: Filtering after a heavy operation
# df.group_by("id").mean().filter(pl.col("id") == 5)
# ✅ GOOD: Lazy API will automatically "push down" the filter
(pl.scan_csv("data.csv")
.filter(pl.col("id") == 5) # Optimized to read only id=5
.group_by("id").mean())
# ❌ BAD: Converting to pandas just to check .head()
# df.to_pandas().head()
# ✅ GOOD: Polars has its own fast .head() and rich printing
print(df.head())
Expression API の詳細
選択と変換
df.select([
pl.col("name"),
pl.col("price") * 1.2, # Scalar math
pl.col("category").str.to_uppercase(), # String methods
pl.col("date").dt.year().alias("year") # Date methods
])
フィルタリング
# Multiple conditions
df.filter(
(pl.col("price") < 100) &
(pl.col("status") == "active") |
(pl.col("category").is_in(["A", "B"]))
)
集計とグループ化
高性能な統計処理
results = df.group_by("department").agg([
pl.col("salary").sum(),
pl.col("salary").max().alias("max_pay"),
pl.col("name").n_unique().alias("unique_employees"),
# Advanced: conditional aggregation inside group
pl.col("salary").filter(pl.col("role") == "manager").mean().alias("manager_avg")
])
結合と連結
SQL のような操作
# Joins: 'inner', 'left', 'outer', 'semi', 'anti', 'cross'
df_joined = df_a.join(df_b, on="id", how="left")
# As-of join (for time-series alignment)
df_aligned = df_trades.join_asof(df_quotes, on="timestamp", by="symbol")
# Concatenation
df_stacked = pl.concat([df1, df2], how="vertical")
形状変更(Pivot と Melt)
# Pivot
pivoted = df.pivot(values="sales", index="date", columns="region", aggregate_function="sum")
# Melt (Unpivot)
melted = df.melt(id_vars="date", value_vars=["store_a", "store_b"])
実用的なワークフロー
1. 大規模データのクリーニングパイプライン
def clean_and_optimize(path):
return (
pl.scan_parquet(path)
.drop_nulls(subset=["user_id"])
.with_columns([
pl.col("email").str.to_lowercase(),
pl.col("timestamp").str.to_datetime("%Y-%m-%d %H:%M:%S"),
(pl.col("income") / 1000).cast(pl.Float32) # Downcast for memory
])
.filter(pl.col("timestamp") > pl.date(2023, 1, 1))
.collect(streaming=True)
)
2. 時系列の特徴量エンジニアリング
def engineer_features(df):
return df.with_columns([
# Rolling average
pl.col("price").rolling_mean(window_size="7d", by="date").alias("rolling_7d"),
# Lead/Lag
pl.col("price").shift(1).alias("prev_price"),
# Cumulative sum
pl.col("sales").cum_sum().over("category")
])
3. 高速な JSON/ログ解析
def parse_logs(path):
return (
pl.scan_ndjson(path) # Read line-delimited JSON
.select([
"level",
pl.col("message").str.extract(r"Error: (.*)", 1),
pl.col("metadata").struct.field("user_id") # Access nested fields
])
.collect()
)
パフォーマンスの最適化
with_columns の効果
列を 1 つずつ作成する代わりに、with_columns を使うと複数の計算を並列に実行できます。
# All 3 columns are calculated simultaneously in different threads
df = df.with_columns([
(pl.col("a") + pl.col("b")).alias("sum"),
(pl.col("a") * pl.col("b")).alias("prod"),
pl.col("c").str.len().alias("c_len")
])
Dtype による列の選択
列のグループに変換をすばやく適用できます。
# Multiply all float columns by 100
df = df.with_columns(
pl.col(pl.Float64) * 100
)
よくある落とし穴と解決策
.apply() の落とし穴
.map_elements()(以前の .apply())で Python 関数を使うと、処理が遅くなります。
# ❌ Problem: Using custom Python code
# df.select(pl.col("txt").map_elements(my_custom_func))
# ✅ Solution: Use Polars native expressions or pl.when()
df.select(
pl.when(pl.col("score") > 50).then(pl.lit("Pass")).otherwise(pl.lit("Fail"))
)
大きなファイルでのメモリーエラー
.collect() で OOM が発生する場合は、メモリーに読み込もうとしているデータが多すぎる可能性があります。
# ✅ Solution:
# 1. Use .filter() early in the Lazy plan.
# 2. Use streaming: .collect(streaming=True).
# 3. Select only the columns you need.
String と Categorical
カーディナリティの低い文字列(「City」や「Gender」など)には、Categorical を使用してください。
# ✅ Solution: Saves massive amounts of RAM and speeds up joins
df = df.with_columns(pl.col("category").cast(pl.Categorical))
ベストプラクティス
- 大きなファイルには必ず Lazy API を使用する -
read_csv()やread_parquet()ではなく、scan_csv()またはscan_parquet()から始めます。 - collect する前に完全なクエリプランを構築する - Polars がパイプライン全体を最適化できるようにします。
- Python 関数より式を使用する -
pl.col()式を活用して、パフォーマンスを最大限に高めます。 - CSV を読み込む際はスキーマを指定する - 型推論のオーバーヘッドとエラーを防ぎます。
- メモリに収まらないデータセットにはストリーミングを使用する -
collect()でstreaming=Trueを有効にします。 - Parquet 形式を優先する - 読み書きが速く、型情報も保持されます。
- カーディナリティの低い文字列は Categorical にキャストする - メモリ使用量とパフォーマンスが大幅に改善します。
- 複数の変換には with_columns を使用する - 列の作成を並列化できます。
- Lazy クエリでは早い段階でフィルターを適用する - 述語プッシュダウンにより、スキャンするデータ量を削減できます。
- pandas への変換は避ける - 最高の速度を得るため、Polars のエコシステム内にとどまります。
Polars は、単一ノードのデータ処理における新たなゴールドスタンダードです。Rust の安全性と Python の柔軟性を組み合わせ、現代のデータサイエンスにシームレスで非常に高速な体験を提供します。