如何用Polars原生with_columns方法拆分字段并展开行?
问题:用Polars原生方法实现按规则拆分product字段并保留原字段
现有代码通过遍历DataFrame行的方式,将每行的product字段按规则拆分为多个product_split项,同时保留qty、price等其他字段。现希望使用Polars原生的with_columns类方法实现相同功能,避免行遍历。
原始实现代码:
import polars as pl import datetime as dt from dateutil.relativedelta import relativedelta def get_3_month_splits(product: str) -> list[str]: front, start_dt, total_m = product.rsplit('.', 2) start_dt = dt.datetime.strptime(start_dt, '%Y%m') total_m = int(total_m) return [f'{front}.{(start_dt+relativedelta(months=m)).strftime("%Y%m")}.3' for m in range(0, total_m, 3)] df = pl.DataFrame({ 'product': ['CHECK.GB.202403.12', 'CHECK.DE.202506.6', 'CASH.US.202509.12'], 'qty': [3, 6, -3], 'price': [100, 102, 95], }) print(df) df2 = pl.DataFrame([{'product_split': split} | d for d in df.iter_rows(named=True) for split in get_3_month_splits(d['product']) ]) print(df2)
原生Polars实现方案
可以通过Polars的字符串拆分、日期处理、列表生成和explode方法实现完全原生的处理,无需行遍历或依赖第三方库,性能更优:
分步实现版本(可读性强)
import polars as pl df = pl.DataFrame({ 'product': ['CHECK.GB.202403.12', 'CHECK.DE.202506.6', 'CASH.US.202509.12'], 'qty': [3, 6, -3], 'price': [100, 102, 95], }) df2 = ( df # 拆分product为前缀、起始年月、总月份数 .with_columns(pl.col("product").str.rsplit(".", 2).alias("parts")) .with_columns( front=pl.col("parts").list[0], start_dt=pl.col("parts").list[1].str.to_date("%Y%m"), total_m=pl.col("parts").list[2].cast(pl.Int32) ) # 生成每个3月间隔的product_split列表 .with_columns( product_split=pl.struct("front", "start_dt", "total_m").map_elements( lambda x: [ f"{x['front']}.{(x['start_dt'] + pl.duration(months=m)).strftime('%Y%m')}.3" for m in range(0, x['total_m'], 3) ], return_dtype=pl.List(pl.String) ) ) # 展开列表为多行,保留所有原字段 .explode("product_split") # 清理临时辅助列 .drop("parts", "front", "start_dt", "total_m") ) print(df2)
简洁实现版本(代码更紧凑)
import polars as pl df = pl.DataFrame({ 'product': ['CHECK.GB.202403.12', 'CHECK.DE.202506.6', 'CASH.US.202509.12'], 'qty': [3, 6, -3], 'price': [100, 102, 95], }) df2 = ( df .with_columns( product_split=pl.col("product").map_elements( lambda p: [ f"{front}.{(start_dt + pl.duration(months=m)).strftime('%Y%m')}.3" for m in range(0, total_m, 3) for front, start_dt_str, total_m_str in [p.rsplit(".", 2)] for start_dt in [pl.date(year=int(start_dt_str[:4]), month=int(start_dt_str[4:]), day=1)] for total_m in [int(total_m_str)] ], return_dtype=pl.List(pl.String) ) ) .explode("product_split") ) print(df2)
方案优势
- 原生API支持:完全使用Polars内置方法,避免Python层面的行遍历,大数据量下性能提升明显
- 无第三方依赖:替换
dateutil.relativedelta为Polars原生的pl.duration,减少外部依赖 - 向量化操作:利用Polars的向量化处理能力,比循环遍历效率更高
内容的提问来源于stack exchange,提问作者Phil-ZXX
相关产品推荐
相关产品推荐

