用对一把刀:Pandas 中的 map、apply、applymap 全景实战与进阶
写 Python,像雕刻。Pandas 给了我们三把刀:map、apply、applymap。刀锋越锋利,越要知道何时用、怎么用、用到什么程度。
开篇引入
Python 从“一门优雅的脚本语言”成长为数据时代的通用底座。它连接 Web、自动化、数据科学与人工智能,被称为“胶水语言”,让工具与系统彼此“说话”。在数据处理领域,Pandas 是事实标准,而“如何把函数施加到数据上”这个看似简单的问题,往往决定了你的代码是否高效、稳定、可维护。
- **为什么写这篇文章:**我在实际项目里见过太多“能跑但慢”“可用但难懂”的 DataFrame 操作。最常见的源头,就是没把
map、apply、applymap用对。本文从直觉到工程,带你建立清晰的心智模型,并给出一套可复制的实战模板。 - **你将获得:**三者差异与适用场景的“速查表”;高频任务的代码模板;性能与可维护性的权衡方法;常见坑与避雷;以及可迁移到任意数据任务的思考框架。
三者差异一图看懂
| 维度 | map | apply | applymap |
|---|---|---|---|
| 作用对象 | Series | Series 或 DataFrame | DataFrame |
| 粒度 | 元素级(单列) | Series:元素级;DataFrame:按行/按列 | 元素级(整表) |
| 典型用途 | 值映射、字典编码 | 行/列聚合、行级多列逻辑 | 整表逐元素清洗/格式化 |
| 返回形态 | Series | 依函数返回:标量/Series/DataFrame | DataFrame |
| 性能倾向 | 快(字典/Series 映射更快) | 视函数与 axis 而定(行级常慢) | 最慢(逐元素 Python 调用) |
经验顺序(快→慢):向量化/NumPy ufunc > 字典/Series 的 map > Series.apply(向量化函数)> DataFrame.apply(axis=1)> applymap。
基础概念与 API 要点
Series.map:单列值映射的首选
- **适用场景:**编码字典映射、类别映射、值替换、函数逐元素映射。
- 核心要点:
- **支持三种输入:**函数、字典、Series。
- **未命中行为:**字典/Series 映射未命中会得到 NaN;
replace的未命中默认保留原值。 - 缺失项优化:
na_action="ignore"在函数模式下跳过 NaN,少做一次 Python 调用。
import pandas as pd
s = pd.Series(["M", "F", "X", None])
mapping = {
"M": 1, "F": 0}
# 字典映射(未命中→NaN)
encoded = s.map(mapping)
# 函数映射(跳过 NaN)
encoded2 = s.map(lambda x: 1 if x == "M" else 0 if x == "F" else -1, na_action="ignore")
- 与 replace 的区别:
- **map:**未命中→NaN,强调“映射表定义全集”。
- **replace:**未命中→保留原值,强调“只替换指定值”。
Series.apply:对单列做逐元素或归约变换
- **适用场景:**需要函数逻辑,但已有向量化替代前暂用;复杂字符串/日期规则可先查
.str/.dt。 - **返回控制:**函数返回标量→Series;返回序列→可能“展开”为 DataFrame(也可先返回字典再构造)。
s = pd.Series([" ab ", "cd ", None])
# 字符串清洗:优先向量化 API
s1 = s.str.strip().str.lower()
# 万不得已再用 apply
s2 = s.apply(lambda x: x.strip().lower() if isinstance(x, str) else x)
DataFrame.apply:按行/按列执行函数
- **适用场景:**行级规则(需要多列共同决策)、列级聚合/派生。
- 关键参数:
- **axis=0/“index”:**对每列的 Series 调用(常用于列级聚合或归一化)。
- **axis=1/“columns”:**对每行的 Series 调用(多列逻辑,性能偏慢)。
- result_type:“expand”(展开成多列)、“reduce”(尽量标量)、“broadcast”(按行/列广播)。
import pandas as pd
df = pd.DataFrame({
"qty": [2, 3, 1],
"price": [10.0, 20.0, 30.0],
"city": ["Tokyo", "Osaka", "Nagoya"],
})
# 行级决策:需要多列
def score_row(row):
base

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



