透视
pivot
函数的作用是透视表转换, 把某一列的列值, 转换为列名, 然后取其它的列, 聚合成新的单元格, 变成一个宽表
及时模式
我们通过几个例子来学习一下什么是pivot
Example1
在下面代码中我们想得到不同产品每个月的销量
1import polars as pl
2
3df = pl.DataFrame({
4 "month": ["Jan", "Jan", "Feb", "Feb"],
5 "product": ["apple", "banana", "apple", "banana"],
6 "sales": [100, 200, 150, 250]
7})
8
9print(df)
10res = df.pivot("product",index="month",values="sales")
11print(res)
通过第二个表格可以看到, product
列的值转换为了列名, sales
列的值转换为了列值, 此时我们就可以统计每个产品的一些数据了, 这就是pivot
的作用.
为了更清晰地表达, 可以看下面的图

1shape: (4, 3)
2┌───────┬─────────┬───────┐
3│ month ┆ product ┆ sales │
4│ --- ┆ --- ┆ --- │
5│ str ┆ str ┆ i64 │
6╞═══════╪═════════╪═══════╡
7│ Jan ┆ apple ┆ 100 │
8│ Jan ┆ banana ┆ 200 │
9│ Feb ┆ apple ┆ 150 │
10│ Feb ┆ banana ┆ 250 │
11└───────┴─────────┴───────┘
12shape: (2, 3)
13┌───────┬───────┬────────┐
14│ month ┆ apple ┆ banana │
15│ --- ┆ --- ┆ --- │
16│ str ┆ i64 ┆ i64 │
17╞═══════╪═══════╪════════╡
18│ Jan ┆ 100 ┆ 200 │
19│ Feb ┆ 150 ┆ 250 │
20└───────┴───────┴────────┘
Example2
Example1中的例子正好是2个月份, 每个月份都有apple/banana, 如果不是这种情况呢, 我们来看下面的代码
1import polars as pl
2
3df = pl.DataFrame({
4 "month": ["Jan","Jan","May", "Feb", "Feb"],
5 "product": ["apple","peach","banana", "apple", "banana"],
6 "sales": [100, 200, 400,150, 250]
7})
8
9print(df)
10res = df.pivot("product",index="month",values="sales")
11print(res)
可以看下面的结果, 第二个表中缺失的值使用null
进行填充
1shape: (5, 3)
2┌───────┬─────────┬───────┐
3│ month ┆ product ┆ sales │
4│ --- ┆ --- ┆ --- │
5│ str ┆ str ┆ i64 │
6╞═══════╪═════════╪═══════╡
7│ Jan ┆ apple ┆ 100 │
8│ Jan ┆ peach ┆ 200 │
9│ May ┆ banana ┆ 400 │
10│ Feb ┆ apple ┆ 150 │
11│ Feb ┆ banana ┆ 250 │
12└───────┴─────────┴───────┘
13shape: (3, 4)
14┌───────┬───────┬───────┬────────┐
15│ month ┆ apple ┆ peach ┆ banana │
16│ --- ┆ --- ┆ --- ┆ --- │
17│ str ┆ i64 ┆ i64 ┆ i64 │
18╞═══════╪═══════╪═══════╪════════╡
19│ Jan ┆ 100 ┆ 200 ┆ null │
20│ May ┆ null ┆ null ┆ 400 │
21│ Feb ┆ 150 ┆ null ┆ 250 │
22└───────┴───────┴───────┴────────┘
聚合参数
上面的例子略显简单, 因为每个月份都只有一个apple/banana, 那如果有多个呢, 不可能变成多个apple列和多个banana列, 此时可以指定怎么选择
Polars提供了以下几种聚合方式:
- first
- last
- sum
- min
- max
- mean
- median
- len
我们使用sum
来说明如何使用
1import polars as pl
2
3df = pl.DataFrame({
4 "month": ["Jan", "Jan","Jan", "Feb", "Feb","Feb"],
5 "product": ["apple", "banana","apple", "apple", "banana","banana"],
6 "sales": [100, 200,300, 150, 250,400]
7})
8
9print(df)
可以看到Jan有两个apple, Feb有两个banana
1shape: (6, 3)
2┌───────┬─────────┬───────┐
3│ month ┆ product ┆ sales │
4│ --- ┆ --- ┆ --- │
5│ str ┆ str ┆ i64 │
6╞═══════╪═════════╪═══════╡
7│ Jan ┆ apple ┆ 100 │
8│ Jan ┆ banana ┆ 200 │
9│ Jan ┆ apple ┆ 300 │
10│ Feb ┆ apple ┆ 150 │
11│ Feb ┆ banana ┆ 250 │
12│ Feb ┆ banana ┆ 400 │
13└───────┴─────────┴───────┘
接下来指定参数aggregate_function="sum"
, 可以看到最后嗯结果中400是100+300来的, 650是250+400来的
1res = df.pivot("product",index="month",values="sales", aggregate_function="sum")
2print(res)
1shape: (2, 3)
2┌───────┬───────┬────────┐
3│ month ┆ apple ┆ banana │
4│ --- ┆ --- ┆ --- │
5│ str ┆ i64 ┆ i64 │
6╞═══════╪═══════╪════════╡
7│ Jan ┆ 400 ┆ 200 │
8│ Feb ┆ 150 ┆ 650 │
9└───────┴───────┴────────┘
多列透视
刚才的例子有点简单, 我们只考虑了一列, 下面是多列透视
1df = pl.DataFrame({
2 "A":["a","b","a","a","b","a"],
3 "B":[1,3,5,1,3,5],
4 "C":["C","C","C","D","D","D"],
5 "value":[10,11,12,2,4,6]
6})
7print(df)
1shape: (6, 4)
2┌─────┬─────┬─────┬───────┐
3│ A ┆ B ┆ C ┆ value │
4│ --- ┆ --- ┆ --- ┆ --- │
5│ str ┆ i64 ┆ str ┆ i64 │
6╞═════╪═════╪═════╪═══════╡
7│ a ┆ 1 ┆ C ┆ 10 │
8│ b ┆ 3 ┆ C ┆ 11 │
9│ a ┆ 5 ┆ C ┆ 12 │
10│ a ┆ 1 ┆ D ┆ 2 │
11│ b ┆ 3 ┆ D ┆ 4 │
12│ a ┆ 5 ┆ D ┆ 6 │
13└─────┴─────┴─────┴───────┘
我们想把C
这一列转换为列名, 然后按照A和B
作为唯一行索引, 最后的值是value
1res = df.pivot(
2 on=["C"], # on指定透视列, 就是把这一列的值给展开成列名
3 index=["A","B"], # index指定行索引
4 values=["value"]) # values指定透视列的值
5print(res)
观察上面的数据, 有:
- (a,1) -> (C,10),(D,2)
- (b,3) -> (C,11),(D,4)
- (a,5) -> (C,12),(D,6)
然后把箭头右边的数据顺时针旋转90度, 就可以得到下面的结果, 耐心比对, 就知道透视是如何工作的了
1shape: (3, 4)
2┌─────┬─────┬─────┬─────┐
3│ A ┆ B ┆ C ┆ D │
4│ --- ┆ --- ┆ --- ┆ --- │
5│ str ┆ i64 ┆ i64 ┆ i64 │
6╞═════╪═════╪═════╪═════╡
7│ a ┆ 1 ┆ 10 ┆ 2 │
8│ b ┆ 3 ┆ 11 ┆ 4 │
9│ a ┆ 5 ┆ 12 ┆ 6 │
10└─────┴─────┴─────┴─────┘
惰性模式
Polars的LazyFrame
在收集数据之前需要静态了解计算的模式, 但是由于数据透视表的输出模式依赖于数据(不能直接确定), 因此在不运行查询的情况下无法确定模式
如果想在惰性模式下使用pivot
, 就需要先collect
, 或者在知道哪些值是唯一值得情况下使用group_by
和agg
1res = (df
2 .lazy().collect()
3 .pivot("product",index="month",values="sales",aggregate_function="sum")
4 .lazy()
5 .collect()
6)
7print(res)
1shape: (2, 3)
2┌───────┬───────┬────────┐
3│ month ┆ apple ┆ banana │
4│ --- ┆ --- ┆ --- │
5│ str ┆ i64 ┆ i64 │
6╞═══════╪═══════╪════════╡
7│ Jan ┆ 400 ┆ 200 │
8│ Feb ┆ 150 ┆ 650 │
9└───────┴───────┴────────┘