告别循环噩梦!Pandas.apply () 让数据处理像搭积木一样简单

一、为什么说 apply 是数据处理的 “瑞士军刀”?​

在 Pandas 的世界里,流传着这样一句话:“如果你觉得数据处理很麻烦,那一定是没用对 apply 函数。”想象一下:你有 10 万条电商订单数据,需要给每个订单计算折扣后的价格;或者有一堆用户评论,需要提取关键词并统计情感倾向;再或者需要按行 / 列对数据进行复杂的逻辑判断……如果用传统的 Python 循环(如 for 循环),代码会像裹脚布一样又长又慢,还容易出错。但 apply 函数就像一个 “数据处理加速器”,用一行代码就能替代几十行循环,让数据处理效率飙升!​

二、apply 的底层逻辑:比循环快 10 倍的秘密​

先别急着学用法,我们先搞懂 apply 的核心优势:向量化运算。Pandas 的底层是用 C 语言优化的数组运算,而 apply 会将自定义函数 “广播” 到整个数组上,避免了 Python 循环的低效性。举个简单的例子:​

TypeScript

取消自动换行复制

# 传统循环:处理10万条数据可能需要几秒​

for i in range(len(data)):​

data['price'][i] = data['price'][i] * 0.8​

# apply方法:瞬间完成​

data['price'] = data['price'].apply(lambda x: x * 0.8)​

** 为什么差距这么大?** 因为循环每次只能处理一个元素,而 apply 会把整个 Series/DataFrame 作为输入,一次性批量处理,这就是 “向量化” 的魔力!​

三、apply 的三种打开方式:从入门到精通​

(一)单刀直入:对 Series 元素逐个加工​

场景:处理一列数据,比如给所有商品价格打 8 折,或者将字符串统一转成大写。语法:series.apply(func, **kwargs)案例 1:数值计算假设有一个 “原始价格” 列,需要计算 “折后价”(满 100 减 30,否则打 9 折):​

TypeScript

取消自动换行复制

import pandas as pd​

data = pd.DataFrame({'原价': [150, 80, 200, 90]})​

# 定义复杂计算函数​

def calculate_discount(price):​

if price >= 100:​

return price - 30​

else:​

return price * 0.9​

# 对Series应用函数​

data['折后价'] = data['原价'].apply(calculate_discount)​

print(data)​

输出:​

原价​

折后价​

150​

120​

80​

72​

200​

170​

90​

81​

案例 2:字符串处理处理用户评论,提取情感关键词(简化版):​

TypeScript

取消自动换行复制

comments = pd.Series([​

"这部电影太棒了!特效超震撼",​

"剧情无聊,演员演技一般",​

"强烈推荐!性价比超高"​

])​

# 提取关键词​

def extract_keywords(text):​

if "太棒了" in text or "推荐" in text:​

return "正面"​

elif "无聊" in text or "一般" in text:​

return "负面"​

else:​

return "中性"​

comments.apply(extract_keywords)​

输出:0 正面1 负面2 正面dtype: object​

(二)横刀立马:对 DataFrame 行 / 列批量操作​

场景:需要同时处理多行或多列数据,比如根据 “身高” 和 “体重” 计算 BMI,或按行筛选符合条件的数据。语法:data.apply(func, axis=0/1, raw=False, **kwargs)​

  • axis=0:按列处理(默认),函数接收每一列作为 Series​
  • axis=1:按行处理,函数接收每一行作为 Series​
  • raw=True:传递原始 ndarray 给函数(而非 Series),适合需要 numpy 运算的场景​

案例 1:按行计算 BMI​

TypeScript

取消自动换行复制

people = pd.DataFrame({​

'姓名': ['Alice', 'Bob', 'Charlie'],​

'体重(kg)': [55, 70, 80],​

'身高(m)': [1.6, 1.75, 1.8]​

})​

# 定义BMI计算公式(行作为Series传入)​

def calculate_bmi(row):​

return row['体重(kg)'] / (row['身高(m)'] ** 2)​

# 按行应用函数,axis=1表示处理每一行​

people['BMI'] = people.apply(calculate_bmi, axis=1)​

print(people)​

输出:​

姓名​

体重 (kg)​

身高 (m)​

BMI​

Alice​

55​

1.6​

21.4843​

Bob​

70​

1.75​

22.8571​

Charlie​

80​

1.8​

24.6914​

案例 2:按列统计缺失值比例​

TypeScript

取消自动换行复制

# 模拟含缺失值的数据​

df = pd.DataFrame({​

'A': [1, 2, np.nan, 4],​

'B': [5, np.nan, np.nan, 8],​

'C': [9, 10, 11, 12]​

})​

# 按列计算缺失率(axis=0,每列作为Series传入)​

def missing_rate(col):​

return col.isnull().mean() * 100 # 缺失比例百分比​

df.apply(missing_rate, axis=0)​

输出:A 25.0B 50.0C 0.0dtype: float64​

(三)倚天屠龙:高阶玩法之复杂数据处理​

场景 1:返回多个结果,生成新列有时候函数会返回多个值,需要拆分成多列。比如从日期字符串中提取 “年”、“月”、“日”:​

TypeScript

取消自动换行复制

dates = pd.Series(["2023-01-05", "2023-02-18", "2023-03-22"])​

# 函数返回字典,键对应新列名​

def parse_date(date_str):​

year, month, day = date_str.split('-')​

return {'年': year, '月': month, '日': day}​

# 应用函数后,自动扩展为多列​

date_df = dates.apply(parse_date).apply(pd.Series) # 关键:用pd.Series展开字典​

print(date_df)​

输出:年 月 日0 2023 01 051 2023 02 182 2023 03 22​

场景 2:结合 lambda 表达式,一行代码搞定简单逻辑比如给 “价格” 列所有大于 100 的数值标红(配合后续可视化库):​

TypeScript

取消自动换行复制

data['原价'].apply(lambda x: f'<span style="color:red">{x}</span>' if x > 100 else x)​

四、避坑指南:apply 不是万能药!​

虽然 apply 很强大,但也有 “软肋”:​

  1. 性能陷阱:​
  • 对简单操作(如加减乘除),直接用向量化运算(如data['price'] * 0.8)比 apply 更快​
  • 大数据量(百万级以上)时,优先用 Pandas 内置函数或 NumPy 数组运算​
  1. axis 参数弄反:​
  • 记不住axis=0和axis=1?记住:axis=0 是 “纵向”(列),axis=1 是 “横向”(行),就像 Excel 的行和列方向​
  1. 函数返回值类型错误:​
  • 若函数返回非标量(如列表、字典),需用apply(pd.Series)展开,否则会生成 “对象” 类型列​

五、对比魔法三兄弟:apply vs map vs applymap​

很多新手会混淆这三个函数,一张表帮你理清:​

函数​

适用对象​

输入​

输出​

典型场景​

apply​

Series/DataFrame​

单个元素 / 列 / 行​

标量 / Series/DataFrame​

复杂逻辑处理,如行计算、自定义函数​

map​

Series​

单个元素​

标量​

简单映射(如字典替换、数值转换)​

applymap​

DataFrame​

单个元素​

标量​

对每个元素统一处理(如格式化字符串)​

举例对比:​

TypeScript

取消自动换行复制

# map:给Series每个元素加前缀​

data['姓名'].map(lambda x: '用户-' + x)​

# applymap:给DataFrame每个数值取绝对值​

data.applymap(lambda x: abs(x) if isinstance(x, (int, float)) else x)​

六、实战案例:用 apply 解锁电影数据的隐藏秘密​

我们用 IMDb 电影数据集来实战 apply 的威力(数据可从 Kaggle 下载),目标:​

  1. 从 “时长” 列(如 “120 min”)提取纯数字​
  1. 按 “类型” 列(如 “Action, Comedy”)拆分多类型,统计每个类型的电影数量​

步骤 1:数据预处理​

TypeScript

取消自动换行复制

movies = pd.read_csv('imdb_movies.csv')​

# 提取时长数字(用正则表达式)​

movies['duration'] = movies['duration'].apply(lambda x: int(x.split()[0]))​

# 拆分类型列​

def split_genres(genres_str):​

return genres_str.split(', ') if isinstance(genres_str, str) else []​

movies['genres_list'] = movies['genres'].apply(split_genres)​

步骤 2:统计各类型出现次数​

TypeScript

取消自动换行复制

from collections import Counter​

# 收集所有类型​

all_genres = movies['genres_list'].apply(Counter).sum()​

# 转换为DataFrame并排序​

genre_counts = pd.DataFrame(all_genres.most_common(), columns=['类型', '数量'])​

print(genre_counts.head())​

输出:​

类型​

数量​

Drama​

3456​

Comedy​

2890​

Thriller​

2341​

Action​

2112​

Romance​

1987​

七、总结:apply 的终极心法​

  • 简单操作不用 apply:能用data['col'] + 10解决的问题,别用 apply​
  • 复杂逻辑首选 apply:涉及条件判断、多行 / 列联动、自定义函数时,apply 就是你的最佳拍档​
  • 学会拆解问题:如果函数返回复杂结构(如列表、字典),用apply(pd.Series)或explode()展开​

最后送大家一句口诀:循环慢如蜗牛爬,apply 快如千里马,数据处理有魔法,一行代码走天下!​

下次我们将探讨 apply 与 groupby 的 “梦幻联动”,学会用 apply 做分组后的复杂计算。关注我,一起解锁更多 Pandas 黑科技!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值