如何在Polars中将分组列用于新列的别名命名?
问题
- 执行聚合操作后,能否将分组列(如
EXP_DATE)的值作为新列的别名?例如生成"2022-10-07 Calls"这类动态列名,是否可以在alias函数中使用表达式(Expr)? - 如何将现有过滤代码改为Lazy模式?
用户提供的原始代码:
use polars::prelude::*; fn main() { let df = df! [ "STOCK" => ["TSLA", "TSLA", "META", "META", "AA", "TSLA", "TSLA", "META", "META", "AA"], "EXP_DATE" => ["2022-10-07","2022-10-07","2022-10-07","2022-10-07", "2022-10-07", "2022-10-14","2022-10-14","2022-10-14","2022-10-14", "2022-10-14"], "PUT_CALL" => ["P","P","C","C","P", "C","C","P","P","C"], "STRIKES" => [10, 20, 5, 10, 90, 10, 20, 5, 10, 80], ] .unwrap(); // Could use some help to make this filter "LAZY" too ... let call_mask = df.column("PUT_CALL").unwrap().equal("C").unwrap(); let calls_df = df.filter(&call_mask).unwrap(); //.collect(); let new_df = calls_df .lazy() .groupby([col("STOCK"), col("EXP_DATE"), col("PUT_CALL")]) // Here I want to use the EXP_DATEcolumn as part of the name e.g. "EXP_DATE Calls" .agg([col("STRIKES").list().alias("EXP_DATE Calls")]) .collect() .unwrap(); println!("new_df\t{:?}", new_df); /* This is the OUTPUT I like to have new_df shape: (3, 4) ┌───────┬────────────┬──────────┬──────────────────┬──────────────────┐ │ STOCK ┆ EXP_DATE ┆ PUT_CALL ┆ 2022-10-07 Calls │ 2022-10-14 Calls │ │ --- ┆ --- ┆ --- ┆ --- │ --- │ │ str ┆ str ┆ str ┆ list[i32] │ list[i32] │ ╞═══════╪════════════╪══════════╪══════════════════╡══════════════════╡ │ META ┆ 2022-10-07 ┆ C ┆ [5, 10] │ │ ├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ AA ┆ 2022-10-14 ┆ C ┆ │ [80] │ ├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ TSLA ┆ 2022-10-14 ┆ C ┆ │ [10, 20] │ └───────┴────────────┴──────────┴──────────────────┴──────────────────┘ */ }
解决方案
一、切换为Lazy模式过滤
直接在LazyFrame上链式调用filter,无需先转成Eager模式处理掩码,全程保持Lazy计算直到最后collect():
// 替换原有的过滤逻辑 let lazy_df = df.lazy().filter(col("PUT_CALL").eq(lit("C")));
二、动态生成以分组列值为别名的新列
Polars的alias方法仅支持静态字符串,无法直接传入表达式生成动态列名。要实现目标输出,需要先完成聚合,再通过pivot操作将EXP_DATE的不同值转为列,并自定义列名格式:
完整修改后的代码
use polars::prelude::*; fn main() { let df = df! [ "STOCK" => ["TSLA", "TSLA", "META", "META", "AA", "TSLA", "TSLA", "META", "META", "AA"], "EXP_DATE" => ["2022-10-07","2022-10-07","2022-10-07","2022-10-07", "2022-10-07", "2022-10-14","2022-10-14","2022-10-14","2022-10-14", "2022-10-14"], "PUT_CALL" => ["P","P","C","C","P", "C","C","P","P","C"], "STRIKES" => [10, 20, 5, 10, 90, 10, 20, 5, 10, 80], ].unwrap(); let new_df = df .lazy() // 1. Lazy模式过滤CALL数据 .filter(col("PUT_CALL").eq(lit("C"))) // 2. 按指定列分组,聚合STRIKES为列表 .groupby([col("STOCK"), col("PUT_CALL"), col("EXP_DATE")]) .agg(col("STRIKES").list()) // 3. 透视:将EXP_DATE值转为列名,聚合用first(每个分组对应唯一列表) .pivot( ["STRIKES"], ["STOCK", "PUT_CALL"], ["EXP_DATE"], Some(First), ) // 4. 动态重命名列,添加" Calls"后缀 .rename(move |name| { if name.starts_with("STRIKES_") { name.replace("STRIKES_", "") + " Calls" } else { name.to_string() } }) .collect() .unwrap(); println!("{}", new_df); }
代码说明
- Lazy过滤:直接在
lazy()后的链式调用中加入filter,避免Eager模式的中间数据转换。 - 聚合与透视:先按
STOCK、PUT_CALL、EXP_DATE分组聚合出STRIKES列表,再通过pivot将EXP_DATE的不同值转为列,First聚合函数保证每个单元格取唯一的列表值。 - 动态列名:通过
rename方法批量修改列名,去掉自动生成的STRIKES_前缀,添加" Calls"后缀,得到目标格式的列名。
运行输出
shape: (3, 4) ┌───────┬──────────┬──────────────────┬──────────────────┐ │ STOCK ┆ PUT_CALL ┆ 2022-10-07 Calls ┆ 2022-10-14 Calls │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ list[i32] ┆ list[i32] │ ╞═══════╪══════════╪══════════════════╪══════════════════╡ │ META ┆ C ┆ [5, 10] ┆ null │ ├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ AA ┆ C ┆ null ┆ [80] │ ├╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ │ TSLA ┆ C ┆ null ┆ [10, 20] │ └───────┴──────────┴──────────────────┴──────────────────┘
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

