分组和简单聚合

LazyFrame经过精心设计,更便于使用:建议调用dataframe.lazy()转换为LazyFrame后再进行聚合。

术语含义
简单聚合输入若干数据,根据某种算法返回一个值。例如:min,max,mean,head,tail等等都属于聚合函数。
分组顾名思义,将数据根据指定字段进行分组,后续的聚合操作将对每个组调用一次聚合函数。

实战1

观察示例数据employee_df,其中保存了3位员工近4个月的业绩。

#![allow(unused)]
fn main() {
let mut employee_df: DataFrame = df!("姓名"=> ["老李", "老李", "老李", "老李", "老张", "老张", "老张", "老张", "老王", "老王", "老王", "老王"],
"员工ID"=> ["员工01", "员工01", "员工01", "员工01", "员工02", "员工02", "员工02", "员工02", "员工03", "员工03", "员工03", "员工03"],
"日期"=> ["8月", "9月", "10月", "11月", "8月", "9月", "10月", "11月", "8月", "9月", "10月", "11月"],
"业绩"=> [83, 24, 86, 74, 89, 59, 48, 79, 51, 71, 44, 90])?;
}

现在的需求是计算每个人所有月份业绩平均值,并且计算出每个人业绩大于70的次数。数据需要根据员工ID进行分组然后聚合。

#![allow(unused)]
fn main() {
   let res = employee_df.lazy().group_by(["员工ID","姓名"]) //group_by执行后可能会打乱行序,group_by_stable可以保留原行序。
            .agg([
                col("业绩").mean().alias("平均业绩"),
                col("业绩").gt(70).cast(DataType::Int32).sum().alias("大于70次数")
            ]).collect()?;
        
    println!("{}",res);
}

polars通过表达式提供的聚合函数能完成简单聚合。 简单聚合演示

实战2

计算每个月份获得业务第一名,第二名的人及其对应业绩。

#![allow(unused)]
fn main() {
    let res = employee_df.lazy().group_by(["日期"]) //group_by执行后可能会打乱行序,group_by_stable可以保留原行序。
    .agg([
        col("员工ID"),
        col("业绩"),
        col("业绩").rank(RankOptions::default(),None).alias("rank"),
    ]) //step 1
    .explode([col("员工ID"),col("业绩"),col("rank")]) //step2
    .filter(col("rank").gt_eq(2))  //step3
    .collect()?
    ;
}

Step1

#![allow(unused)]
fn main() {
.group_by(["日期"]).agg([
        col("员工ID"),
        col("业绩"),
        col("业绩").rank(RankOptions::default(),None).alias("rank"),
    ])
}

实战2中调用的agg聚合操作将组内的多个结果包裹进一个list。

shape: (4, 4)
┌──────┬────────────────────────────────┬──────────────┬───────────┐
│ 日期 │ 员工ID                         ┆ 业绩          ┆ rank      │
│ ---  ┆ ---                            ┆ ---          ┆ ---       │
│ str  ┆ list[str]                      ┆ list[i32]    ┆ list[u32] │
╞══════╪════════════════════════════════╪══════════════╪═══════════╡
│ 10月 ┆ ["员工01", "员工02", "员工03"]  ┆ [86, 48, 44] ┆ [3, 2, 1] │
│ 8月  ┆ ["员工01", "员工02", "员工03"]  ┆ [83, 89, 51] ┆ [2, 3, 1] │
│ 11月 ┆ ["员工01", "员工02", "员工03"]  ┆ [74, 79, 90] ┆ [1, 2, 3] │
│ 9月  ┆ ["员工01", "员工02", "员工03"]  ┆ [24, 59, 71] ┆ [1, 2, 3] │
└──────┴────────────────────────────────┴──────────────┴───────────┘

step2

.explode([col("员工ID"),col("业绩"),col("rank")]) 该调用将被list包裹的值解包。

#![allow(unused)]
fn main() {
shape: (12, 4)
┌──────┬────────┬──────┬──────┐
│ 日期 ┆ 员工ID ┆ 业绩 ┆ rank │
│ ---  ┆ ---    ┆ ---  ┆ ---  │
│ str  ┆ str    ┆ i32  ┆ u32  │
╞══════╪════════╪══════╪══════╡
│ 8月  ┆ 员工01 ┆ 83   ┆ 2    │
│ 8月  ┆ 员工02 ┆ 89   ┆ 3    │
│ 8月  ┆ 员工03 ┆ 51   ┆ 1    │
│ 10月 ┆ 员工01 ┆ 86   ┆ 3    │
│ 10月 ┆ 员工02 ┆ 48   ┆ 2    │
│ …    ┆ …      ┆ …    ┆ …    │
│ 11月 ┆ 员工02 ┆ 79   ┆ 2    │
│ 11月 ┆ 员工03 ┆ 90   ┆ 3    │
│ 9月  ┆ 员工01 ┆ 24   ┆ 1    │
│ 9月  ┆ 员工02 ┆ 59   ┆ 2    │
│ 9月  ┆ 员工03 ┆ 71   ┆ 3    │
└──────┴────────┴──────┴──────┘
}