基本操作

这一节展示如何在DataFrame列上做一些基本操作, 比如基本的算数运算, 比较和其他通用操作。

数据准备

Python
1import polars as pl
2import numpy as np
3
4np.random.seed(42)  # For reproducibility.
5
6df = pl.DataFrame(
7    {
8        "nrs": [1, 2, 3, None, 5],
9        "names": ["foo", "ham", "spam", "egg", "spam"],
10        "random": np.random.rand(5),
11        "groups": ["A", "A", "B", "A", "B"],
12    }
13)
14print(df)
1shape: (5, 4)
2┌──────┬───────┬──────────┬────────┐
3│ nrs  ┆ names ┆ random   ┆ groups │
4│ ---  ┆ ---   ┆ ---      ┆ ---    │
5│ i64  ┆ str   ┆ f64      ┆ str    │
6╞══════╪═══════╪══════════╪════════╡
7│ 1    ┆ foo   ┆ 0.37454  ┆ A      │
8│ 2    ┆ ham   ┆ 0.950714 ┆ A      │
9│ 3    ┆ spam  ┆ 0.731994 ┆ B      │
10│ null ┆ egg   ┆ 0.598658 ┆ A      │
11│ 5    ┆ spam  ┆ 0.156019 ┆ B      │
12└──────┴───────┴──────────┴────────┘

基本运算

Polars支持相同长度的Series之间或Series和字面量之间的基本运算。当两者混合时, 字面量会被广播以匹配其所用Series的长度,也就是会将字面量和Series中的每个值都进行操作。如下示例:

  • pl.col("nrs")+5 : 将nrs这一列中的每一个值都+5。
  • pl.col("nrs")/pl.col("random") : 将nrs这一列中的每一个值和random列中对应的值进行除法运算。
Python
1result = df.select(
2    (pl.col("nrs") + 5).alias("nrs + 5"),
3    (pl.col("nrs") - 5).alias("nrs - 5"),
4    (pl.col("nrs") * pl.col("random")).alias("nrs * random"),
5    (pl.col("nrs") / pl.col("random")).alias("nrs / random"),
6    (pl.col("nrs") ** 2).alias("nrs ** 2"),
7    (pl.col("nrs") % 3).alias("nrs % 3"),
8)
9
10print(result)
1shape: (5, 6)
2┌─────────┬─────────┬──────────────┬──────────────┬──────────┬─────────┐
3│ nrs + 5 ┆ nrs - 5 ┆ nrs * random ┆ nrs / random ┆ nrs ** 2 ┆ nrs % 3 │
4│ ---     ┆ ---     ┆ ---          ┆ ---          ┆ ---      ┆ ---     │
5│ i64     ┆ i64     ┆ f64          ┆ f64          ┆ i64      ┆ i64     │
6╞═════════╪═════════╪══════════════╪══════════════╪══════════╪═════════╡
7│ 6       ┆ -4      ┆ 0.37454      ┆ 2.669941     ┆ 1        ┆ 1       │
8│ 7       ┆ -3      ┆ 1.901429     ┆ 2.103681     ┆ 4        ┆ 2       │
9│ 8       ┆ -2      ┆ 2.195982     ┆ 4.098395     ┆ 9        ┆ 0       │
10│ null    ┆ null    ┆ null         ┆ null         ┆ null     ┆ null    │
11│ 10      ┆ 0       ┆ 0.780093     ┆ 32.047453    ┆ 25       ┆ 2       │
12└─────────┴─────────┴──────────────┴──────────────┴──────────┴─────────┘
TIP

结果中高亮行, 当算术运算中将 null作为其操作数之一的时候, 结果为 null

Polars 使用运算符重载,允许在表达式中使用语言的原生算术运算符,也可以使用相应的命名函数,如下面的代码片段所示:

Python
1result = df.select(
2    (pl.col("nrs") + 5).alias("nrs + 5"),
3    (pl.col("nrs") - 5).alias("nrs - 5"),
4    (pl.col("nrs") * pl.col("random")).alias("nrs * random"),
5    (pl.col("nrs") / pl.col("random")).alias("nrs / random"),
6    (pl.col("nrs") ** 2).alias("nrs ** 2"),
7    (pl.col("nrs") % 3).alias("nrs % 3"),
8)
9
10# Python only:
11result_named_operators = df.select(
12    (pl.col("nrs").add(5)).alias("nrs + 5"),
13    (pl.col("nrs").sub(5)).alias("nrs - 5"),
14    (pl.col("nrs").mul(pl.col("random"))).alias("nrs * random"),
15    (pl.col("nrs").truediv(pl.col("random"))).alias("nrs / random"),
16    (pl.col("nrs").pow(2)).alias("nrs ** 2"),
17    (pl.col("nrs").mod(3)).alias("nrs % 3"),
18)
19
20print(result.equals(result_named_operators))
1True

比较

提示

与算数运算一样, Polars支持运算符重载或命名函数来进行比较。

使用运算符:

Python
1result = df.select(
2    (pl.col("nrs") > 1).alias("nrs > 1"),
3    (pl.col("nrs") >= 3).alias("nrs >= 3"),
4    (pl.col("random") < 0.2).alias("random < 0.2"),
5    (pl.col("random") <= 0.5).alias("random <= 0.5"),
6    (pl.col("nrs") != 1).alias("nrs != 1"),
7    (pl.col("nrs") == 1).alias("nrs == 1"),
8)
9print(result)
1shape: (5, 6)
2┌─────────┬──────────┬──────────────┬───────────────┬──────────┬──────────┐
3│ nrs > 1 ┆ nrs >= 3 ┆ random < 0.2 ┆ random <= 0.5 ┆ nrs != 1 ┆ nrs == 1 │
4│ ---     ┆ ---      ┆ ---          ┆ ---           ┆ ---      ┆ ---      │
5│ bool    ┆ bool     ┆ bool         ┆ bool          ┆ bool     ┆ bool     │
6╞═════════╪══════════╪══════════════╪═══════════════╪══════════╪══════════╡
7│ false   ┆ false    ┆ false        ┆ true          ┆ false    ┆ true     │
8│ true    ┆ false    ┆ false        ┆ false         ┆ true     ┆ false    │
9│ true    ┆ true     ┆ false        ┆ false         ┆ true     ┆ false    │
10│ null    ┆ null     ┆ false        ┆ false         ┆ null     ┆ null     │
11│ true    ┆ true     ┆ true         ┆ true          ┆ true     ┆ false    │
12└─────────┴──────────┴──────────────┴───────────────┴──────────┴──────────┘

使用命名函数:

Python
1result = df.select(
2    (pl.col("nrs").gt(1).alias("nrs > 1")), # greater than
3    (pl.col("nrs").ge(3)).alias("nrs >= 3"), # greater than or equal
4    (pl.col("random").lt(0.2)).alias("random < 0.2"),
5    (pl.col("random").le(0.5)).alias("random <= 0.5"),
6    (pl.col("nrs").ne(1)).alias("nrs != 1"),
7    (pl.col("nrs").eq(1)).alias("nrs == 1"),
8)
9print(result)
1shape: (5, 6)
2┌─────────┬──────────┬──────────────┬───────────────┬──────────┬──────────┐
3│ nrs > 1 ┆ nrs >= 3 ┆ random < 0.2 ┆ random <= 0.5 ┆ nrs != 1 ┆ nrs == 1 │
4│ ---     ┆ ---      ┆ ---          ┆ ---           ┆ ---      ┆ ---      │
5│ bool    ┆ bool     ┆ bool         ┆ bool          ┆ bool     ┆ bool     │
6╞═════════╪══════════╪══════════════╪═══════════════╪══════════╪══════════╡
7│ false   ┆ false    ┆ false        ┆ true          ┆ false    ┆ true     │
8│ true    ┆ false    ┆ false        ┆ false         ┆ true     ┆ false    │
9│ true    ┆ true     ┆ false        ┆ false         ┆ true     ┆ false    │
10│ null    ┆ null     ┆ false        ┆ false         ┆ null     ┆ null     │
11│ true    ┆ true     ┆ true         ┆ true          ┆ true     ┆ false    │
12└─────────┴──────────┴──────────────┴───────────────┴──────────┴──────────┘

布尔运算和按位运算

布尔运算

TIP
  • 运算符: &, |, ~(分别表示与、或、非)
  • 同名函数: and_, or_, not_

使用运算符:

Python
1result = df.select(
2    ((~pl.col("nrs").is_null()) & (pl.col("groups") == "A"))
3    .alias("number not null and group A"),
4
5    ((pl.col("random") < 0.5) | (pl.col("groups") == "B"))
6    .alias("random < 0.5 or group B"),
7)
8print(result)
1shape: (5, 2)
2┌─────────────────────────────┬─────────────────────────┐
3│ number not null and group A ┆ random < 0.5 or group B │
4│ ---                         ┆ ---                     │
5│ bool                        ┆ bool                    │
6╞═════════════════════════════╪═════════════════════════╡
7│ true                        ┆ true                    │
8│ true                        ┆ false                   │
9│ false                       ┆ true                    │
10│ false                       ┆ false                   │
11│ false                       ┆ true                    │
12└─────────────────────────────┴─────────────────────────┘

使用命名函数, 由于and,or,not是Python的关键字, 所以使用and_,or_,not_

Python
1result2 = df.select(
2    (pl.col("nrs").is_null().not_().and_(pl.col("groups") == "A"))
3    .alias("number not null and group A"),
4    ((pl.col("random") < 0.5).or_(pl.col("groups") == "B"))
5    .alias("random < 0.5 or group B"),
6)
7# 比较两个DataFrame是否相同
8print(result2.equals(result))
1True

位运算

Python
1result = df.select(
2    pl.col("nrs"),
3    (pl.col("nrs") & 6).alias("nrs & 6"),   # 与运算
4    (pl.col("nrs") | 6).alias("nrs | 6"),   # 或运算
5    (~pl.col("nrs")).alias("not nrs"),      # 取反
6    (pl.col("nrs") ^ 6).alias("nrs ^ 6"),   # 异或运算
7)
8
9print(result)
1shape: (5, 5)
2┌──────┬─────────┬─────────┬─────────┬─────────┐
3│ nrs  ┆ nrs & 6 ┆ nrs | 6 ┆ not nrs ┆ nrs ^ 6 │
4│ ---  ┆ ---     ┆ ---     ┆ ---     ┆ ---     │
5│ i64  ┆ i64     ┆ i64     ┆ i64     ┆ i64     │
6╞══════╪═════════╪═════════╪═════════╪═════════╡
7│ 1    ┆ 0       ┆ 7       ┆ -2      ┆ 7       │
8│ 2    ┆ 2       ┆ 6       ┆ -3      ┆ 4       │
9│ 3    ┆ 2       ┆ 7       ┆ -4      ┆ 5       │
10│ null ┆ null    ┆ null    ┆ null    ┆ null    │
11│ 5    ┆ 4       ┆ 7       ┆ -6      ┆ 3       │
12└──────┴─────────┴─────────┴─────────┴─────────┘

计数(唯一)值

n_unique可用于计算序列中唯一值的确切数量。 但是对于非常大的数据集, 此操作可能会非常缓慢。 如果对计算的值准确度不要求或者只需要近似值就行,那么可以使用基于HyperLogLog++approx_n_unique函数来估算结果。

Python
1import numpy as np
2import polars as pl
3# 数据准备, 生成10万个随机数
4long_df = pl.DataFrame({"numbers": np.random.randint(0, 100_000, 100_000)})
5
6result = long_df.select(
7    pl.col("numbers").n_unique().alias("n_unique"),
8    pl.col("numbers").approx_n_unique().alias("approx_n_unique"),
9)
10
11print(result)
1shape: (1, 2)
2┌──────────┬─────────────────┐
3│ n_unique ┆ approx_n_unique │
4│ ---      ┆ ---             │
5│ u32      ┆ u32             │
6╞══════════╪═════════════════╡
7│ 63218    ┆ 64141           │
8└──────────┴─────────────────┘

value_counts: 获取有关唯一值及其计数的更多信息,以结构体形式返回结果。

Python
1result = df.select(
2    pl.col("names").value_counts().alias("value_counts"),
3)
4
5print(result)
1shape: (4, 1)
2┌──────────────┐
3│ value_counts │
4│ ---          │
5│ struct[2]    │
6╞══════════════╡
7│ {"ham",1}    │
8│ {"egg",1}    │
9│ {"spam",2}   │
10│ {"foo",1}    │
11└──────────────┘

如果只想知道唯一值有什么或者只想知道唯一值的具体数量,则可以通过uniqueunique_counts函数实现:

Python
1result = df.select(
2    pl.col("names").unique(maintain_order=True).alias("unique"),
3    pl.col("names").unique_counts().alias("unique_counts"),
4)
5
6print(result)
1shape: (4, 2)
2┌────────┬───────────────┐
3│ unique ┆ unique_counts │
4│ ---    ┆ ---           │
5│ str    ┆ u32           │
6╞════════╪═══════════════╡
7│ foo    ┆ 1             │
8│ ham    ┆ 1             │
9│ spam   ┆ 2             │
10│ egg    ┆ 1             │
11└────────┴───────────────┘
TIP

unique函数中使用了maintain_order参数,它能够使结果顺序和原序列中出现的顺序一致,但存在一定的性能问题。

条件语句

TIP
  • 涉及到三个函数: whenthenotherwise, 可以链式调用。
  • 对比类似语句:ifelse。对于类似elif的效果,Polars通过使用多个whenthen来实现。
对比
1flag = 1
2
3# 其他编程语言,如Java
4if(flag==1){
5  ...
6}elif(flag==2){
7  ...
8}elif(flag==3){
9  ...
10}else{
11  ...
12}
13
14# 对比Polars代码
15pl.when(flag==1).then(...)
16  .when(flag==2).then(...)
17  .when(flag==3).then(...)
18  .otherwise(...)
Python
1result = df.select(
2    pl.col("nrs"),
3    pl.when(pl.col("nrs") % 2 == 1)
4    .then(3 * pl.col("nrs") + 1)
5    .when(pl.col("nrs") % 2 == 0)
6    .then(2 * pl.col("nrs"))
7    .otherwise(pl.col("nrs") // 2)
8    .alias("Collatz"),
9)
10
11print(result)
1shape: (5, 2)
2┌──────┬─────────┐
3│ nrs  ┆ Collatz │
4│ ---  ┆ ---     │
5│ i64  ┆ i64     │
6╞══════╪═════════╡
7│ 1    ┆ 4       │
8│ 2    ┆ 4       │
9│ 3    ┆ 10      │
10│ null ┆ null    │
11│ 5    ┆ 16      │
12└──────┴─────────┘
TIP

对于每个给定的值,只有当前面的谓词都对该值失败时,Polars 才会考虑链中更深层的替换表达式。

Python
1np.random.seed(42)  # For reproducibility.
2
3df = pl.DataFrame(
4    {
5        "nrs": [1, 2, 3, None, 2],
6        "names": ["foo", "ham", "spam", "egg", "spam"],
7        "random": np.random.rand(5),
8        "groups": ["A", "A", "B", "A", "B"],
9    }
10)
11
12result = df.select(
13    pl.col("names"),
14    pl.when(pl.col("groups") == "A")
15    .then(pl.lit("AAA"))
16    .when(pl.col("nrs") == 2)
17    .then(pl.lit("BBB"))
18    .otherwise(pl.lit("CCC"))
19    .alias("Collatz"),
20)
21
22print(result)
1shape: (5, 2)
2┌───────┬─────────┐
3│ names ┆ Collatz │
4│ ---   ┆ ---     │
5│ str   ┆ str     │
6╞═══════╪═════════╡
7│ foo   ┆ AAA     │
8│ ham   ┆ AAA     │
9│ spam  ┆ CCC     │
10│ egg   ┆ AAA     │
11│ spam  ┆ BBB     │
12└───────┴─────────┘