import pandas as pd
team_stats = pd.DataFrame({"Team":["ARI","ATL","BAL"],"League":["NL","NL","AL"],"Year":[2012,2012,2012],"RunsScored":[734,700,712],"RunsAllowed":[688,600,705],"Wins":[81,94,93],"Games":[162,162,162],"Playoffs":[0,1,1],})# Vectorized: fast and idiomatic
team_stats["RunDiff"]= team_stats["RunsScored"]- team_stats["RunsAllowed"]print(team_stats[["Team","RunsScored","RunsAllowed","RunDiff"]])技术支持
defcompute_run_diff(row):# Treat the row as read-only; return a scalarreturn row["RunsScored"]- row["RunsAllowed"]# Row-wise apply (Python-level loop; can be slow on large data)
team_stats["RunDiff_apply"]= team_stats.apply(compute_run_diff, axis=1)技术支持
import numpy as np
defcompute_run_diff_raw(values):# values is a NumPy array when raw=True# Order matches the column order we select
rs, ra = values
return rs - ra
team_stats["RunDiff_raw"]= team_stats[["RunsScored","RunsAllowed"]].apply(
compute_run_diff_raw, axis=1, raw=True)技术支持
先验证一次函数输入
在我接入一个新的按行函数时,我会先打印一行来确认传入的是什么。
defdebug_row(row):# Print the first row onlyif row.name == team_stats.index[0]:print("Example row:", row.to_dict())return0
_ = team_stats.apply(debug_row, axis=1)技术支持
# Preferred
totals = rays_by_year.sum(axis=0)print(totals)# If you must use apply (not recommended here)
totals_apply = rays_by_year.apply(sum, axis=0)技术支持
# Using map with a dict
rays_by_year["PlayoffsText"]= rays_by_year["Playoffs"].map({0:"No",1:"Yes"})# Equivalent with replace
rays_by_year["PlayoffsText2"]= rays_by_year["Playoffs"].replace({0:"No",1:"Yes"})技术支持
控制 .apply() 的输出形状
当您的函数每行返回多个值时,请显式指定输出形状。这可以避免意外以及未来版本变更带来的问题。
返回列表并展开为多列
使用 result_type='expand' 将列表/数组拆分为多个列。
defwins_losses(row):# Derive wins and losses as two outputs
wins = row["Wins"]
losses = row["Games"]- row["Wins"]if"Games"in row else np.nan
return[wins, losses]# Example using team_stats, which has "Wins" and "Games"
expanded = team_stats.apply(wins_losses, axis=1, result_type="expand")
expanded.columns =["WinsOut","LossesOut"]
team_stats = pd.concat([team_stats, expanded], axis=1)技术支持
许多人使用
.apply()来“避免循环”,并期待它既快又直接。实际上,按行的.apply(axis=1)仍然是 Python 层面的循环。在大数据量上可能很慢,而且有时会返回出乎意料的形状。解决方法很简单:常见任务使用向量化的 pandas/NumPy 操作,把.apply()留给确实需要跨多列处理的逻辑。本指南介绍
.apply()的当前行为,指出常见陷阱,并提供更快更清晰的可替换用法模式。什么是 DataFrame.apply() 与 Series.apply()?
DataFrame.apply(func, axis=0)默认对每一列调用func。使用axis=1时,它会对每一行调用func。而Series.apply(func)则对每个元素调用func(或对整个Series,这取决于新版本中的函数签名)。axis=1意为“按行”。axis=0(默认)意为“按列”。Series。如果返回Series或dict,pandas 会将这些键展开为DataFrame。如果返回列表/数组,除非设置result_type='expand',否则您将得到一个由列表组成的 Series。优先推荐:向量化操作
大多数涉及合并或转换列的任务,使用向量化表达式或内置方法更快也更清晰。下面的示例在不使用
.apply()的情况下计算一支棒球队的得失分差。当您确实需要时再用按行 .apply()
当您的逻辑确实跨越多列且不易向量化时(例如依赖多个字段的条件规则),请使用
.apply(axis=1)。从多列计算派生值
此模式使用多个输入为每行计算一个值。仍然更推荐上面的向量化方法,但这里展示了正确的按行用法,以及会影响速度与输出的选项。
当函数是数值型并且只需要原始数组时,传入
raw=True会直接向您的函数提供一个 NumPy 数组,从而跳过部分 pandas 的开销。先验证一次函数输入
在我接入一个新的按行函数时,我会先打印一行来确认传入的是什么。
案例研究:求和、赛季总计与文本标记
针对光芒队(Rays)逐年数据,尽量使用内置方法和按元素工具,能不用
.apply()就不用。高效获取列求和
使用
DataFrame.sum()代替.apply(sum)。它更快也更清晰。计算某赛季的总得分与失分
跨列做向量化运算;无需使用
.apply()。将 0/1 标记转换为文本
对单列进行按元素转换时,优先使用
Series.map()或replace()。控制 .apply() 的输出形状
当您的函数每行返回多个值时,请显式指定输出形状。这可以避免意外以及未来版本变更带来的问题。
返回列表并展开为多列
使用
result_type='expand'将列表/数组拆分为多个列。返回 Series 或 dict 以自动命名列
从每行返回一个
Series或dict会生成一个列标签匹配的 DataFrame。关键性能建议
按行的
.apply(axis=1)很方便,但在大 DataFrame 上很慢,因为它会对每一行调用一次您的 Python 函数。以下模式可避免这一瓶颈。sum、mean、clip、where、astype以及字符串/访问器方法等内置方法。raw=True以获取 NumPy 数组并降低 pandas 开销。Series.map()或向量化方法,而非DataFrame.apply(axis=1)。您需要了解的版本说明(pandas 2.x)
近期的 pandas 版本收紧了 .apply() 的行为,使结果更可预测。
result_type='expand'。Series或dict会展开为以这些键为列名的 DataFrame(自 0.23 起稳定)。Series.apply()的变更:convert_dtype参数已弃用。若需要混合类型,请先转换为object(例如s.astype("object").apply(fn))。较新的签名允许在更多场景下控制fn接收标量或 Series(例如by_row)。.apply() vs. .map() vs. .applymap() vs. .agg()
请选择与您的变换“形状”相匹配的 API。
Series.map(func_or_dict): 对单列进行按元素变换。最适合查表或简单函数。DataFrame.apply(func, axis=1): 需要多列参与的按行逻辑。DataFrame.apply(func, axis=0): 按列逻辑(每次输入是代表一列的 Series)。DataFrame.applymap(func): 对每个单元格按元素操作。谨慎使用;向量化方法更快。.agg()/.transform(): 聚合与组内变换;在 groupby 流水线中更应优先使用。常见错误与快速修复
以下问题最常导致结果不正确或性能下降,并附有相应修复方法。
axis=1。result_type='expand'以拆分为多列。raw=True降低开销。astype进行回转。结语
.apply()是个灵活的工具,但并不是性能捷径。对于算术、聚合以及单列的按元素变换,请使用向量化操作。只有当您的逻辑确实需要按行访问多列时,才使用DataFrame.apply(axis=1)。在使用时,通过result_type控制输出形状,数值函数可考虑raw=True,并留意dtypes。这些模式在现代 pandas 中能产出可预测的结果,并随数据增长而更好扩展。