如何在Pandas函数式管道中实现类似R中across的多列操作?
Pandas中实现类似R
dplyr::across的函数式列处理方案 问题概述
能否在Pandas中遵循函数式管道原则(无副作用、结果仅依赖输入参数、保留所有原有列)的前提下,对指定列批量应用函数,替代多次调用assign的繁琐写法,找到R语言dplyr::across的等效实现?
答案是肯定的,以下是几种简洁且符合要求的实现方式:
原冗余实现(多次调用assign)
虽然可行,但写法重复繁琐:
import pandas as pd df = ( pd.DataFrame({ "column_a":[0,3,4,2,1], "column_b":[1,2,4,5,18], "column_c":[2,4,25,25,26], "column_d":[2,4,-1,5,2], "column_e":[-1,-7,-8,-9,3] }) .assign(column_a=lambda df:df["column_a"]+20) .assign(column_c=lambda df:df["column_c"]+20) .assign(column_e=lambda df:df["column_e"]/3) .assign(column_b=lambda df:df["column_b"]/3) ) print(df)
对应R语言across的简洁写法:
library(dplyr) df <- tibble( column_a = c(0,3,4,2,1), column_b = c(1,2,4,5,18), column_c = c(2,4,25,25,26), column_d = c(2,4,-1,5,2), column_e = c(-1,-7,-8,-9,3) ) %>% mutate(across(c(column_a,column_c),~.x + 20), across(c(column_e,column_b),~.x / 3))
方法1:assign配合字典批量处理
通过向assign传递字典参数,一次完成多组列的不同函数处理,完全符合函数式管道要求:
import pandas as pd df = ( pd.DataFrame({ "column_a":[0,3,4,2,1], "column_b":[1,2,4,5,18], "column_c":[2,4,25,25,26], "column_d":[2,4,-1,5,2], "column_e":[-1,-7,-8,-9,3] }) .assign( **{col: lambda df, col=col: df[col] + 20 for col in ["column_a", "column_c"]}, **{col: lambda df, col=col: df[col] / 3 for col in ["column_b", "column_e"]} ) ) print(df)
- 注:
col=col用于在lambda闭包中捕获当前循环的列名,避免闭包延迟绑定导致的列名错误。
方法2:自定义across风格函数
封装一个贴近R语法的自定义函数,通过pipe在管道中调用,语义更清晰:
import pandas as pd def across(df, cols, func): # 对指定列应用函数,返回新DataFrame,无副作用 return df.assign(**{col: func(df[col]) for col in cols}) df = ( pd.DataFrame({ "column_a":[0,3,4,2,1], "column_b":[1,2,4,5,18], "column_c":[2,4,25,25,26], "column_d":[2,4,-1,5,2], "column_e":[-1,-7,-8,-9,3] }) .pipe(across, cols=["column_a", "column_c"], func=lambda x: x + 20) .pipe(across, cols=["column_b", "column_e"], func=lambda x: x / 3) ) print(df)
方法3:transform批量处理同函数列
如果多列需要应用相同函数,可以用transform生成处理后的列,再通过assign合并:
import pandas as pd df = ( pd.DataFrame({ "column_a":[0,3,4,2,1], "column_b":[1,2,4,5,18], "column_c":[2,4,25,25,26], "column_d":[2,4,-1,5,2], "column_e":[-1,-7,-8,-9,3] }) .assign(**df[["column_a", "column_c"]].transform(lambda x: x + 20)) .assign(**df[["column_b", "column_e"]].transform(lambda x: x / 3)) ) print(df)
以上所有方法均满足:
- 无副作用:所有操作返回新的DataFrame,不修改原始输入数据
- 结果仅依赖参数:处理逻辑完全由输入的列名和函数决定
- 保留所有列:未指定的列(如
column_d)会被完整保留
内容的提问来源于stack exchange,提问作者Arnaud Feldmann
相关产品推荐
相关产品推荐

