コンテキスト
Polarsは、データ変換のための独自のドメイン固有言語(DSL)を開発しました。このDSLは非常に使いやすく、人間が読みやすい上に、複雑なクエリを可能にします。この言語の2つの主要な構成要素は、コンテキストとエクスプレッションです。後者については次のセクションで説明します。
名前が示すように、コンテキストは、エクスプレッションを評価するために考慮すべき条件や関係を表します。主なコンテキストは以下の3 つです1:
- 選択:
df.select(...)
,df.with_columns(...)
- フィルタリング:
df.filter()
- グループ化 / 集計:
df.group_by(...).agg(...)
以下の例は、次の DataFrame
で実行されます:
df = pl.DataFrame(
{
"nrs": [1, 2, 3, None, 5],
"names": ["foo", "ham", "spam", "egg", None],
"random": np.random.rand(5),
"groups": ["A", "A", "B", "C", "B"],
}
)
print(df)
use rand::{thread_rng, Rng};
let mut arr = [0f64; 5];
thread_rng().fill(&mut arr);
let df = df! (
"nrs" => &[Some(1), Some(2), Some(3), None, Some(5)],
"names" => &[Some("foo"), Some("ham"), Some("spam"), Some("eggs"), None],
"random" => &arr,
"groups" => &["A", "A", "B", "C", "B"],
)?;
println!("{}", &df);
shape: (5, 4)
┌──────┬───────┬──────────┬────────┐
│ nrs ┆ names ┆ random ┆ groups │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str │
╞══════╪═══════╪══════════╪════════╡
│ 1 ┆ foo ┆ 0.154163 ┆ A │
│ 2 ┆ ham ┆ 0.74005 ┆ A │
│ 3 ┆ spam ┆ 0.263315 ┆ B │
│ null ┆ egg ┆ 0.533739 ┆ C │
│ 5 ┆ null ┆ 0.014575 ┆ B │
└──────┴───────┴──────────┴────────┘
選択
選択コンテキストは、カラムにエクスプレッションを適用します。select
は、集計、式の組み合わせ、または新しいリテラルのカラムの生成を行います。
選択コンテキストのエクスプレッションは、すべて同じ長さの Series
を生成するか、長さが 1 の Series
を生成する必要があります。リテラルは長さ 1 の Series
として扱われます。
一部のエクスプレッションが長さ 1 の Series
を生成し、他のエクスプレッションが長さ 1 ではない場合、長さ 1 の Series
は残りの Series
の長さにブロードキャストされます。
ブロードキャストは、式内部でも発生することに注意してください。例えば、pl.col.value() / pl.col.value.sum()
では、value
カラムの各要素がカラムの合計で除されます。
out = df.select(
pl.sum("nrs"),
pl.col("names").sort(),
pl.col("names").first().alias("first name"),
(pl.mean("nrs") * 10).alias("10xnrs"),
)
print(out)
let out = df
.clone()
.lazy()
.select([
sum("nrs"),
col("names").sort(Default::default()),
col("names").first().alias("first name"),
(mean("nrs") * lit(10)).alias("10xnrs"),
])
.collect()?;
println!("{}", out);
shape: (5, 4)
┌─────┬───────┬────────────┬────────┐
│ nrs ┆ names ┆ first name ┆ 10xnrs │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str ┆ f64 │
╞═════╪═══════╪════════════╪════════╡
│ 11 ┆ null ┆ foo ┆ 27.5 │
│ 11 ┆ egg ┆ foo ┆ 27.5 │
│ 11 ┆ foo ┆ foo ┆ 27.5 │
│ 11 ┆ ham ┆ foo ┆ 27.5 │
│ 11 ┆ spam ┆ foo ┆ 27.5 │
└─────┴───────┴────────────┴────────┘
クエリから分かるように、選択コンテキストは非常に強力で、お互いに独立して(そして並行して)任意のエクスプレッションを評価できます。
select
ステートメントと同様に、with_columns
ステートメントも選択コンテキストに入ります。with_columns
と select
の主な違いは、with_columns
は元のカラムを保持し新しいカラムを追加するのに対し、select
は元のカラムを削除することです。
df = df.with_columns(
pl.sum("nrs").alias("nrs_sum"),
pl.col("random").count().alias("count"),
)
print(df)
let out = df
.clone()
.lazy()
.with_columns([
sum("nrs").alias("nrs_sum"),
col("random").count().alias("count"),
])
.collect()?;
println!("{}", out);
shape: (5, 6)
┌──────┬───────┬──────────┬────────┬─────────┬───────┐
│ nrs ┆ names ┆ random ┆ groups ┆ nrs_sum ┆ count │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str ┆ i64 ┆ u32 │
╞══════╪═══════╪══════════╪════════╪═════════╪═══════╡
│ 1 ┆ foo ┆ 0.154163 ┆ A ┆ 11 ┆ 5 │
│ 2 ┆ ham ┆ 0.74005 ┆ A ┆ 11 ┆ 5 │
│ 3 ┆ spam ┆ 0.263315 ┆ B ┆ 11 ┆ 5 │
│ null ┆ egg ┆ 0.533739 ┆ C ┆ 11 ┆ 5 │
│ 5 ┆ null ┆ 0.014575 ┆ B ┆ 11 ┆ 5 │
└──────┴───────┴──────────┴────────┴─────────┴───────┘
フィルタリング
フィルタリングコンテキストは、Boolean
データ型に評価される 1 つ以上のエクスプレッションに基づいて DataFrame
をフィルタリングします。
shape: (2, 6)
┌─────┬───────┬──────────┬────────┬─────────┬───────┐
│ nrs ┆ names ┆ random ┆ groups ┆ nrs_sum ┆ count │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ f64 ┆ str ┆ i64 ┆ u32 │
╞═════╪═══════╪══════════╪════════╪═════════╪═══════╡
│ 3 ┆ spam ┆ 0.263315 ┆ B ┆ 11 ┆ 5 │
│ 5 ┆ null ┆ 0.014575 ┆ B ┆ 11 ┆ 5 │
└─────┴───────┴──────────┴────────┴─────────┴───────┘
グループ化 / 集計
group_by
コンテキストでは、エクスプレッションはグループを単位として機能するため、結果のデータ長は様々です(各グループのメンバー数に依存します)。
out = df.group_by("groups").agg(
pl.sum("nrs"), # sum nrs by groups
pl.col("random").count().alias("count"), # count group members
# sum random where name != null
pl.col("random").filter(pl.col("names").is_not_null()).sum().name.suffix("_sum"),
pl.col("names").reverse().alias("reversed names"),
)
print(out)
let out = df
.lazy()
.group_by([col("groups")])
.agg([
sum("nrs"), // sum nrs by groups
col("random").count().alias("count"), // count group members
// sum random where name != null
col("random")
.filter(col("names").is_not_null())
.sum()
.name()
.suffix("_sum"),
col("names").reverse().alias("reversed names"),
])
.collect()?;
println!("{}", out);
shape: (3, 5)
┌────────┬─────┬───────┬────────────┬────────────────┐
│ groups ┆ nrs ┆ count ┆ random_sum ┆ reversed names │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ u32 ┆ f64 ┆ list[str] │
╞════════╪═════╪═══════╪════════════╪════════════════╡
│ A ┆ 3 ┆ 2 ┆ 0.894213 ┆ ["ham", "foo"] │
│ C ┆ 0 ┆ 1 ┆ 0.533739 ┆ ["egg"] │
│ B ┆ 8 ┆ 2 ┆ 0.263315 ┆ [null, "spam"] │
└────────┴─────┴───────┴────────────┴────────────────┘
結果から分かるように、group_by
コンテキストで定義されたグループに対してすべての式が適用されます。標準の group_by
の他に、group_by_dynamic
および group_by_rolling
もグループ化コンテキストです。
-
このガイドの後の部分で説明されている List コンテキストと SQL コンテキストもありますが、簡単のため、ここでは対象外とします。 ↩