函数应用:apply、map 与 applymap
本教程共 54 篇 · 第 41 篇 · 更新于 2026-08-11 · 约 5 分钟阅读
本节目标:分清 Series.map、DataFrame.apply、DataFrame.map 三个函数的适用场景,并建立”向量化优先”的思维。
pandas 处理数据默认走向量化:整列一起算,速度飞快。但总有内置方法覆盖不到的自定义逻辑,这时用函数应用三兄弟:map(逐元素)、apply(按行或按列)、以及 DataFrame.map(3.0 里逐元素处理整个表)。
Series.map:逐元素变换
map 对 Series 的每个元素调用函数,返回值与输入等长:
import pandas as pd
import numpy as np
s = pd.Series([1, 2, 3, 4, 5])
s.map(lambda x: x * 2)
函数也可以是现成的,比如 np.sqrt:
s.map(np.sqrt)
map 最有价值的能力是”字典映射”——把值翻译成另一个值:
等级 = {1: "低", 2: "中", 3: "高"}
s.map(等级)
字典里没有的键变成 NaN。传 Series 做映射表也行,按索引对齐取值:
映射表 = pd.Series(["甲", "乙", "丙"], index=[1, 2, 3])
s.map(映射表)
Tip“把一列代码翻译成中文名”这种活,map + 字典一行搞定,别用循环。
注意:map 遇到 NaN 会直接跳过,函数不会被调用,某些统计逻辑可能因此漏算。
DataFrame.apply:按列或按行
apply 的粒度是”一整列”或”一整行”,函数收到的是 Series:
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [4, 5, 6],
})
df.apply(sum) # 默认 axis=0,每列求和
df.apply(lambda x: x.max() - x.min(), axis=1) # 每行极差
默认 axis=0 按列:函数调用次数 = 列数,每次收到一列。axis=1 按行:调用次数 = 行数,每次收到一行。
函数返回标量时,apply 结果是一维的;返回 Series 时结果升级成 DataFrame:
df.apply(lambda x: pd.Series({"总和": x.sum(), "平均": x.mean()}))
多列同时算就靠这个。写函数时多用列名取值,比如 row["A"],比位置索引 row[0] 更不容易出错。
Warning
apply(axis=1)按行逐条调用 Python 函数,是性能重灾区。行数过万就开始明显变慢,优先想想能不能用列运算表达(如df["B"] - df["A"])。
DataFrame.map:逐元素处理整个表
早期版本用 applymap 逐元素处理整个 DataFrame,pandas 2.1 起改名为 DataFrame.map,3.0 里 applymap 已经移除。现在写:
df.map(lambda x: x * 2)
对表里每个单元格调用函数。它和 Series.map 的区别是作用范围:一个管单列,一个管全表。
Warning3.0 里调用
df.applymap(...)会直接报 AttributeError。看到老教程写 applymap,统一换成df.map(...)。
Series.apply:和 map 的区别
Series 也有 apply,功能上几乎和 map 一样,都能逐元素应用函数。区别在细节:
- map 支持字典/Series 映射表,apply 不支持
- apply 支持给函数传额外参数(args、**kwargs)
- map 对 NaN 静默跳过,apply 会把 NaN 传给函数
def 裁剪(x, 下限, 上限):
return max(下限, min(上限, x))
s.apply(裁剪, 下限=0, 上限=4)
带参数的自定义函数,用 apply 传参比写 lambda 更清晰。
向量化优先原则
社区里有个流传很广的对比:同样的”计算两列比值”任务,
# 逐行 UDF:约 5.6 秒
df["新列"] = df.apply(lambda row: 100 * (row["A"] / row["B"]), axis=1)
# 向量化:约 0.004 秒
df["新列"] = 100 * (df["A"] / df["B"])
差了上千倍。向量化是 C 级别的整列运算,apply/map 是 Python 循环加函数调用,天然慢。
条件判断也有向量化版本。apply 写法:
df["标记"] = df["A"].apply(lambda x: "高" if x > 2 else "低")
用 np.where 一行替代:
df["标记"] = np.where(df["A"] > 2, "高", "低")
所以选择顺序是:
- 先找向量化写法:四则运算、比较、内置方法、.str 系列
- 再找内置函数:np.sqrt、np.clip、np.where 这类
- 最后才考虑 apply/map:复杂自定义逻辑、字典映射、跨行跨列的组合计算
Noteapply/map 不是不能用,是要用在刀刃上:数据量小无所谓;数据量大时,一行向量化代码可能顶得上一百行循环。
小结
map 管逐元素和字典映射,apply 管行列级自定义计算,DataFrame.map 管整表逐元素。写代码前先问一句:这能用向量化表达吗?能,就别用函数。