当前位置: 首页 > news >正文

pandas扩展:apply自定义函数、分组进阶(五大核心)、透视表

apply自定义函数

apply() 函数是 Pandas 中非常强大和常用的功能,它的存在解决了数据分析中的一个核心需求:灵活地对数据应用自定义操作

为什么需要 apply() 函数?

  1. 超越内置函数的限制:Pandas 虽然提供了很多内置方法(如 sum(), mean() 等),但无法覆盖所有可能的计算需求。
  2. 处理复杂逻辑:有些操作需要复杂的条件判断或多步计算,无法用简单的向量化操作完成。
  3. 代码复用:可以将复杂的操作封装成函数,然后通过 apply() 应用到数据上。

apply() 的主要作用

apply() 允许你将任意函数应用到:

  • DataFrame 的每一行或每一列
  • Series 的每一个元素

基本语法

# 对 DataFrame
df.apply(function, axis=0)  # axis=0: 对每列应用函数
df.apply(function, axis=1)  # axis=1: 对每行应用函数# 对 Series
series.apply(function)  # 对每个元素应用函数

实例

1. 对 Series 使用 apply()(元素级操作)

import pandas as pd# 创建示例数据
df = pd.DataFrame({'name': ['Alice', 'Bob', 'Charlie'],'score': [85, 92, 78],'subject': ['math', 'English', 'MATH']
})# 示例1:字符串处理 - 统一大小写
df['subject'] = df['subject'].apply(str.upper)
print(df['subject'])# 示例2:数值转换 - 分数转等级
def score_to_grade(score):if score >= 90:return 'A'elif score >= 80:return 'B'elif score >= 70:return 'C'else:return 'D'df['grade'] = df['score'].apply(score_to_grade)
print(df[['score', 'grade']])

2. 对 DataFrame 使用 apply()(行或列级操作)

# 示例3:基于多列计算新值 - 计算综合评分
def calculate_final_score(row):base_score = row['score']# 根据科目给不同权重if row['subject'] == 'MATH':return base_score * 1.1else:return base_score * 1.05df['final_score'] = df.apply(calculate_final_score, axis=1)
print(df[['score', 'subject', 'final_score']])

3. 实际应用场景

# 示例4:数据清洗 - 处理异常值
def clean_age(age):if age < 0:return 0elif age > 150:return 150else:return ageages = pd.Series([-5, 25, 200, 30])
clean_ages = ages.apply(clean_age)
print(clean_ages)
# 示例5:日期处理
import datetime# 假设我们有天数数据,想转换成年龄段
def days_to_category(days):years = days / 365.25if years < 18:return '未成年'elif years < 60:return '成年人'else:return '老年人'days_series = pd.Series([365*16, 365*30, 365*70])
age_categories = days_series.apply(days_to_category)
print(age_categories)

4. 使用 lambda 函数(简洁写法)

# 快速实现简单逻辑
df['score_squared'] = df['score'].apply(lambda x: x**2)
df['is_high_score'] = df['score'].apply(lambda x: '是' if x >= 85 else '否')# 对多列操作
df['info'] = df.apply(lambda row: f"{row['name']}{row['subject']}成绩是{row['score']}", axis=1)

apply() 的优势

  1. 灵活性:可以应用任何 Python 函数
  2. 可读性:代码逻辑清晰
  3. 复用性:函数可以多次调用
  4. 简化复杂操作:将复杂逻辑封装起来

注意事项

  • 对于简单的数学运算,直接使用向量化操作更快
  • apply() 在处理大数据时可能较慢,因为它本质上是循环
  • 尽量使用向量化操作替代 apply() 以获得更好的性能

向量化后面会提到
lambda当函数比较简单的时候, 没有必要创建一个def 一个函数, 可以使用lambda表达式创建匿名函数


向量化函数

向量化函数是指能够一次性对整个数组或数据序列的所有元素进行操作的函数,而不是逐个元素循环处理。

与普通循环的区别

import pandas as pd
import numpy as np# 创建示例数据
data = pd.Series([1, 2, 3, 4, 5])#  普通循环方式(低效)
result_loop = []
for x in data:result_loop.append(x ** 2)#  向量化方式(高效)
result_vectorized = data ** 2  # 或 data.pow(2)

Pandas 中的向量化函数类型

1. 数学运算(自动向量化)

s = pd.Series([1, 2, 3, 4, 5])# 基本运算
s + 10      # 所有元素加10
s * 2       # 所有元素乘以2
s ** 2      # 所有元素平方# 三角函数
np.sin(s)   # 所有元素求正弦
np.log(s)   # 所有元素求对数

2. 统计函数

s = pd.Series([1, 2, 3, 4, 5])# 这些都是向量化的聚合函数
s.sum()     # 求和
s.mean()    # 平均值  
s.std()     # 标准差
s.max()     # 最大值
s.min()     # 最小值

3. 字符串向量化操作

names = pd.Series([' Alice', 'BOB ', 'Charlie '])# 字符串方法都是向量化的
names.str.strip()           # 去除首尾空格
names.str.lower()          # 转小写
names.str.upper()          # 转大写
names.str.len()            # 计算字符串长度
names.str.contains('a')    # 检查是否包含'a'

4. 条件向量化操作

scores = pd.Series([85, 92, 78, 95, 67])# 使用 np.where 进行向量化条件判断
grades = np.where(scores >= 90, 'A', np.where(scores >= 80, 'B', 'C'))# 使用 pandas 的 mask
pass_fail = scores.where(scores >= 70, 'Fail')

自定义向量化函数(注意:这不是真正的向量化)

1. 使用 np.vectorize

import numpy as np# 定义普通函数
def custom_func(x):if x > 80:return x * 1.1else:return x * 0.9# 向量化这个函数
vectorized_func = np.vectorize(custom_func)# 应用到整个 Series
scores = pd.Series([85, 92, 78, 95, 67])
result = vectorized_func(scores)

2. 使用 pd.Series.apply

# 注意:apply 本质上是循环,不是真正的向量化
result = scores.apply(custom_func)  # 较慢

向量化的优势

1. 性能优势

import time
import numpy as np# 大数据集测试
large_data = pd.Series(np.random.randn(1000000))# 向量化方式
start = time.time()
result1 = large_data ** 2
vectorized_time = time.time() - start# 循环方式(非常慢,仅作对比)
start = time.time()
result2 = [x**2 for x in large_data]
loop_time = time.time() - startprint(f"向量化: {vectorized_time:.4f}秒")
print(f"循环: {loop_time:.4f}秒")
# 向量化通常快10-100倍

2. 代码简洁性

# 向量化:一行代码
normalized = (data - data.mean()) / data.std()# 循环:多行代码
mean_val = data.mean()
std_val = data.std()
normalized = []
for x in data:normalized.append((x - mean_val) / std_val)

常见的向量化函数示例

# 1. 数据标准化
data = pd.Series([10, 20, 30, 40])
normalized = (data - data.min()) / (data.max() - data.min())# 2. 条件赋值
scores = pd.Series([85, 92, 78, 95])
# 向量化条件
level = np.where(scores >= 90, '高级', np.where(scores >= 80, '中级', '初级'))# 3. 时间序列处理
dates = pd.date_range('2023-01-01', periods=5)
# 向量化提取日期信息
years = dates.year
months = dates.month
days = dates.day# 4. 缺失值处理
data_with_nan = pd.Series([1, 2, np.nan, 4, 5])
# 向量化填充
filled = data_with_nan.fillna(0)
# 向量化检测
is_null = data_with_nan.isna()

向量化 vs apply vs vectorize

特性/方法真正向量化操作pd.Series.applynumpy.vectorize
实现方式底层C/C++实现,同时处理整个数组逐元素应用函数的Python循环将标量函数包装成可处理数组的函数
性能最高中等略高于apply
返回类型保持原类型(pd.Series/np.ndarray)pd.Series(保持索引)np.ndarray
适用场景简单数学运算、NumPy函数数据清洗、复杂逻辑处理复杂自定义函数、多数组操作
代码示例s ** 2
np.sqrt(s)
s.apply(lambda x: x**2)@np.vectorize
def func(x): return x**2
保持pandas特性是(如果是pandas对象)是(索引、标签等)否(返回numpy数组)
多参数支持有限通过额外参数是(多个数组参数)
推荐使用优先级1(首选)3(需要pandas特性时)2(复杂函数时)

总结

  • 简单运算优先使用向量化操作
  • 复杂逻辑才使用 apply
  • 尽量避免 Python 循环

您总结得非常好!这确实是 Pandas 分组操作(GroupBy)的五大核心类型。您已经抓住了核心框架,我来为您系统性地补充、完善和深化这些概念,帮助您彻底理解 groupby 的完整逻辑和应用场景。


Pandas 分组操作五大核心类型

示例数据:

import pandas as pd
import numpy as npdf = pd.DataFrame({'地区': ['北京', '上海', '北京', '上海', '广州', '广州'],'产品': ['A', 'B', 'A', 'B', 'A', 'B'],'销售额': [100, 150, 200, 180, 120, 90],'数量': [10, 15, 20, 12, 10, 6],'日期': pd.to_datetime(['2023-01-01', '2023-01-02', '2023-02-01', '2023-02-02', '2023-01-05', '2023-02-10'])
})
print(df)

一、分组(Split)—— groupby()

核心作用:

将一个 DataFrame 拆分成多个子组(sub-groups),每个子组是一个“小表格”,后续操作在这些小组上进行。

本质:

groupby 返回的是一个 DataFrameGroupBy 对象,它还没有计算,只是“承诺”了分组方式。

grouped = df.groupby('地区')
print(type(grouped))  # <class 'pandas.core.groupby.generic.DataFrameGroupBy'>

分组方式详解:

分组方式示例说明
单列分组df.groupby('地区')最常见
多列分组df.groupby(['地区', '产品'])生成多级索引
按函数分组df.groupby(df['日期'].dt.month)按月份分组
按索引分组df.groupby(level=0)按行索引分组
自定义规则df.groupby(df['销售额'] > 150)布尔条件分组

技巧:可以用 grouped.groups 查看每个组包含哪些行索引。


二、聚合(Aggregate)—— agg() / aggregate()

核心作用:

对每个分组计算一个汇总值,结果是一个更小的 DataFrame 或 Series(行数 ≤ 原始数据)。

特点:

  • 每个组 → 一个值
  • 降维操作

常用聚合函数:

# 单函数聚合
df.groupby('地区')['销售额'].sum()        # 每个地区的总销售额# 多函数聚合
df.groupby('地区')['销售额'].agg(['sum', 'mean', 'count'])# 对不同列使用不同函数
df.groupby('地区').agg({'销售额': ['sum', 'mean'],'数量': 'sum'
})

自定义聚合函数:

def range_func(x):return x.max() - x.min()df.groupby('地区')['销售额'].agg(range_func)

注意agg 可以接受函数名字符串、函数对象、列表、字典,非常灵活!


三、转换(Transform)—— transform()

核心作用:

对每个分组进行计算,但返回结果的形状与原始数据一致(行数不变),用于组内标准化、填充、打分等。

本质:

  • 每个组 → 一组值(长度 = 该组行数)
  • 不降维,保持原始结构

经典应用场景:

# 1. 组内标准化(Z-score)
df['销售额_标准化'] = df.groupby('地区')['销售额'].transform(lambda x: (x - x.mean()) / x.std()
)# 2. 填充组内缺失值(用组均值)
df['销售额_填充'] = df.groupby('地区')['销售额'].transform(lambda x: x.fillna(x.mean())
)# 3. 计算组内排名
df['销售额_组内排名'] = df.groupby('地区')['销售额'].transform('rank'
)# 4. 创建新特征:组均值作为新列
df['地区平均销售额'] = df.groupby('地区')['销售额'].transform('mean')

transform特征工程的利器!


四、过滤(Filter)—— filter()

核心作用:

根据组的整体属性保留或丢弃某些组,而不是对单行过滤。

query / boolean indexing 的区别:

  • df[df['销售额'] > 100]:按行条件过滤
  • df.groupby('地区').filter(lambda x: x['销售额'].sum() > 250):按组条件过滤

实例:

# 保留总销售额 > 250 的地区
high_sales_regions = df.groupby('地区').filter(lambda group: group['销售额'].sum() > 250
)# 保留记录数 >= 2 的组
df.groupby('地区').filter(lambda x: len(x) >= 2)# 保留组内方差较大的组
df.groupby('地区').filter(lambda x: x['销售额'].std() > 50)

filter 返回的是原始数据的子集,不是统计结果。


五、应用(Apply)—— apply()

核心作用:

灵活、最强大的操作,可以对每个分组的 完整 DataFrame 进行任意操作。

aggtransform 的区别:

方法输入输出要求返回形状
aggSeries/数值标量降维
transformSeries同长度Series不降维
applyDataFrame任意灵活

应用场景:

# 1. 组内排序
df.groupby('地区').apply(lambda group: group.sort_values('销售额', ascending=False)
)# 2. 返回复杂结构(如字典)
df.groupby('地区').apply(lambda g: {'总销售额': g['销售额'].sum(), '平均数量': g['数量'].mean()}
)# 3. 组内线性回归(示例)
from scipy.stats import linregress
def fit_trend(group):slope, _, _, _, _ = linregress(group.index, group['销售额'])return slope# 4. 返回 DataFrame(多行)
df.groupby('地区').apply(lambda g: g[['销售额', '数量']].assign(占比=lambda x: x['销售额']/x['销售额'].sum())
)

注意apply 可能较慢,尽量用 aggtransform 替代。


五大操作对比

类型方法输入单元输出形状典型用途
分组groupby()列/函数/索引GroupBy对象拆分数据
聚合agg()每组的列(Series)降维(1值/组)统计汇总
转换transform()每组的列(Series)同原始行数组内标准化、填充
过滤filter()整个组(DataFrame)行子集筛选重要组
应用apply()整个组(DataFrame)任意复杂自定义逻辑

总结

  1. 优先使用 aggtransform:它们性能好,语义清晰。
  2. transform 是特征工程的核心:学会用它创建“组内特征”。
  3. filter 用于数据预筛选:比如只分析大客户、活跃地区。
  4. apply 是最后的选择:当其他方法无法满足时再用。

实战示例

# 目标:分析各地区各产品的表现,并标准化销售额
result = (df.groupby(['地区', '产品']).apply(lambda g: g.assign(地区产品平均 = g['销售额'].mean(),标准化销售额 = (g['销售额'] - g['销售额'].mean()) / g['销售额'].std())).reset_index(drop=True)
)

数据透视表

数据透视表是一种交叉汇总表,它可以:

  • 按一个或多个字段分组
  • 对数值进行聚合计算(如求和、平均、计数等)
  • 将结果以二维表格的形式展示(行 × 列)

核心函数:pd.pivot_table()

基本语法:

pd.pivot_table(data,           # DataFrameindex,          # 行索引(分组依据)columns,        # 列(用于展开的分类)values,         # 要聚合的数值列aggfunc,        # 聚合函数(如 'sum', 'mean' 等)fill_value,     # 填充缺失值margins         # 是否添加总计行/列
)

实例

示例数据:

import pandas as pd
import numpy as npdf = pd.DataFrame({'地区': ['北京', '上海', '北京', '上海', '广州', '广州', '北京', '上海'],'产品': ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B'],'季度': ['Q1', 'Q1', 'Q2', 'Q2', 'Q1', 'Q2', 'Q1', 'Q1'],'销售额': [100, 150, 200, 180, 120, 90, 130, 160],'数量': [10, 15, 20, 12, 10, 6, 13, 16]
})print(df)

示例 1:基本透视表(按地区和产品汇总销售额)

pivot = pd.pivot_table(data=df,index='地区',           # 行:按地区分组columns='产品',         # 列:按产品展开values='销售额',        # 聚合的值aggfunc='sum'           # 聚合方式
)print(pivot)

输出:

产品      A    B
地区          
北京   430  NaN
上海   NaN  490
广州   120   90

说明:北京没有产品 B 的销售,所以是 NaN


示例 2:填充缺失值 + 添加总计

pivot = pd.pivot_table(data=df,index='地区',columns='产品',values='销售额',aggfunc='sum',fill_value=0,      # 把 NaN 替换为 0margins=True,      # 添加总计行和列margins_name='总计'
)print(pivot)

输出:

产品     A   B  总计
地区            
北京   430   0   430
广州   120  90   210
上海     0 490   490
总计   550 580  1130

示例 3:多级行索引(index 多列)

pivot = pd.pivot_table(data=df,index=['地区', '季度'],   # 多级行索引columns='产品',values='销售额',aggfunc='sum',fill_value=0
)print(pivot)

输出:

               A   B
地区 季度        
北京 Q1     230   0Q2     200   0
上海 Q1       0 310Q2       0 180
广州 Q1     120   0Q2       0  90

示例 4:多聚合函数 + 多值列

pivot = pd.pivot_table(data=df,index='地区',columns='产品',values=['销售额', '数量'],      # 多个值列aggfunc={'销售额': 'sum', '数量': 'mean'},  # 不同列用不同聚合fill_value=0
)print(pivot)

输出:

       销售额      数量    
产品      A   B    A    B
地区                  
北京   430   0  14.3  0.0
广州   120  90  10.0  6.0
上海     0 490   0.0 15.5

示例 5:使用 aggfunc 传入多个函数

pivot = pd.pivot_table(data=df,index='产品',values='销售额',aggfunc=['mean', 'sum', 'count'],margins=True
)print(pivot)

输出:

       mean   sum  count
产品                   
A     170.0   680      4
B     205.0   820      4
All   187.5  1500      8

pivot_table vs groupby

特性pivot_tablegroupby
输出形式二维交叉表(行×列)一维分组结果
易读性高(适合报表)
灵活性高(支持行列展开)极高(支持 transform/filter 等)
典型用途报表、可视化前处理数据清洗、特征工程

** 总结**:

  • 想生成“表格报表” → 用 pivot_table
  • 想做“数据处理流水线” → 用 groupby

实际应用场景

  1. 销售分析:按地区、产品、时间维度汇总销售额
  2. 用户行为分析:按用户分组统计访问次数、停留时间
  3. 财务报表:生成月度/季度汇总表
  4. A/B 测试:对比不同组的转化率、均值
  5. 数据探索:快速查看分类变量之间的关系

补充

  • 如果只想重塑数据而不聚合,用 df.pivot()(但要求索引唯一)
  • pivot_table自动处理重复索引(通过聚合)
  • 可以结合 plot() 直接可视化:
    pivot.plot(kind='bar', title='各地区产品销售额')
    

总结

方法适用场景
pd.pivot_table()生成交叉汇总表、做报表、探索数据关系
df.groupby()复杂分组操作、特征工程、数据清洗
http://www.dtcms.com/a/343414.html

相关文章:

  • C6.0:晶体管放大器的原理与应用(基极偏置篇)
  • 单词记忆-轻松记忆10个实用英语单词(13)
  • 【openGauss】1分钟掌握:openGauss活动会话CPU占用率获取
  • Java获取被nginx代理的emqx客户端真实ip
  • STM32F030/070芯片解密及应用
  • DAY 23|动态规划1
  • LeetCode234~258题解
  • 深入解析JUC线程间通信:使用ReentrantLock与Condition实现精准线程调度
  • 32、智能仓库管理与优化系统 (模拟) - /物流与仓储组件/warehouse-optimization-system
  • IPSec 与 IKE 核心知识点总结
  • 使用Python 创建虚拟环境的两种方式
  • 订单簿数据深度学习方法在大单发现应用
  • 让医学数据更直观——MedCalc 23.1.7 最新版使用体验
  • sageattention低比特量化注意力机制,比FlashAttention快5 倍
  • DeepSeek-V3.1 Claude Code: 革命性的AI编码助手详解与应用指南
  • 论文图片在要求dpi下,压缩尺寸
  • ES_预处理
  • java18学习笔记-Simple Web Server
  • 美国联邦调查局警告俄罗斯针对思科设备的网络间谍活动
  • 残差神经网络(ResNet)
  • 矫平机与纵剪:一条钢卷“变身”的全过程
  • 【UE5-Airsim】Windows10下安装UE5-Airsim的仿真环境
  • leetcode 1658 将x减到0的最小操作数
  • 同题异构解决leetcode第3646题下一个特殊回文数
  • Linux网络socket套接字(上)
  • linux 之virtio 的驱动框架
  • Motocycle 智能仪表盘
  • 白光干涉测量系统的复合相移三维重建和多视场形貌拼接的复现
  • 【自然语言处理与大模型】微调与RAG的区别
  • JavaScript基础语法five