本文目录导读:

我来详细讲解如何使用 Pandas 进行数据分组重构,包括多个实际案例。
基础数据准备
import pandas as pd
import numpy as np
# 创建示例数据
df = pd.DataFrame({
'日期': ['2024-01-01', '2024-01-01', '2024-01-02', '2024-01-02', '2024-01-03'],
'产品': ['A', 'B', 'A', 'B', 'A'],
'城市': ['北京', '上海', '北京', '上海', '北京'],
'销量': [100, 150, 120, 180, 110],
'金额': [1000, 1500, 1200, 1800, 1100]
})
print("原始数据:")
print(df)
案例一:分组统计与聚合
1 单列分组统计
# 按产品分组,计算销量总和
product_sum = df.groupby('产品')['销量'].sum()
print("各产品销量总和:")
print(product_sum)
# 按产品分组,计算多列统计量
product_stats = df.groupby('产品').agg({
'销量': ['sum', 'mean', 'max', 'min'],
'金额': ['sum', 'mean']
})
print("\n多指标统计:")
print(product_stats)
2 多列分组
# 按产品和城市分组
grouped = df.groupby(['产品', '城市'])['销量'].sum()
print("各产品在各城市的销量:")
print(grouped)
# 使用 reset_index() 转换回 DataFrame
grouped_df = df.groupby(['产品', '城市']).agg({
'销量': 'sum',
'金额': 'mean'
}).reset_index()
print("\n重置索引后:")
print(grouped_df)
案例二:数据透视表
1 基本透视表
# 创建透视表
pivot_table = pd.pivot_table(
df,
values='销量',
index='日期',
columns='产品',
aggfunc='sum',
fill_value=0
)
print("销量透视表:")
print(pivot_table)
# 多值透视表
multi_pivot = pd.pivot_table(
df,
values=['销量', '金额'],
index='日期',
columns='产品',
aggfunc='sum',
fill_value=0
)
print("\n多值透视表:")
print(multi_pivot)
2 复杂透视表
# 创建更复杂的数据
df_complex = pd.DataFrame({
'年份': [2023, 2023, 2023, 2023, 2024, 2024],
'季度': ['Q1', 'Q2', 'Q1', 'Q2', 'Q1', 'Q2'],
'产品': ['A', 'A', 'B', 'B', 'A', 'B'],
'销量': [100, 150, 200, 180, 120, 190],
'利润': [30, 45, 60, 54, 36, 57]
})
# 多层次透视表
complex_pivot = pd.pivot_table(
df_complex,
values='销量',
index=['年份', '季度'],
columns='产品',
aggfunc='sum',
fill_value=0,
margins=True, # 添加汇总行/列
margins_name='合计'
)
print("多层次透视表(含汇总):")
print(complex_pivot)
案例三:分组后的数据重构
1 stack/unstack 操作
# 先设置索引
df_indexed = df.set_index(['日期', '产品', '城市'])
# unstack - 将索引转为列
unstacked = df_indexed.unstack(level='产品')
print("unstack 转换:")
print(unstacked)
# stack - 将列转为索引
stacked = unstacked.stack(level='产品')
print("\nstack 恢复:")
print(stacked)
2 melt 宽表转长表
# 创建宽表数据
df_wide = pd.DataFrame({
'日期': ['2024-01-01', '2024-01-02'],
'产品A销量': [100, 120],
'产品B销量': [150, 180],
'产品A金额': [1000, 1200],
'产品B金额': [1500, 1800]
})
print("宽表数据:")
print(df_wide)
# 转换为长表
df_long = pd.melt(
df_wide,
id_vars=['日期'],
var_name='产品_指标',
value_name='值'
)
print("\n转换后的长表:")
print(df_long)
# 进一步拆分产品名称和指标
df_long[['产品', '指标']] = df_long['产品_指标'].str.extract(r'产品(.)(.+)')
df_long = df_long.drop('产品_指标', axis=1)
print("\n拆分后的数据:")
print(df_long)
案例四:分组后的自定义处理
1 使用 apply 自定义函数
# 定义自定义聚合函数
def custom_agg(x):
return pd.Series({
'总和': x.sum(),
'均值': x.mean(),
'标准差': x.std(),
'变异系数': x.std() / x.mean() if x.mean() != 0 else 0
})
# 应用自定义函数
custom_result = df.groupby('产品')['销量'].apply(custom_agg)
print("自定义聚合结果:")
print(custom_result)
2 分组排名
# 按产品分组,计算每日销量排名
df['销量排名'] = df.groupby('产品')['销量'].rank(ascending=False)
print("各产品销量排名:")
print(df[['日期', '产品', '销量', '销量排名']])
# 分组计算累计百分比
df['累计销量'] = df.groupby('产品')['销量'].cumsum()
df['累计百分比'] = df.groupby('产品')['销量'].apply(
lambda x: x.cumsum() / x.sum() * 100
)
print("\n累计分析:")
print(df[['日期', '产品', '销量', '累计销量', '累计百分比']])
案例五:实际应用场景
1 销售数据分析
# 模拟销售数据
np.random.seed(42)
sales_data = pd.DataFrame({
'日期': pd.date_range('2024-01-01', periods=100, freq='D'),
'门店': np.random.choice(['门店A', '门店B', '门店C'], 100),
'产品': np.random.choice(['手机', '电脑', '平板'], 100),
'销量': np.random.randint(1, 30, 100),
'单价': np.random.choice([2999, 3999, 4999, 5999], 100)
})
sales_data['销售额'] = sales_data['销量'] * sales_data['单价']
# 月度门店销售汇总
sales_data['月份'] = sales_data['日期'].dt.month
monthly_store = pd.pivot_table(
sales_data,
values=['销量', '销售额'],
index='月份',
columns='门店',
aggfunc='sum',
fill_value=0
)
print("月度门店销售汇总:")
print(monthly_store)
# 产品销量排行
product_rank = sales_data.groupby('产品').agg({
'销量': 'sum',
'销售额': 'sum',
'门店': lambda x: x.nunique()
}).rename(columns={'门店': '门店数量'})
product_rank['销量占比'] = product_rank['销量'] / product_rank['销量'].sum() * 100
product_rank = product_rank.sort_values('销售额', ascending=False)
print("\n产品销售排行:")
print(product_rank)
2 库存分析
# 创建库存数据
inventory_data = pd.DataFrame({
'产品': ['A', 'A', 'B', 'B', 'C', 'C'],
'仓库': ['北京仓', '上海仓', '北京仓', '上海仓', '北京仓', '上海仓'],
'入库量': [100, 150, 200, 180, 120, 90],
'出库量': [50, 80, 120, 100, 60, 40],
'安全库存': [80, 100, 120, 100, 60, 50]
})
# 计算库存状态
inventory_data['当前库存'] = inventory_data['入库量'] - inventory_data['出库量']
inventory_data['库存状态'] = inventory_data.apply(
lambda row: '正常' if row['当前库存'] >= row['安全库存'] else '补货',
axis=1
)
# 按产品和仓库分组
inventory_summary = inventory_data.groupby(['产品', '仓库']).agg({
'当前库存': 'sum',
'安全库存': 'sum',
'入库量': 'sum'
}).reset_index()
inventory_summary['补货建议'] = inventory_summary['安全库存'] - inventory_summary['当前库存']
inventory_summary['补货建议'] = inventory_summary['补货建议'].clip(lower=0)
print("库存分析结果:")
print(inventory_summary)
性能优化技巧
# 大数据集处理优化
def optimized_groupby(df, group_cols, agg_dict):
"""
优化分组聚合性能
"""
# 使用分类数据类型
for col in group_cols:
if df[col].dtype == 'object':
df[col] = df[col].astype('category')
# 使用 transform 代替 apply
result = df.groupby(group_cols, observed=True).agg(agg_dict)
return result
# 使用示例
df_optimized = df.copy()
result = optimized_groupby(
df_optimized,
['产品'],
{'销量': 'sum', '金额': 'mean'}
)
print("优化后的分组结果:")
print(result)
这些案例涵盖了 Pandas 数据分组重构的主要场景,核心要点:
- groupby + agg:最常用的分组聚合方式
- pivot_table:创建透视表进行多维度分析
- stack/unstack:行列转换
- melt:宽表转长表
- 自定义函数:处理复杂的分组逻辑
根据实际需求选择合适的重构方法,可以提高数据分析效率。