上下文

表达式几乎可以在任何地方使用,但是表达式需要一个上下文,这些上下文包括:

  • 选择: df.select([..])
  • 分组集合: df.groupby(..).agg([..])
  • 横向堆叠(hstack) 或者增加列: df.with_columns([..])

语法糖

需要上下文的主要原因是:即使在即时执行中,你也在使用 Polars 的延迟执行API。 比如如下代码实例:

df.groupby("foo").agg([pl.col("bar").sum()])

去掉语法糖后:

(df.lazy().groupby("foo").agg([pl.col("bar").sum()])).collect()

这种设计可以让 Polars 把表达式推送给查询引擎,进行一些优化和缓存操作。

select 上下文

select 上下文中,选择操作是按照列进行的。在选择向下文的表达式必须要返回 Series 并且这些 Series 需要有相同的长度或者长度为1。

一个长度为 1 的 Series 会将 DataFrame 的一列赋予完全一样的值(这个值来自Series)。 注意,select 可能会返回一个新的列,这个列可能是一些聚合的结果、一些表达式的组合或者常量。

选择上下文

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)
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   │
└─────┴───────┴────────────┴────────┘

添加列

采用 with_columnsDataFrame 增加列同样也是选择上下文。

df = df.with_columns(
    [
        pl.sum("nrs").alias("nrs_sum"),
        pl.col("random").count().alias("count"),
    ]
)
print(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     │
└──────┴───────┴──────────┴────────┴─────────┴───────┘

Groupby 上下文

groupby 上下文中的表达式主要作用域分组上,因此他们会返回任意长度(每个组可能有不同数量的成员)。

out = df.groupby("groups").agg(
    [
        pl.sum("nrs"),  # 通过groups列对nrs求和
        pl.col("random").count().alias("count"),  # 记录组数
        # 如果name != null记录random列的和
        pl.col("random").filter(pl.col("names").is_not_null()).sum().suffix("_sum"),
        pl.col("names").reverse().alias(("reversed names")),
    ]
)
print(out)
shape: (3, 5)
┌────────┬─────┬───────┬────────────┬────────────────┐
│ groups ┆ nrs ┆ count ┆ random_sum ┆ reversed names │
│ ---    ┆ --- ┆ ---   ┆ ---        ┆ ---            │
│ str    ┆ i64 ┆ u32   ┆ f64        ┆ list[str]      │
╞════════╪═════╪═══════╪════════════╪════════════════╡
│ C      ┆ 0   ┆ 1     ┆ 0.533739   ┆ ["egg"]        │
│ A      ┆ 3   ┆ 2     ┆ 0.894213   ┆ ["ham", "foo"] │
│ B      ┆ 8   ┆ 2     ┆ 0.263315   ┆ [null, "spam"] │
└────────┴─────┴───────┴────────────┴────────────────┘

除了标准的 groupby,还有 groupby_dynamicgroupby_rolling 也属于 Groupby 上下文。