基本操作

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

数据准备

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和Literals之间的基本运算.
  • 当两者混合时, 将广播Literals已匹配它们正在使用的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└─────────┴─────────┴──────────────┴──────────────┴──────────┴─────────┘
TIP

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

  • Polars使用运算符重载允许在表达式中使用语言的原生算数运算符
  • 当然也可以使用相应的命名函数
    算数运算符
    • .add()
    • .sub()
    • .mul()
    • .truediv()
    • .pow()
    • .mod()
1# Python only:
2result_named_operators = df.select(
3    (pl.col("nrs").add(5)).alias("nrs + 5"),
4    (pl.col("nrs").sub(5)).alias("nrs - 5"),
5    (pl.col("nrs").mul(pl.col("random"))).alias("nrs * random"),
6    (pl.col("nrs").truediv(pl.col("random"))).alias("nrs / random"),
7    (pl.col("nrs").pow(2)).alias("nrs ** 2"),
8    (pl.col("nrs").mod(3)).alias("nrs % 3"),
9)
10
11print(result.equals(result_named_operators))

比较

提示

与算数运算一样, 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└─────────┴──────────┴──────────────┴───────────────┴──────────┴──────────┘

布尔运算和按位运算

回顾下数据

布尔运算

TIP
  • 运算符: &, |, ~
  • 同名函数: 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可用于计算序列中唯一值的确切数量. 但是对于非常大的数据集, 此操作可能会非常缓慢.

在这种情况下, 如果近似值足够好, 可以使用approx_n_unique基于HyperLogLog++算法的函数来估算结果

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│ 63217    ┆ 63699           │
8└──────────┴─────────────────┘

value_counts: 获取有关唯一值及其计数的更多信息, df依然为最初提供的数据

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

unique,unique_counts: 让每个值只出现一次, 并且获取每个值出现的次数

1result = df.select(
2    pl.col("names").unique().alias("unique"),
3    pl.col("names").unique_counts().alias("unique_counts"),
4)
5
6print(result)

name这一列有两行spam, 其余都是一行

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

条件语句

NOTE
  • Polars通过函数支持条件语句, 有三个函数: when,then,otherwise, 可以链式调用来完成自己的需求
  • Polars不会"只执行满足条件的表达式", 而是: "并行执行所有表达式, 然后根据where条件决定用哪个结果"

比如想新增一列color, groups值为A的, 就为red, 否则就为blue

下面的then()中如果是字符串, 会被解析为一个列, 如果想填充一个值, 请使用pl.lit()

1df.select(
2    pl.col("groups"),
3    pl.when(pl.col("groups")=="A").then(pl.lit("red")).otherwise(pl.lit("blue"))
4)
1shape: (5, 2)
2┌────────┬─────────┐
3│ groups ┆ literal │
4│ ---    ┆ ---     │
5│ str    ┆ str     │
6╞════════╪═════════╡
7│ A      ┆ red     │
8│ A      ┆ red     │
9│ B      ┆ blue    │
10│ A      ┆ red     │
11│ B      ┆ blue    │
12└────────┴─────────┘
TIP

Polars是条件选择, 而不是条件执行

意思不是: "如果条件满足, 就执行该表达式, 否则就忽略"

而是: "并行执行所有表达式, 然后根据where条件决定用哪个结果"