mcpField
P
polars

polars

github.com/tondevrelデータ・データベース2456 回インストール39 週間前に更新
リポジトリ

polars について

DataFrameライブラリPolarsでデータを処理・変換するためのスキルです。即時・遅延クエリ、式による操作、クエリ最適化、大容量ファイル向けのメモリ管理を扱います。

主な機能

  1. 01即時・遅延DataFrame API
  2. 02式ベースのデータ変換
  3. 03クエリプランの最適化
  4. 04大規模データのストリーミング処理
  5. 05Parquet、CSV、Avroのワークフロー

活用シーン

  1. 01大容量ファイルのデータクリーニングパイプライン構築
  2. 02表形式データの変換と集計
  3. 03時系列特徴量の作成
  4. 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))

ベストプラクティス

  1. 大きなファイルには必ず Lazy API を使用する - read_csv() や read_parquet() ではなく、scan_csv() または scan_parquet() から始めます。
  2. collect する前に完全なクエリプランを構築する - Polars がパイプライン全体を最適化できるようにします。
  3. Python 関数より式を使用する - pl.col() 式を活用して、パフォーマンスを最大限に高めます。
  4. CSV を読み込む際はスキーマを指定する - 型推論のオーバーヘッドとエラーを防ぎます。
  5. メモリに収まらないデータセットにはストリーミングを使用する - collect() で streaming=True を有効にします。
  6. Parquet 形式を優先する - 読み書きが速く、型情報も保持されます。
  7. カーディナリティの低い文字列は Categorical にキャストする - メモリ使用量とパフォーマンスが大幅に改善します。
  8. 複数の変換には with_columns を使用する - 列の作成を並列化できます。
  9. Lazy クエリでは早い段階でフィルターを適用する - 述語プッシュダウンにより、スキャンするデータ量を削減できます。
  10. pandas への変換は避ける - 最高の速度を得るため、Polars のエコシステム内にとどまります。

Polars は、単一ノードのデータ処理における新たなゴールドスタンダードです。Rust の安全性と Python の柔軟性を組み合わせ、現代のデータサイエンスにシームレスで非常に高速な体験を提供します。

コメント