这一节展示如何在DataFrame
列上做一些基本操作, 比如基本的算数运算, 比较和其他通用操作。
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列中对应的值进行除法运算。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└─────────┴─────────┴──────────────┴──────────────┴──────────┴─────────┘
结果中高亮行, 当算术运算中将 null
作为其操作数之一的时候, 结果为 null
Polars 使用运算符重载,允许在表达式中使用语言的原生算术运算符,也可以使用相应的命名函数,如下面的代码片段所示:
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支持运算符重载或命名函数来进行比较。
使用运算符:
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└─────────┴──────────┴──────────────┴───────────────┴──────────┴──────────┘
使用命名函数:
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└─────────┴──────────┴──────────────┴───────────────┴──────────┴──────────┘
&
, |
, ~
(分别表示与、或、非)and_
, or_
, not_
使用运算符:
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_
:
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
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
函数来估算结果。
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
: 获取有关唯一值及其计数的更多信息,以结构体形式返回结果。
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└──────────────┘
如果只想知道唯一值有什么或者只想知道唯一值的具体数量,则可以通过unique
或unique_counts
函数实现:
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└────────┴───────────────┘
在unique
函数中使用了maintain_order
参数,它能够使结果顺序和原序列中出现的顺序一致,但存在一定的性能问题。
when
、then
、otherwise
, 可以链式调用。if
、else
。对于类似elif
的效果,Polars通过使用多个when
和then
来实现。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(...)
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└──────┴─────────┘
对于每个给定的值,只有当前面的谓词都对该值失败时,Polars 才会考虑链中更深层的替换表达式。
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└───────┴─────────┘