Skip to content

スキーマ

Polars の DataFrame または LazyFrame のスキーマは、列の名前とデータ型を定義します。 DataFrame または LazyFrame.schema メソッドを使ってスキーマを確認できます。

DataFrame · lazy

q3 = pl.DataFrame({"foo": ["a", "b", "c"], "bar": [0, 1, 2]}).lazy()

print(q3.schema)

OrderedDict({'foo': String, 'bar': Int64})

スキーマは、Lazy API で重要な役割を果たします。

Lazy API でのタイプチェック

Lazy API の利点の 1 つは、Polars がデータを処理する前にスキーマをチェックすることです。このチェックは、Lazy クエリを実行するときに行われます。

整数の bar 列に .round 式を呼び出す以下の簡単な例で、この仕組みがわかります。

lazy · with_columns

q4 = (
    pl.DataFrame({"foo": ["a", "b", "c"], "bar": [0, 1, 2]})
    .lazy()
    .with_columns(pl.col("bar").round(0))
)

.round 式は、浮動小数点型の列でのみ有効です。整数列に .round を呼び出すと、collect でクエリを評価したときに InvalidOperationError が発生します。このスキーマチェックは、データを処理する前の collect の呼び出し時に行われます。

try:
    print(q4.collect())
except Exception as e:
    print(e)
`round` operation not supported for dtype `i64`

このクエリをイーガーモードで実行すると、エラーは最初のステップでデータが処理された後にのみ見つかります。

Lazy クエリを実行すると、Polars は時間のかかるデータ処理の前に、潜在的な InvalidOperationError をチェックします。

Lazy API にはスキーマが必要

Lazy API では、Polars のクエリオプティマイザがクエリプランのあらゆるステップでスキーマを推測できる必要があります。これは、事前にスキーマが分からない操作は Lazy API で使えないことを意味します。

事前にスキーマが分からない操作の典型例は .pivot 操作です。.pivot では、新しい列名がある列のデータから決まります。これらの列名は事前に分からないため、.pivot は Lazy API では使えません。

Lazy API で使えない操作への対処

パイプラインに Lazy API で使えない操作が含まれる場合は、通常以下のようにするのが最善です:

  • その操作までは Lazy モードで実行
  • .collect でパイプラインを実行し、DataFrame を具体化
  • DataFrame で非 Lazy の操作を実行
  • 出力を再び LazyFrame に変換 (lazy) し、Lazy モードで続行
  • .filter などの操作を行う
  • 最後に .collect でクエリを実行し、DataFrame を取得

以下の例では、この手順を示しています:

  • 簡単な DataFrame を作成
  • .lazyLazyFrame に変換
  • .with_columns で変換
  • .collect でクエリを実行し DataFrame を取得
  • DataFrame.pivot を実行
  • 再び LazyFrame に変換 (lazy)
  • .filter を実行
  • 最後に .collect でクエリを実行し DataFrame を取得

collect · pivot · filter

lazy_eager_query = (
    pl.DataFrame(
        {
            "id": ["a", "b", "c"],
            "month": ["jan", "feb", "mar"],
            "values": [0, 1, 2],
        }
    )
    .lazy()
    .with_columns((2 * pl.col("values")).alias("double_values"))
    .collect()
    .pivot(
        index="id", columns="month", values="double_values", aggregate_function="first"
    )
    .lazy()
    .filter(pl.col("mar").is_null())
    .collect()
)
print(lazy_eager_query)

shape: (2, 4)
┌─────┬──────┬──────┬──────┐
│ id  ┆ jan  ┆ feb  ┆ mar  │
│ --- ┆ ---  ┆ ---  ┆ ---  │
│ str ┆ i64  ┆ i64  ┆ i64  │
╞═════╪══════╪══════╪══════╡
│ a   ┆ 0    ┆ null ┆ null │
│ b   ┆ null ┆ 2    ┆ null │
└─────┴──────┴──────┴──────┘