承接《Pandas 时间类型与 dt 访问器》,本篇对比
map、apply、applymap三者用法、适用范围、性能差异,区分 Series 与 DataFrame 场景,附带向量化优化方案,是业务自定义逻辑的常用工具,保持系列教程统一风格。
前置导入与示例数据
import pandas as pd
import numpy as np
df = pd.DataFrame({
"name": ["张三", "李四", "王五", "赵六", "孙七"],
"age": [22, 25, 30, 28, 35],
"salary": [8000, 12000, 15000, 11000, 18000],
"city": ["杭州", "上海", "杭州", "北京", "上海"]
})
print(df)
一、核心一句话区分
Series.map():Series 专用,值映射、一对一转换;适合字典映射、单值函数Series.apply():Series 专用,比 map 更灵活,支持复杂逻辑、多返回值DataFrame.apply():按行 / 按列批量运算(axis=0 列,axis=1 行),返回 Series/DataFrameDataFrame.applymap():单元格级,遍历每一个元素(Pandas 2.1+ 推荐替换为df.map())
⚠️ 重点:这几个都是循环封装,性能弱于向量化写法(str、dt、布尔索引),优先向量化,不得已再用它们。
二、Series.map()
最适合字典映射,一对一翻译,非常常用于编码转名称、状态翻译
# 字典映射
city_map = {
"杭州": "HZ",
"上海": "SH",
"北京": "BJ"
}
df["city_code"] = df["city"].map(city_map)
# 传入函数
def get_level(sal):
if sal >= 15000:
return "高"
elif sal >= 10000:
return "中"
else:
return "低"
df["sal_level_map"] = df["salary"].map(get_level)
map 只能接收一个入参(当前元素);无法拿到同行其他字段。
三、Series.apply()
同样作用于 Series 的每个元素,相比 map:
- 支持更复杂函数
- 支持返回多个值,扩展成多列
def calc_tax(sal):
tax = sal * 0.05
net = sal - tax
return tax, net
# 返回元组,再展开成多列
df[["tax", "net_salary"]] = df["salary"].apply(calc_tax).apply(pd.Series)
什么时候选 map / apply:简单字典映射优先 map;复杂函数、多返回值用 apply。
四、DataFrame.apply()(高频,按行/列)
axis=0:默认,按列传入 Series
axis=1:按行传入一行的 Series,可以访问同行多个字段!业务最常用
# 按列:求每一列最大值
df[["age","salary"]].apply(max)
# 按行:同时读取本行age、salary、city,多字段联合计算
def row_func(row):
if row["city"] == "杭州" and row["salary"] > 10000:
return "重点员工"
else:
return "普通员工"
df["tag"] = df.apply(row_func, axis=1)
axis=1 是很多报表逻辑的救星,但大数据量很慢,几十万行慎用。
lambda 简写示例:
df["tag"] = df.apply(lambda r: "重点员工" if (r["city"]=="杭州" and r["salary"]>10000) else "普通员工", axis=1)
五、DataFrame.applymap()
遍历 DataFrame 每一个单元格元素,所有字段统一处理
注意:Pandas 2.1 之后官方推荐使用
df.map(),applymap 逐步废弃
# 全部转为字符串
df_str = df[["name","city"]].applymap(str)
# 数字统一保留2位小数
df[["age","salary"]].applymap(lambda x: round(x,2))
六、参数说明
func:自定义函数args:位置参数,给函数额外传参kwargs:关键字参数
def add_val(x, offset):
return x + offset
df["salary_add"] = df["salary"].apply(add_val, args=(1000,))
七、返回值类型
- 函数返回单个值 → apply 输出 Series
- 函数返回 Series → apply 输出 DataFrame
- 函数返回列表/元组 → 可以展开成多列
八、性能对比(非常重要)
优先级从高到低:
- 向量化运算(
df["salary"] * 1.05、str、dt、布尔筛选)✅ 首选 - map(字典映射,底层优化好)
- Series.apply
- DataFrame.apply(axis=1) ❌ 大数据尽量避免
- applymap ❌ 最慢
示例:不要用 apply 做简单判断
# ❌ 慢写法 apply
df["level"] = df["salary"].apply(lambda x: "高" if x>15000 else "中")
# ✅ 推荐向量化 np.where
df["level"] = np.where(df["salary"]>15000, "高", "中")
多条件向量化:np.select
conds = [
df["salary"] >= 15000,
df["salary"] >= 10000
]
choices = ["高", "中"]
df["level"] = np.select(conds, choices, default="低")
九、综合实战模板
def calc_emp_info(row):
"""行级复合逻辑示例"""
age = row["age"]
sal = row["salary"]
if age >= 30 and sal >= 12000:
return "资深"
elif age >= 25:
return "骨干"
else:
return "新人"
df["emp_type"] = df.apply(calc_emp_info, axis=1)
print(df)
十、高频坑清单
apply(axis=1)速度极慢,上万行以上优先改用 np.where / np.select- map 传入函数时,遇到匹配不到的值会返回 NaN
- 不要在 apply 里面写循环,循环套循环性能灾难
- apply 返回多列时,忘记
.apply(pd.Series)会得到元组列 - applymap 只适合全表统一元素处理,按行多字段判断不能用
- 区分:
df.apply是按行/列,不是逐个单元格
十一、选型速查表
| 方法 | 对象 | 遍历粒度 | 典型场景 |
|---|---|---|---|
| Series.map | Series | 单个值 | 字典映射、一对一转换 |
| Series.apply | Series | 单个值 | 复杂单值函数、多返回值 |
| DataFrame.apply | DataFrame | 整行 / 整列 | 跨行多字段联合逻辑 |
| DataFrame.applymap | DataFrame | 每个单元格 | 全表统一元素转换(逐步弃用) |
0 条笔记