简体中文 繁體中文 English 日本語 Deutsch 한국 사람 بالعربية TÜRKÇE português คนไทย Français

站内搜索

搜索

活动公告

11-02 12:46
10-23 09:32
通知:本站资源由网友上传分享,如有违规等问题请到版务模块进行投诉,将及时处理!
10-23 09:31
10-23 09:28
通知:签到时间调整为每日4:00(东八区)
10-23 09:26

pandas分组输出实战指南从基础语法到高级应用全面解决数据分析中的分组统计与结果展示问题助你提升数据处理效率

3万

主题

317

科技点

3万

积分

大区版主

木柜子打湿

积分
31893

财Doro三倍冰淇淋无人之境【一阶】立华奏小樱(小丑装)⑨的冰沙以外的星空【二阶】

发表于 2025-10-3 20:30:01 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
引言

Pandas是Python数据分析的核心库之一,而分组(GroupBy)操作是数据分析中最常用、最强大的功能之一。通过分组操作,我们可以对数据集进行灵活的切片、切块、汇总等操作,从而揭示数据中隐藏的模式和关系。本文将从基础语法到高级应用,全面介绍pandas中的分组操作,帮助读者掌握这一重要技能,提升数据处理效率。

基础语法:groupby的基本用法

Pandas中的groupby操作遵循”拆分-应用-合并”的模式。首先,我们需要了解groupby方法的基本语法:
  1. import pandas as pd
  2. # 创建示例DataFrame
  3. data = {
  4.     'Department': ['HR', 'IT', 'HR', 'IT', 'Finance', 'Finance', 'HR', 'IT'],
  5.     'Employee': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Henry'],
  6.     'Salary': [70000, 80000, 75000, 90000, 85000, 82000, 72000, 95000],
  7.     'Years': [3, 5, 4, 6, 7, 5, 2, 8]
  8. }
  9. df = pd.DataFrame(data)
  10. # 按Department列分组
  11. grouped = df.groupby('Department')
  12. # 查看分组结果
  13. print(grouped.groups)
复制代码

在这个例子中,我们首先创建了一个包含部门、员工、薪资和工作年限的DataFrame。然后使用groupby方法按’Department’列进行分组。grouped.groups会显示每个分组包含的行索引。

需要注意的是,groupby操作本身不会立即计算任何结果,而是创建一个GroupBy对象,这个对象包含了后续计算所需的所有信息。这种”惰性计算”的方式可以提高效率,特别是处理大型数据集时。

我们也可以按多列进行分组,只需将列名列表传递给groupby方法:
  1. # 按Department和Years列分组
  2. multi_grouped = df.groupby(['Department', 'Years'])
  3. print(multi_grouped.groups)
复制代码

常用聚合函数:sum, mean, count等

分组后,我们通常需要对每个组应用聚合函数,如求和、平均值、计数等。pandas提供了多种内置聚合函数:
  1. # 计算每个部门的平均薪资
  2. avg_salary = df.groupby('Department')['Salary'].mean()
  3. print("平均薪资:\n", avg_salary)
  4. # 计算每个部门的薪资总和
  5. total_salary = df.groupby('Department')['Salary'].sum()
  6. print("\n薪资总和:\n", total_salary)
  7. # 计算每个部门的员工数量
  8. employee_count = df.groupby('Department')['Employee'].count()
  9. print("\n员工数量:\n", employee_count)
  10. # 计算每个部门的薪资最大值、最小值和标准差
  11. salary_stats = df.groupby('Department')['Salary'].agg(['max', 'min', 'std'])
  12. print("\n薪资统计:\n", salary_stats)
复制代码

这些聚合函数可以直接应用于GroupBy对象,也可以通过agg方法一次性应用多个函数。除了上述函数外,常用的聚合函数还包括:

• median(): 计算中位数
• var(): 计算方差
• first(): 获取第一个值
• last(): 获取最后一个值
• size(): 获取组的大小(包括NaN值)
  1. # 计算每个部门的中位数薪资和工作年限
  2. median_values = df.groupby('Department')[['Salary', 'Years']].median()
  3. print("中位数薪资和工作年限:\n", median_values)
  4. # 获取每个部门的第一位员工和薪资
  5. first_values = df.groupby('Department').first()
  6. print("\n每个部门的第一位员工:\n", first_values)
  7. # 获取每个部门的大小(包括NaN值)
  8. group_sizes = df.groupby('Department').size()
  9. print("\n每个部门的大小:\n", group_sizes)
复制代码

多重分组:按多列分组

在实际数据分析中,我们经常需要按多个列进行分组,以获得更细粒度的分析结果。多重分组可以让我们从不同维度审视数据:
  1. # 创建更详细的示例数据
  2. data = {
  3.     'Department': ['HR', 'IT', 'HR', 'IT', 'Finance', 'Finance', 'HR', 'IT', 'HR', 'IT'],
  4.     'Team': ['Recruitment', 'Development', 'Training', 'Support', 'Accounting', 'Investment', 'Recruitment', 'Development', 'Training', 'Support'],
  5.     'Employee': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack'],
  6.     'Salary': [70000, 80000, 75000, 90000, 85000, 82000, 72000, 95000, 78000, 92000],
  7.     'Years': [3, 5, 4, 6, 7, 5, 2, 8, 3, 6],
  8.     'Gender': ['F', 'M', 'M', 'M', 'F', 'M', 'F', 'M', 'F', 'M']
  9. }
  10. df = pd.DataFrame(data)
  11. # 按部门和团队分组,计算平均薪资
  12. dept_team_avg = df.groupby(['Department', 'Team'])['Salary'].mean()
  13. print("部门和团队的平均薪资:\n", dept_team_avg)
  14. # 按部门和性别分组,计算员工数量和平均工作年限
  15. dept_gender_stats = df.groupby(['Department', 'Gender']).agg({
  16.     'Employee': 'count',
  17.     'Years': 'mean'
  18. })
  19. print("\n部门和性别的统计:\n", dept_gender_stats)
复制代码

多重分组的结果是一个具有多级索引的Series或DataFrame。我们可以使用unstack方法将多级索引转换为列,使数据更易于阅读和分析:
  1. # 将部门和团队的分组结果转换为更易读的格式
  2. dept_team_avg_unstacked = df.groupby(['Department', 'Team'])['Salary'].mean().unstack()
  3. print("转换后的部门和团队平均薪资:\n", dept_team_avg_unstacked)
  4. # 将部门和性别的统计结果转换为更易读的格式
  5. dept_gender_stats_unstacked = df.groupby(['Department', 'Gender']).agg({
  6.     'Employee': 'count',
  7.     'Years': 'mean'
  8. }).unstack()
  9. print("\n转换后的部门和性别统计:\n", dept_gender_stats_unstacked)
复制代码

分组迭代:遍历分组对象

有时我们需要对每个分组执行特定的操作,这时可以通过遍历GroupBy对象来实现。GroupBy对象是可迭代的,每次迭代返回一个包含组名和对应DataFrame的元组:
  1. # 遍历每个部门
  2. for name, group in df.groupby('Department'):
  3.     print(f"部门: {name}")
  4.     print(group)
  5.     print("\n")
  6. # 遍历部门和团队
  7. for (dept, team), group in df.groupby(['Department', 'Team']):
  8.     print(f"部门: {dept}, 团队: {team}")
  9.     print(group)
  10.     print("\n")
复制代码

通过遍历分组,我们可以对每个组执行复杂的自定义操作:
  1. # 计算每个部门的薪资范围
  2. for name, group in df.groupby('Department'):
  3.     salary_range = group['Salary'].max() - group['Salary'].min()
  4.     print(f"{name}部门的薪资范围: {salary_range}")
  5. # 找出每个部门薪资最高的员工
  6. for name, group in df.groupby('Department'):
  7.     highest_paid = group.loc[group['Salary'].idxmax()]
  8.     print(f"{name}部门薪资最高的员工: {highest_paid['Employee']}, 薪资: {highest_paid['Salary']}")
复制代码

自定义聚合函数:使用agg和apply

虽然pandas提供了许多内置聚合函数,但有时我们需要使用自定义的聚合逻辑。这时可以使用agg和apply方法。

agg方法主要用于聚合操作,可以接受函数名、函数列表或字典:
  1. # 使用自定义函数计算薪资范围
  2. def salary_range(series):
  3.     return series.max() - series.min()
  4. # 对每个部门应用自定义聚合函数
  5. dept_salary_range = df.groupby('Department')['Salary'].agg(salary_range)
  6. print("每个部门的薪资范围:\n", dept_salary_range)
  7. # 使用多个聚合函数,包括自定义函数
  8. dept_stats = df.groupby('Department')['Salary'].agg(['mean', 'std', salary_range])
  9. print("\n每个部门的薪资统计:\n", dept_stats)
  10. # 对不同的列使用不同的聚合函数
  11. multi_agg = df.groupby('Department').agg({
  12.     'Salary': ['mean', 'max', salary_range],
  13.     'Years': ['mean', 'min'],
  14.     'Employee': 'count'
  15. })
  16. print("\n多重聚合:\n", multi_agg)
复制代码

apply方法更加灵活,可以应用任何函数到每个分组:
  1. # 使用apply方法计算每个部门的薪资差异百分比
  2. def salary_diff_pct(group):
  3.     group['Salary_Pct_Diff'] = (group['Salary'] - group['Salary'].mean()) / group['Salary'].mean() * 100
  4.     return group
  5. # 应用函数并显示结果
  6. df_with_pct = df.groupby('Department').apply(salary_diff_pct)
  7. print("带有薪资差异百分比的数据:\n", df_with_pct[['Department', 'Employee', 'Salary', 'Salary_Pct_Diff']])
  8. # 使用apply方法获取每个部门薪资排名前二的员工
  9. def top_earners(group, n=2):
  10.     return group.nlargest(n, 'Salary')
  11. top_employees = df.groupby('Department').apply(top_earners)
  12. print("\n每个部门薪资排名前二的员工:\n", top_employees[['Department', 'Employee', 'Salary']])
复制代码

分组转换:transform方法

transform方法与apply方法类似,但它返回一个与原始DataFrame形状相同的对象,这使得它非常适合于添加派生列:
  1. # 计算每个部门的平均薪资,并将结果添加到每一行
  2. df['Dept_Avg_Salary'] = df.groupby('Department')['Salary'].transform('mean')
  3. print("带有部门平均薪资的数据:\n", df[['Department', 'Employee', 'Salary', 'Dept_Avg_Salary']])
  4. # 计算每个员工薪资与部门平均薪资的差异
  5. df['Salary_vs_Dept_Avg'] = df['Salary'] - df.groupby('Department')['Salary'].transform('mean')
  6. print("\n薪资与部门平均的差异:\n", df[['Department', 'Employee', 'Salary', 'Salary_vs_Dept_Avg']])
  7. # 计算每个员工在部门内的薪资排名
  8. df['Salary_Rank_in_Dept'] = df.groupby('Department')['Salary'].rank(ascending=False)
  9. print("\n部门内薪资排名:\n", df[['Department', 'Employee', 'Salary', 'Salary_Rank_in_Dept']].sort_values(['Department', 'Salary_Rank_in_Dept']))
复制代码

transform方法特别适用于数据标准化和归一化:
  1. # 对每个部门的薪资进行标准化(Z-score)
  2. def z_score(x):
  3.     return (x - x.mean()) / x.std()
  4. df['Salary_Z_Score'] = df.groupby('Department')['Salary'].transform(z_score)
  5. print("薪资标准化(Z-score):\n", df[['Department', 'Employee', 'Salary', 'Salary_Z_Score']])
  6. # 对每个部门的薪资进行最小-最大归一化
  7. def min_max_norm(x):
  8.     return (x - x.min()) / (x.max() - x.min())
  9. df['Salary_Normalized'] = df.groupby('Department')['Salary'].transform(min_max_norm)
  10. print("\n薪资归一化(0-1):\n", df[['Department', 'Employee', 'Salary', 'Salary_Normalized']])
复制代码

分组过滤:filter方法

filter方法允许我们根据分组级别的条件来过滤数据。它返回一个DataFrame,其中只包含满足条件的组:
  1. # 只保留员工数量大于2的部门
  2. large_depts = df.groupby('Department').filter(lambda x: len(x) > 2)
  3. print("员工数量大于2的部门:\n", large_depts)
  4. # 只保留平均薪资大于80000的部门
  5. high_salary_depts = df.groupby('Department').filter(lambda x: x['Salary'].mean() > 80000)
  6. print("\n平均薪资大于80000的部门:\n", high_salary_depts)
  7. # 只保留有至少一名女性员工的部门
  8. depts_with_female = df.groupby('Department').filter(lambda x: (x['Gender'] == 'F').any())
  9. print("\n有女性员工的部门:\n", depts_with_female)
复制代码

filter方法也可以与自定义函数结合使用,实现更复杂的过滤逻辑:
  1. # 自定义过滤函数:保留薪资标准差大于5000的部门
  2. def high_salary_variance(group):
  3.     return group['Salary'].std() > 5000
  4. varied_depts = df.groupby('Department').filter(high_salary_variance)
  5. print("薪资标准差大于5000的部门:\n", varied_depts)
  6. # 自定义过滤函数:保留薪资和工作年限相关性大于0.5的部门
  7. def high_corr(group):
  8.     return group['Salary'].corr(group['Years']) > 0.5
  9. corr_depts = df.groupby('Department').filter(high_corr)
  10. print("\n薪资和工作年限相关性大于0.5的部门:\n", corr_depts)
复制代码

分组排序:sort_values在分组中的应用

在分组操作中,排序是一个常见需求。我们可以对分组后的结果进行排序,也可以在每个组内进行排序:
  1. # 按部门分组后,按平均薪资降序排序
  2. dept_avg_salary = df.groupby('Department')['Salary'].mean().sort_values(ascending=False)
  3. print("按平均薪资降序排列的部门:\n", dept_avg_salary)
  4. # 在每个部门内,按薪资降序排序
  5. sorted_within_dept = df.groupby('Department', group_keys=False).apply(lambda x: x.sort_values('Salary', ascending=False))
  6. print("\n每个部门内按薪资降序排列:\n", sorted_within_dept[['Department', 'Employee', 'Salary']])
复制代码

我们也可以使用rank方法在组内进行排名:
  1. # 计算每个员工在部门内的薪资排名
  2. df['Salary_Rank'] = df.groupby('Department')['Salary'].rank(ascending=False)
  3. print("部门内薪资排名:\n", df[['Department', 'Employee', 'Salary', 'Salary_Rank']].sort_values(['Department', 'Salary_Rank']))
  4. # 计算每个员工在部门内的工作年限排名(百分比)
  5. df['Years_Pct_Rank'] = df.groupby('Department')['Years'].rank(pct=True)
  6. print("\n部门内工作年限百分比排名:\n", df[['Department', 'Employee', 'Years', 'Years_Pct_Rank']].sort_values(['Department', 'Years']))
复制代码

高级应用:透视表和交叉表

透视表(pivot_table)和交叉表(crosstab)是pandas中强大的数据重塑工具,它们可以看作是分组操作的高级应用。

透视表允许我们以电子表格的方式查看数据,其中一个或多个字段作为行,一个或多个字段作为列,以及一个聚合函数填充值:
  1. # 创建更复杂的示例数据
  2. data = {
  3.     'Department': ['HR', 'IT', 'HR', 'IT', 'Finance', 'Finance', 'HR', 'IT', 'HR', 'IT', 'Finance', 'HR'],
  4.     'Team': ['Recruitment', 'Development', 'Training', 'Support', 'Accounting', 'Investment', 'Recruitment', 'Development', 'Training', 'Support', 'Accounting', 'Training'],
  5.     'Employee': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank', 'Grace', 'Henry', 'Ivy', 'Jack', 'Kelly', 'Liam'],
  6.     'Salary': [70000, 80000, 75000, 90000, 85000, 82000, 72000, 95000, 78000, 92000, 87000, 76000],
  7.     'Years': [3, 5, 4, 6, 7, 5, 2, 8, 3, 6, 8, 4],
  8.     'Gender': ['F', 'M', 'M', 'M', 'F', 'M', 'F', 'M', 'F', 'M', 'F', 'M'],
  9.     'Performance': ['Good', 'Excellent', 'Good', 'Excellent', 'Excellent', 'Good', 'Average', 'Excellent', 'Good', 'Excellent', 'Excellent', 'Good']
  10. }
  11. df = pd.DataFrame(data)
  12. # 创建透视表:部门为行,团队为列,值为平均薪资
  13. pivot_dept_team = pd.pivot_table(df, values='Salary', index='Department', columns='Team', aggfunc='mean')
  14. print("部门-团队平均薪资透视表:\n", pivot_dept_team)
  15. # 创建透视表:部门和性别为行,绩效为列,值为员工数量
  16. pivot_dept_gender_perf = pd.pivot_table(df, values='Employee', index=['Department', 'Gender'], columns='Performance', aggfunc='count', fill_value=0)
  17. print("\n部门-性别-绩效员工数量透视表:\n", pivot_dept_gender_perf)
  18. # 创建透视表:添加 margins 参数显示总计
  19. pivot_with_margins = pd.pivot_table(df, values='Salary', index='Department', columns='Performance', aggfunc='mean', margins=True)
  20. print("\n带总计的部门-绩效平均薪资透视表:\n", pivot_with_margins)
复制代码

交叉表是计算两个(或多个)因素之间频率的特殊透视表:
  1. # 创建部门和性别的交叉表
  2. cross_dept_gender = pd.crosstab(df['Department'], df['Gender'])
  3. print("部门-性别交叉表:\n", cross_dept_gender)
  4. # 创建部门和绩效的交叉表,并添加行和列的百分比
  5. cross_dept_perf = pd.crosstab(df['Department'], df['Performance'], margins=True)
  6. print("\n部门-绩效交叉表(带总计):\n", cross_dept_perf)
  7. # 创建部门和绩效的交叉表,并标准化为行百分比
  8. cross_dept_perf_pct = pd.crosstab(df['Department'], df['Performance'], normalize='index')
  9. print("\n部门-绩效交叉表(行百分比):\n", cross_dept_perf_pct)
复制代码

性能优化:提高分组操作效率的技巧

当处理大型数据集时,分组操作可能会变得很慢。以下是一些提高分组操作效率的技巧:
  1. # 创建更大的示例数据集
  2. import numpy as np
  3. np.random.seed(42)
  4. n = 1000000  # 100万行数据
  5. departments = ['HR', 'IT', 'Finance', 'Marketing', 'Operations']
  6. teams = {
  7.     'HR': ['Recruitment', 'Training', 'Compensation'],
  8.     'IT': ['Development', 'Support', 'Infrastructure'],
  9.     'Finance': ['Accounting', 'Investment', 'Analysis'],
  10.     'Marketing': ['Digital', 'Content', 'Brand'],
  11.     'Operations': ['Logistics', 'Quality', 'Production']
  12. }
  13. data = {
  14.     'Department': np.random.choice(departments, n),
  15.     'Team': [np.random.choice(teams[dept]) for dept in np.random.choice(departments, n)],
  16.     'Salary': np.random.normal(75000, 15000, n).astype(int),
  17.     'Years': np.random.randint(1, 20, n),
  18.     'Gender': np.random.choice(['M', 'F'], n),
  19.     'Performance': np.random.choice(['Average', 'Good', 'Excellent'], n, p=[0.2, 0.5, 0.3])
  20. }
  21. large_df = pd.DataFrame(data)
  22. # 技巧1:使用category数据类型优化内存使用和性能
  23. print("优化前的内存使用:")
  24. print(large_df.memory_usage(deep=True))
  25. # 将字符串列转换为category类型
  26. for col in ['Department', 'Team', 'Gender', 'Performance']:
  27.     large_df[col] = large_df[col].astype('category')
  28. print("\n优化后的内存使用:")
  29. print(large_df.memory_usage(deep=True))
  30. # 技巧2:使用更高效的聚合方法
  31. import time
  32. # 方法1:使用agg
  33. start_time = time.time()
  34. result1 = large_df.groupby(['Department', 'Team'])['Salary'].agg(['mean', 'std'])
  35. time1 = time.time() - start_time
  36. print(f"\n使用agg方法的时间:{time1:.4f}秒")
  37. # 方法2:使用多个单独的聚合
  38. start_time = time.time()
  39. result2_mean = large_df.groupby(['Department', 'Team'])['Salary'].mean()
  40. result2_std = large_df.groupby(['Department', 'Team'])['Salary'].std()
  41. result2 = pd.DataFrame({'mean': result2_mean, 'std': result2_std})
  42. time2 = time.time() - start_time
  43. print(f"使用多个单独聚合的时间:{time2:.4f}秒")
  44. # 技巧3:避免在循环中使用groupby
  45. # 不好的方法
  46. start_time = time.time()
  47. results = []
  48. for dept in large_df['Department'].unique():
  49.     dept_df = large_df[large_df['Department'] == dept]
  50.     for team in dept_df['Team'].unique():
  51.         team_df = dept_df[dept_df['Team'] == team]
  52.         results.append({
  53.             'Department': dept,
  54.             'Team': team,
  55.             'Avg_Salary': team_df['Salary'].mean()
  56.         })
  57. result3 = pd.DataFrame(results)
  58. time3 = time.time() - start_time
  59. print(f"\n在循环中使用groupby的时间:{time3:.4f}秒")
  60. # 好的方法
  61. start_time = time.time()
  62. result4 = large_df.groupby(['Department', 'Team'])['Salary'].mean().reset_index()
  63. result4.columns = ['Department', 'Team', 'Avg_Salary']
  64. time4 = time.time() - start_time
  65. print(f"直接使用groupby的时间:{time4:.4f}秒")
  66. # 技巧4:使用numba加速自定义聚合函数
  67. from numba import jit
  68. @jit(nopython=True)
  69. def weighted_mean_numba(values, weights):
  70.     total = 0
  71.     total_weights = 0
  72.     for i in range(len(values)):
  73.         total += values[i] * weights[i]
  74.         total_weights += weights[i]
  75.     return total / total_weights
  76. # 创建一个加权平均的函数
  77. def weighted_mean(group):
  78.     return weighted_mean_numba(group['Salary'].values, group['Years'].values)
  79. start_time = time.time()
  80. result5 = large_df.groupby(['Department', 'Team']).apply(weighted_mean)
  81. time5 = time.time() - start_time
  82. print(f"\n使用numba加速的自定义聚合时间:{time5:.4f}秒")
复制代码

实战案例:综合应用分组操作解决实际问题

让我们通过一个综合案例来展示如何应用分组操作解决实际数据分析问题。假设我们是一家公司的数据分析师,需要分析员工数据,找出各部门的薪资分布、绩效差异以及潜在的不平等问题。
  1. # 创建更真实的员工数据集
  2. np.random.seed(42)
  3. n = 5000
  4. departments = ['HR', 'IT', 'Finance', 'Marketing', 'Operations', 'Sales', 'R&D']
  5. genders = ['M', 'F']
  6. education_levels = ['High School', 'Bachelor', 'Master', 'PhD']
  7. # 创建数据
  8. data = {
  9.     'Employee_ID': range(1, n+1),
  10.     'Department': np.random.choice(departments, n, p=[0.1, 0.2, 0.15, 0.15, 0.15, 0.15, 0.1]),
  11.     'Gender': np.random.choice(genders, n),
  12.     'Age': np.random.normal(40, 10, n).astype(int),
  13.     'Education': np.random.choice(education_levels, n, p=[0.1, 0.5, 0.3, 0.1]),
  14.     'Years_at_Company': np.random.randint(0, 20, n),
  15.     'Performance': np.random.choice(['Below Average', 'Average', 'Good', 'Excellent'], n, p=[0.05, 0.2, 0.5, 0.25])
  16. }
  17. # 根据部门、教育程度、绩效和经验生成薪资
  18. base_salaries = {
  19.     'HR': 60000,
  20.     'IT': 80000,
  21.     'Finance': 85000,
  22.     'Marketing': 75000,
  23.     'Operations': 70000,
  24.     'Sales': 65000,
  25.     'R&D': 90000
  26. }
  27. education_bonus = {
  28.     'High School': 1.0,
  29.     'Bachelor': 1.2,
  30.     'Master': 1.4,
  31.     'PhD': 1.6
  32. }
  33. performance_bonus = {
  34.     'Below Average': 0.8,
  35.     'Average': 1.0,
  36.     'Good': 1.2,
  37.     'Excellent': 1.5
  38. }
  39. salaries = []
  40. for i in range(n):
  41.     dept = data['Department'][i]
  42.     edu = data['Education'][i]
  43.     perf = data['Performance'][i]
  44.     years = data['Years_at_Company'][i]
  45.    
  46.     # 基础薪资
  47.     base = base_salaries[dept]
  48.    
  49.     # 教育程度加成
  50.     edu_factor = education_bonus[edu]
  51.    
  52.     # 绩效加成
  53.     perf_factor = performance_bonus[perf]
  54.    
  55.     # 经验加成(每年增加2%)
  56.     exp_factor = 1 + (years * 0.02)
  57.    
  58.     # 性别差异(模拟现实中的性别薪资差距)
  59.     gender_factor = 1.0 if data['Gender'][i] == 'M' else 0.95
  60.    
  61.     # 计算最终薪资,添加一些随机性
  62.     salary = base * edu_factor * perf_factor * exp_factor * gender_factor * np.random.normal(1.0, 0.05)
  63.     salaries.append(int(salary))
  64. data['Salary'] = salaries
  65. employee_df = pd.DataFrame(data)
  66. # 问题1:各部门的平均薪资和薪资范围
  67. dept_salary_stats = employee_df.groupby('Department')['Salary'].agg(['mean', 'min', 'max', 'std'])
  68. print("各部门薪资统计:\n", dept_salary_stats.sort_values('mean', ascending=False))
  69. # 问题2:各部门的性别分布和性别薪资差距
  70. gender_dist = employee_df.groupby(['Department', 'Gender']).size().unstack()
  71. print("\n各部门性别分布:\n", gender_dist)
  72. gender_pay_gap = employee_df.groupby(['Department', 'Gender'])['Salary'].mean().unstack()
  73. gender_pay_gap['Gap'] = (gender_pay_gap['M'] - gender_pay_gap['F']) / gender_pay_gap['M'] * 100
  74. print("\n各部门性别薪资差距(%):\n", gender_pay_gap)
  75. # 问题3:教育程度对薪资的影响
  76. education_salary = employee_df.groupby(['Department', 'Education'])['Salary'].mean().unstack()
  77. print("\n各部门不同教育程度的平均薪资:\n", education_salary)
  78. # 问题4:绩效与薪资的关系
  79. performance_salary = employee_df.groupby(['Department', 'Performance'])['Salary'].mean().unstack()
  80. print("\n各部门不同绩效等级的平均薪资:\n", performance_salary)
  81. # 问题5:识别潜在的薪资不平等
  82. # 计算每个部门内相同教育程度、绩效和工作年限的员工的薪资差异
  83. def identify_pay_inequality(group):
  84.     # 按教育程度、绩效和工作年限分组
  85.     subgroups = group.groupby(['Education', 'Performance', pd.cut(group['Years_at_Company'], bins=[0, 5, 10, 15, 20])])
  86.    
  87.     # 计算每个子组的薪资标准差
  88.     subgroup_std = subgroups['Salary'].std().reset_index()
  89.    
  90.     # 找出标准差大于平均标准差的子组
  91.     high_std_subgroups = subgroup_std[subgroup_std['Salary'] > subgroup_std['Salary'].mean()]
  92.    
  93.     # 如果有高差异的子组,返回它们
  94.     if len(high_std_subgroups) > 0:
  95.         return high_std_subgroups.assign(Department=group.name)
  96.     else:
  97.         return None
  98. # 应用函数并合并结果
  99. inequality_results = []
  100. for dept, group in employee_df.groupby('Department'):
  101.     result = identify_pay_inequality(group)
  102.     if result is not None:
  103.         inequality_results.append(result)
  104. if inequality_results:
  105.     inequality_df = pd.concat(inequality_results)
  106.     print("\n潜在的薪资不平等领域:\n", inequality_df)
  107. else:
  108.     print("\n未发现明显的薪资不平等。")
  109. # 问题6:创建一个综合的部门分析报告
  110. def create_department_report(group):
  111.     report = {}
  112.    
  113.     # 基本信息
  114.     report['Department'] = group.name
  115.     report['Employee_Count'] = len(group)
  116.    
  117.     # 薪资统计
  118.     report['Mean_Salary'] = group['Salary'].mean()
  119.     report['Median_Salary'] = group['Salary'].median()
  120.     report['Salary_Std'] = group['Salary'].std()
  121.     report['Salary_Range'] = group['Salary'].max() - group['Salary'].min()
  122.    
  123.     # 性别分布
  124.     gender_counts = group['Gender'].value_counts()
  125.     report['Male_Count'] = gender_counts.get('M', 0)
  126.     report['Female_Count'] = gender_counts.get('F', 0)
  127.     report['Female_Percentage'] = report['Female_Count'] / report['Employee_Count'] * 100
  128.    
  129.     # 性别薪资差距
  130.     male_salary = group[group['Gender'] == 'M']['Salary'].mean()
  131.     female_salary = group[group['Gender'] == 'F']['Salary'].mean()
  132.     if male_salary and female_salary:
  133.         report['Gender_Pay_Gap'] = (male_salary - female_salary) / male_salary * 100
  134.     else:
  135.         report['Gender_Pay_Gap'] = None
  136.    
  137.     # 绩效分布
  138.     performance_counts = group['Performance'].value_counts(normalize=True) * 100
  139.     for perf in ['Below Average', 'Average', 'Good', 'Excellent']:
  140.         report[f'Perf_{perf.replace(" ", "_")}'] = performance_counts.get(perf, 0)
  141.    
  142.     # 教育程度分布
  143.     education_counts = group['Education'].value_counts(normalize=True) * 100
  144.     for edu in ['High School', 'Bachelor', 'Master', 'PhD']:
  145.         report[f'Edu_{edu.replace(" ", "_")}'] = education_counts.get(edu, 0)
  146.    
  147.     # 平均工作年限
  148.     report['Avg_Years'] = group['Years_at_Company'].mean()
  149.    
  150.     return pd.Series(report)
  151. # 生成报告
  152. department_reports = employee_df.groupby('Department').apply(create_department_report)
  153. print("\n部门分析报告:\n", department_reports)
复制代码

这个实战案例展示了如何综合应用pandas的分组操作来解决实际的数据分析问题。我们通过分组和聚合分析了各部门的薪资情况、性别分布、教育程度影响、绩效与薪资关系,并识别了潜在的薪资不平等问题。最后,我们创建了一个综合的部门分析报告,提供了各部门的全面概览。

总结与展望

pandas的分组操作是数据分析中不可或缺的工具,它提供了强大而灵活的功能,使我们能够从不同维度审视和分析数据。本文从基础语法到高级应用,全面介绍了pandas中的分组操作,包括:

1. 基础的groupby语法和常用聚合函数
2. 多重分组和分组迭代
3. 自定义聚合函数和转换操作
4. 分组过滤和排序
5. 透视表和交叉表等高级应用
6. 性能优化技巧
7. 实战案例展示

通过掌握这些技术,数据分析师可以更高效地处理和分析数据,发现数据中隐藏的模式和关系,为决策提供有力支持。

展望未来,随着数据量的不断增长和分析需求的日益复杂,pandas的分组操作将继续发展和完善。我们可以期待更多性能优化、更丰富的聚合函数以及与其他数据处理工具(如Dask、Spark)的更好集成。同时,随着机器学习和人工智能在数据分析中的应用越来越广泛,分组操作也将与这些技术更紧密地结合,为数据科学家提供更强大的分析能力。

无论你是数据分析的初学者还是经验丰富的专业人士,掌握pandas的分组操作都将大大提升你的数据处理效率和分析能力。希望本文能成为你学习和应用pandas分组操作的有用指南。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

频道订阅

频道订阅

加入社群

加入社群

联系我们|TG频道|RSS

Powered by Pixtech

© 2025 Pixtech Team.