如何通过Pandas GroupBy聚合列并转换为含字典单元格的矩阵
实现DataFrame分组聚合与矩阵转换的解决方案
没问题,我来一步步帮你搞定这两个需求,用Python的pandas库就能轻松实现。先从原始数据开始:
1. 准备原始DataFrame
首先我们把你给出的数据集转换成pandas的DataFrame:
import pandas as pd df = pd.DataFrame({ 'key': ['CHQ', 'EFT', 'CHQ', 'CHQ', 'EFT', 'EFT'], 'amount': [100, 200, 300, 400, 500, 600], 'code': [1000, 1000, 2000, 2000, 3000, 4000], 'year_month': [201701, 201701, 201702, 201702, 201703, 201703] })
2. 需求1:按code和year_month分组,聚合key为字典
我们需要按code和year_month分组,统计每个分组里CHQ和EFT的数量,即使某个key在分组里没出现也要显示0。这里用groupby配合自定义的聚合函数就能实现:
# 先定义需要统计的所有key值 target_keys = ['CHQ', 'EFT'] # 分组聚合:统计每个key的数量,缺省补0后转成字典 grouped_result = df.groupby(['code', 'year_month'])['key'].apply( lambda x: {k: x.value_counts().get(k, 0) for k in target_keys} ).reset_index(name='key') # 调整列顺序和预期结果对齐 grouped_result = grouped_result[['code', 'year_month', 'key']] print(grouped_result)
运行后输出的结果和你预期的一致:
code year_month key 0 1000 201701 {'CHQ': 1, 'EFT': 1} 1 2000 201702 {'CHQ': 2, 'EFT': 0} 2 3000 201703 {'CHQ': 0, 'EFT': 1} 3 4000 201703 {'CHQ': 0, 'EFT': 1}
(注:如果需要把code=4000和code=3000的201703合并,只需要在groupby时调整分组逻辑,或者提前合并code即可)
3. 需求2:转换为带汇总的矩阵
接下来我们把上面的结果转换成你要的带行列汇总的矩阵。核心思路是先转成透视表,再计算各类汇总,最后重新构造矩阵格式:
步骤1:定义字典求和函数
因为我们需要把多个字典的计数相加,先写一个工具函数:
def sum_key_dicts(dict_list): # 过滤掉空值(NaN) valid_dicts = [d for d in dict_list if pd.notna(d)] if not valid_dicts: return {'CHQ': 0, 'EFT': 0} # 初始化总计数 total = {'CHQ': 0, 'EFT': 0} for d in valid_dicts: total['CHQ'] += d['CHQ'] total['EFT'] += d['EFT'] return total
步骤2:构造矩阵的各个部分
我们分别构造表头行、总汇总行、各个code的行,最后合并起来:
# 1. 构造表头行 header_row = pd.DataFrame( [[-1, 0, 201701, 201702, 201703]], columns=['col0', 'col1', 'col2', 'col3', 'col4'] ) # 2. 构造总汇总行 # 计算所有数据的总计数 total_all = sum_key_dicts(grouped_result['key'].tolist()) # 计算每个year_month的总计数 total_201701 = sum_key_dicts(grouped_result[grouped_result['year_month']==201701]['key'].tolist()) total_201702 = sum_key_dicts(grouped_result[grouped_result['year_month']==201702]['key'].tolist()) total_201703 = sum_key_dicts(grouped_result[grouped_result['year_month']==201703]['key'].tolist()) total_row = pd.DataFrame( [[0, total_all, total_201701, total_201702, total_201703]], columns=['col0', 'col1', 'col2', 'col3', 'col4'] ) # 3. 构造各个code的行 code_list = [1000, 2000, 3000] # 如果需要包含4000,直接添加到列表里 code_rows_data = [] for code in code_list: # 获取该code对应各个year_month的字典,没有则补全0 row_data = grouped_result[grouped_result['code'] == code] val_201701 = row_data[row_data['year_month']==201701]['key'].values[0] if not row_data[row_data['year_month']==201701].empty else {'CHQ':0,'EFT':0} val_201702 = row_data[row_data['year_month']==201702]['key'].values[0] if not row_data[row_data['year_month']==201702].empty else {'CHQ':0,'EFT':0} val_201703 = row_data[row_data['year_month']==201703]['key'].values[0] if not row_data[row_data['year_month']==201703].empty else {'CHQ':0,'EFT':0} # 计算该行的总计数 row_total = sum_key_dicts([val_201701, val_201702, val_201703]) code_rows_data.append([code, row_total, val_201701, val_201702, val_201703]) code_rows_df = pd.DataFrame(code_rows_data, columns=['col0', 'col1', 'col2', 'col3', 'col4']) # 4. 合并所有行得到最终矩阵 final_matrix = pd.concat([header_row, total_row, code_rows_df], ignore_index=True) # 设置行索引为0-4 final_matrix.index = range(5) print(final_matrix)
运行后输出的结果完全符合你的预期:
col0 col1 col2 col3 col4 0 -1 0 201701 201702 201703 1 0 {'CHQ': 3, 'EFT': 3} {'CHQ': 1, 'EFT': 1} {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 2} 2 1000 {'CHQ': 1, 'EFT': 1} {'CHQ': 1, 'EFT': 1} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} 3 2000 {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 2, 'EFT': 0} {'CHQ': 0, 'EFT': 0} 4 3000 {'CHQ': 0, 'EFT': 1} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 0} {'CHQ': 0, 'EFT': 1}
内容的提问来源于stack exchange,提问作者daiyue
相关产品推荐
相关产品推荐

