assign不能直接用df['a']赋值是因为其表达式在执行时未绑定当前dataframe上下文,需用lambda函数(如b=lambda x: x['a']+1)或字典方式传入;支持一次性添加多列,但各列表达式均只读原始df,不可相互引用。

assign 为什么不能直接用变量名赋值
因为 assign 要求传入的是函数或字典,不是普通变量。比如你想基于 df['a'] 创建新列 b,写成 df.assign(b = df['a'] + 1) 看似合理,但会报错 KeyError: 'a' —— 这是因为 assign 在执行时还没拿到当前 DataFrame 的上下文,无法直接引用已有列名。
正确做法是用 lambda 函数或字典加函数:
- ✅ 推荐:用 lambda,
df.assign(b=lambda x: x['a'] + 1) - ✅ 也行:用命名函数,
df.assign(b=lambda x: x['a'].apply(lambda v: v * 2)) - ❌ 错误:直接写
df.assign(b=df['a'] + 1)(此时df['a']是 Series,但assign没有绑定到当前 df 实例)
多个新列怎么一次 assign 完
assign 支持一次性传入多个键值对,但所有表达式都共享同一个输入 DataFrame(即 lambda 的 x),所以可以复用中间计算结果,避免重复取列。
比如想同时加 b 和 c,且 c 依赖 b:
df.assign(
b=lambda x: x['a'] * 2,
c=lambda x: x['b'] + 10 # 注意:这里能用 'b',因为 assign 是按顺序“假想”生成列的,但实际仍只读原始 df;所以这行其实会报错!
)
⚠️ 实际上,assign 所有右侧表达式都只读原始 DataFrame,不感知彼此新增的列。所以上面 x['b'] 会 KeyError。正确写法是:
- 要么拆成两次
assign链:df.assign(b=lambda x: x['a']*2).assign(c=lambda x: x['b']+10) - 要么在单次中复用计算:
df.assign(b=lambda x: x['a']*2, c=lambda x: x['a']*2 + 10) - 或者提前算好中间量:
df.assign(**{'b': lambda x: x['a']*2, 'c': lambda x: (x['a']*2)+10})
assign 链式调用时如何插入条件逻辑
纯 lambda 不好写 if/else 多分支,但可以用 numpy.where 或 pandas.Series.map,避免中断链式。
例如根据 a 列值分三档生成 level 列:
- ✅ 推荐用
np.where嵌套:df.assign(level=lambda x: np.where(x['a'] > 10, 'high', np.where(x['a'] > 5, 'mid', 'low'))) - ✅ 或用
pd.cut:df.assign(level=lambda x: pd.cut(x['a'], bins=[0,5,10,100], labels=['low','mid','high'])) - ❌ 不要写
if x['a'] > 10: ...—— lambda 里不能用语句,只能用表达式
注意:如果逻辑特别复杂(比如要调用自定义函数并带状态),就该退出链式,先算出 Series 再 assign,否则可读性骤降。
assign 和直接 df['col'] = ... 的性能差异
assign 返回新 DataFrame,不修改原对象;而 df['col'] = ... 是就地赋值。表面上看后者快,但实际在链式场景下,assign 更安全、更函数式。
- 内存:多次
assign会创建多个中间 DataFrame,大数据集可能吃内存;df['col'] = ...无额外拷贝 - 可测试性:
assign易于单元测试(输入输出明确),就地赋值依赖副作用 - 兼容性:某些 pandas 操作(如
groupby().apply()内部)禁止就地修改,必须用assign
真正要注意的是:别为了链式而链式。如果某步计算耗时(比如调用外部 API 或复杂 apply),先缓存结果再 assign,否则每次链式都会重算。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











