对列和行使用 apply()
使用 axis=0 和 axis=1 调用 DataFrame.apply(),对每一列或每一行运行自定义函数。
对列和行使用 apply() 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。
何时使用 apply()
DataFrame.apply() 会对每一列(axis=0)或每一行(axis=1)运行自定义 Python 函数。它是不得已时才使用的工具——速度比内置的向量化操作慢——但当计算无法通过 NumPy 算术、布尔索引或内置聚合函数表达时,它必不可少。当您需要复杂的条件逻辑、自定义字符串解析,或需要一次处理一行或一列的多步计算时,请使用它。
import pandas as pd
import numpy as np
df = pd.DataFrame({
'price': [10.5, 25.0, 8.3, 100.0],
'quantity': [3, 1, 5, 2],
'tax_rate': [0.05, 0.10, 0.05, 0.15]
})
print(df)沿列使用 apply()(axis=0)
使用 axis=0(默认值)时,apply() 会将每一列作为 Series 传递给函数。函数每次接收一列,并应返回一个标量(用于聚合)或一个 Series(用于转换)。这适合在一次调用中,对所有数值列统一执行自定义规范化或清洗步骤。
# Custom range normalisation (0 to 1) for each column
def min_max_scale(col):
return (col - col.min()) / (col.max() - col.min())
df_scaled = df[['price', 'quantity']].apply(min_max_scale, axis=0)
print(df_scaled)沿行使用 apply()(axis=1)
使用 axis=1 时,apply() 会将每一行作为 Series 传递给函数。该行的索引就是列名,因此您可以按名称访问值。这非常适合依赖多个列的行级计算,例如计算税后总收入,其中每一行都有自己的税率。
def revenue_after_tax(row):
subtotal = row['price'] * row['quantity']
return subtotal * (1 + row['tax_rate'])
df['revenue_after_tax'] = df.apply(revenue_after_tax, axis=1)
print(df)使用 apply() 返回多个值
传递给 apply(axis=1) 的函数可以返回带有命名条目的 pd.Series,Pandas 会将其展开为多个新列。与为两个新列调用两次 apply() 相比,这种方式更简洁。或者,函数也可以返回一个 dict,Pandas 同样会将其展开为多个列。
def compute_totals(row):
subtotal = row['price'] * row['quantity']
tax = subtotal * row['tax_rate']
return pd.Series({'subtotal': subtotal, 'tax': tax, 'total': subtotal + tax})
result = df.apply(compute_totals, axis=1)
print(result)使用 apply() 对列执行自定义聚合
使用 apply(func, axis=0) 计算每列的自定义统计量,并返回汇总 Series。例如,一次调用即可计算每个数值列的变异系数(标准差除以平均值)。结果是一个按列名建立索引的 Series,适合快速执行逐列诊断。
def coeff_of_variation(col):
return col.std() / col.mean() if col.mean() != 0 else np.nan
cv = df[['price', 'quantity']].apply(coeff_of_variation, axis=0)
print('Coefficient of variation per column:')
print(cv)将 apply() 与向量化替代方案进行比较
使用 apply() 之前,请始终检查是否存在向量化替代方案。对于税后收入计算,df['price'] * df['quantity'] * (1 + df['tax_rate']) 与 apply(axis=1) 版本完成相同的工作,但由于使用 NumPy 的 C 级循环,运行速度快 10–100 倍。请将 apply() 留给向量化逻辑会变得难以阅读的情况。
import time
start = time.time()
df['vectorised'] = df['price'] * df['quantity'] * (1 + df['tax_rate'])
print('Vectorised:', time.time() - start, 's')
start = time.time()
df['apply_result'] = df.apply(revenue_after_tax, axis=1)
print('apply():', time.time() - start, 's')将 apply() 与 Lambda 一起使用
对于简短的单行转换,请直接将 lambda 传递给 apply(),而不是定义命名函数。Lambda 适合简洁地执行简单操作,但对于复杂逻辑应避免使用,因为带有注释的命名函数更易于理解和测试。请谨慎使用 Lambda——它们不容易按名称进行单元测试或性能分析。
# Clip revenue between 0 and 200, then round to nearest 10
df['revenue_clean'] = df['revenue_after_tax'].apply(lambda x: round(min(max(x, 0), 200) / 10) * 10)
print(df[['revenue_after_tax', 'revenue_clean']])向 apply() 传递额外参数
请使用 args 元组,或在 apply(func, args=(val,), axis=1) 中使用关键字参数,向函数传递额外参数。这样可以避免在函数内部使用全局变量,并使函数能够使用不同的参数值重复利用。在 Python 3.8 及更高版本中,您还可以使用 functools.partial 创建函数的部分应用版本。
def discount_price(row, discount_pct, threshold):
if row['quantity'] >= threshold:
return row['price'] * (1 - discount_pct)
return row['price']
df['discounted_price'] = df.apply(
discount_price, axis=1,
args=(0.1, 3) # 10% discount for quantity >= 3
)
print(df[['price', 'quantity', 'discounted_price']])使用 apply() 进行类型转换
当一列包含混合类型——包括整数、浮点数和字符串——时,astype() 会引发错误。请使用 apply(pd.to_numeric, errors='coerce') 逐行尝试转换,并将无法转换的值返回为 NaN。对于本应为数值但偶尔包含因数据录入错误而产生的非数值条目的列,这是一种安全模式。
messy = pd.Series(['10', '20.5', 'N/A', '100', '--'])
clean = messy.apply(pd.to_numeric, errors='coerce')
print(clean)性能:apply() 与 np.vectorize
当必须对标量函数逐元素应用时,np.vectorize(func)(array) 通常比 Series.apply(func) 更快,因为 NumPy 的 vectorize 会通过 C 进行分派,而不是承受 Python 的函数调用开销。不过,np.vectorize 仍然比真正的广播慢——只有在没有任何广播表达式能够表达该逻辑时,它才有用。
def classify_size(price):
if price < 15:
return 'small'
elif price < 50:
return 'medium'
return 'large'
# np.vectorize approach
classify_v = np.vectorize(classify_size)
df['size_class'] = classify_v(df['price'].values)
print(df[['price', 'size_class']])何时应使用 apply()
在以下情况下,Apply 是合适的工具:(1) 逻辑需要同时根据多个列的值进行分支;(2) 函数必须为每一行调用外部 API 或数据库(无法避免顺序执行);(3) 函数需要解析存储在单元格中的复杂字符串,例如 JSON 数据块;或 (4) 函数返回列表等长度可变的对象。在其他情况下,请优先使用向量化操作,以获得更快的速度和更好的可读性。
import json
# Parse a JSON metadata column row by row
df['metadata'] = ['{"color": "red"}', '{"color": "blue"}', '{}', '{"color": "green"}']
df['color'] = df['metadata'].apply(lambda s: json.loads(s).get('color', 'unknown'))
print(df[['metadata', 'color']])快速检查
测试您对本课中数据分析概念的理解。
课程回顾
本课您学会了:使用 apply(axis=0) 计算列级自定义统计量、使用 apply(axis=1) 结合多列输入进行行级计算,以及识别何时应优先使用向量化替代方案来提升性能。接下来我们将探索结合 GroupBy 使用 apply() 进行复杂的分组级聚合。
常见问题解答
「对列和行使用 apply()」课时是免费的吗?
是的 — 「对列和行使用 apply()」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。
「对列和行使用 apply()」这节课中我会学到什么?
使用 axis=0 和 axis=1 调用 DataFrame.apply(),对每一列或每一行运行自定义函数。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Pandas & NumPy Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「对列和行使用 apply()」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?
能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。