assign返回新dataframe而非就地修改,必须赋值或链式调用才生效;支持lambda但形参固定为df且作用域受限;多列应一次传字典而非多次assign;lambda共享原始df快照,不支持同次调用内列间依赖。

assign 本身不支持就地修改,它返回新 DataFrame;想“添加列”就得接回变量或链式调用,否则原数据不变。
assign 必须赋值或链式使用,否则新列直接丢弃
很多人写完 df.assign(new_col=df.a + df.b) 就以为列加好了,其实 df 没变。Pandas 的 assign 是纯函数式设计,返回副本,不修改原对象。
- 正确做法:显式赋值
df = df.assign(new_col=df.a + df.b) - 或嵌入链式操作:
df.assign(new_col=df.a + df.b).query("new_col > 0").head() - 错误写法:
df.assign(new_col=df.a + df.b)单独一行 → 新列计算完就被垃圾回收
lambda 形参名必须是 df,且不能引用外部变量(除非用闭包)
当需要复用已有列逻辑、又不想重复写 df.xxx 时,assign 支持传入 lambda,但形参固定为 df,且作用域受限。
- ✅ 正确:
df.assign(score=lambda df: df.math * 0.6 + df.chinese * 0.4) - ❌ 错误:
df.assign(score=lambda x: x.math * 0.6 + x.chinese * 0.4)(形参名不是df会报KeyError) - ⚠️ 注意:lambda 内无法直接访问外部定义的变量(如
weight_math = 0.6),需用默认参数捕获:df.assign(score=lambda df, w=weight_math: df.math * w + df.chinese * (1-w))
多个 assign 调用不如一次传入字典,否则性能下降明显
连续写多个 assign 看起来清晰,但每次都会复制整个 DataFrame,对大表很慢。
- ❌ 低效:
df.assign(a=df.x*2).assign(b=df.y**2).assign(c=df.a + df.b) - ✅ 高效:
df.assign(a=lambda df: df.x*2, b=lambda df: df.y**2, c=lambda df: df.a + df.b) - ⚠️ 注意:后定义的列(如
c)可依赖前面同次assign中定义的列(a,b),顺序执行,无需分步
assign 无法处理原地依赖(比如用新列做滚动计算),会报 KeyError
如果新列逻辑依赖自己刚生成的中间结果(例如先算差值再算累计和),assign 一次调用也搞不定——它内部按字典键顺序逐个计算,但所有 lambda 共享的是原始 df 快照,不是逐步更新后的视图。
- ❌ 失败示例:
df.assign(diff=lambda df: df.val.diff(), cumsum_diff=lambda df: df.diff.cumsum())→KeyError: 'diff' - ✅ 替代方案:先用
assign加基础列,再用assign第二次加衍生列;或改用df["diff"] = ...; df["cumsum_diff"] = df["diff"].cumsum() - ⚠️ 关键点:assign 的“链式可见性”只存在于单次调用内、且仅限于字典中已声明的键,不包括运行时动态产生的中间列
真正容易被忽略的是:assign 的 lambda 执行时,df 是原始快照,哪怕你在同一行写了十个新列,第 10 个 lambda 里也看不到前 9 个的结果——它们是并行计算的,不是流水线。这点和 R 的 dplyr::mutate 行为不同,得时刻绷着这根弦。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











