来自 Pandas

如果你很熟悉Pandas,那么你只需要知道一件事:

polars != pandas

如果你的Polars代码写起来很像Pandas,程序也许可以运行,但是很有可能会慢于它本该有的速度。

下面我们就通过几段经典Pandas代码来对比同样功能的Polars代码。

列运算

Pandas

# 以下代码是顺序执行的
df["a"] = df["b"] * 10
df["c"] = df["b"] * 100

Polars

# 以下代码是并发执行的
df.with_columns([
    (pl.col("b") * 10).alias("a"),
    (pl.col("b") * 100).alias("c"),
])

基于判定的列运算

Pandas

df.loc[df["c"] == 2, "a"] = df.loc[df["c"] == 2, "b"]

Polars

df.with_column(
    pl.when(pl.col("c") == 2)
    .then(pl.col("b"))
    .otherwise(pl.col("a")).alias("a")
)

注意,Polars的方式更“干净”,因而原始DataFrame中的数据并没有被修改。并且,mask(掩膜)也不像在Pandas中那样被计算了两次。

当然,你可以在Pandas中防止原始DataFrame中的数据在这一步被修改,但这需要借助临时变量。

另外,Polars能并行计算每一个 if -> then -> otherwise的分支。当分支的计算复杂度提高时,就能体现并行计算的优势了。

筛选

Pandas

df.loc[(df['sqft_living'] > 2500) & (df['price'] < 300000)]

Polars

df.filter(
    (pl.col("m2_living") > 2500) & (pl.col("price") < 300000)
)

PS: 这部分内容还在建设中,内容有缺少?欢迎提交PR!

没有索引列

根本不需要索引列!没有索引列会让处理变得更简单。如果你不相信来说服我们吧!

Pandas重塑

Pandas文档中演示了一种聚合操作 transform(重塑):

Pandas

df = pd.DataFrame({
    "c": [1, 1, 1, 2, 2, 2, 2],
    "type": ["m", "n", "o", "m", "m", "n", "n"]
})

df["size"] = df.groupby("c")["type"].transform(len)

使用Pandas 要先聚合"c"列、截取出"type"列、计算组的长度,最后将结果拼接回原始DataFrame中。

其结果是:

   c type size
0  1    m    3
1  1    n    3
2  1    o    3
3  2    m    4
4  2    m    4
5  2    n    4
6  2    n    4

Polars

Polars中可以用 窗口 函数来达到相同的目的。

df.select([
    pl.all(),
    pl.col("type").count().over("c").alias("size")
])
shape: (7, 3)
┌─────┬──────┬──────┐
│ c   ┆ type ┆ size │
│ --- ┆ ---  ┆ ---  │
│ i64 ┆ str  ┆ u32  │
╞═════╪══════╪══════╡
│ 1   ┆ m    ┆ 3    │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ 1   ┆ n    ┆ 3    │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ 1   ┆ o    ┆ 3    │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ 2   ┆ m    ┆ 4    │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ 2   ┆ m    ┆ 4    │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ 2   ┆ n    ┆ 4    │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┤
│ 2   ┆ n    ┆ 4    │
└─────┴──────┴──────┘

因为我们可以将所有的操作放在一个语句中,因此结合多个窗口函数,甚至结合不同的组都是可以的!

Polars会将应用于相同组的窗口函数表达式缓存,所以将多个表达式入在一个select语句中既方便优雅。

例如:

df.select([
    pl.all(),
    pl.col("c").count().over("c").alias("size"),
    pl.col("c").sum().over("type").alias("sum"),
    pl.col("c").reverse().over("c").flatten().alias("reverse_type")
])
shape: (7, 5)
┌─────┬──────┬──────┬─────┬──────────────┐
│ c   ┆ type ┆ size ┆ sum ┆ reverse_type │
│ --- ┆ ---  ┆ ---  ┆ --- ┆ ---          │
│ i64 ┆ str  ┆ u32  ┆ i64 ┆ i64          │
╞═════╪══════╪══════╪═════╪══════════════╡
│ 1   ┆ m    ┆ 3    ┆ 5   ┆ 2            │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1   ┆ n    ┆ 3    ┆ 5   ┆ 2            │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 1   ┆ o    ┆ 3    ┆ 1   ┆ 2            │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ m    ┆ 4    ┆ 5   ┆ 2            │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ m    ┆ 4    ┆ 5   ┆ 1            │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ n    ┆ 4    ┆ 5   ┆ 1            │
├╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ 2   ┆ n    ┆ 4    ┆ 5   ┆ 1            │
└─────┴──────┴──────┴─────┴──────────────┘