如何将DataFrame的B类数组列转为无重复元素对的新行结构
问题描述
现有如下结构的DataFrame,其中Group是分组标识,A_x、A_y是A类物品的属性,B_m、B_n是B类物品的属性列表:
import pandas as pd df = pd.DataFrame( data=[ [0, 4, 9, [8, 7, 3], [-10, 5, 2]], [0, 1, 2, [8, 7, 3], [-10, 5, 2]], [1, 3, 3, [1, 2], [-5, 1]], ], columns=['Group', 'A_x', 'A_y', 'B_m', 'B_n'], )
初始DataFrame输出:
Group A_x A_y B_m B_n 0 0 4 9 [8, 7, 3] [-10, 5, 2] 1 0 1 2 [8, 7, 3] [-10, 5, 2] 2 1 3 3 [1, 2] [-5, 1]
需要将B_m和B_n列转换为每行仅含单个元素的四列(B_m1、B_n1、B_m2、B_n2),生成所有无重复的两两元素对,最终期望结果如下:
expected = pd.DataFrame( data=[ [0, 4, 9, 8, -10, 7, 5], [0, 4, 9, 8, -10, 3, 2], [0, 4, 9, 7, 5, 3, 2], [0, 1, 2, 8, -10, 7, 5], [0, 1, 2, 8, -10, 3, 2], [0, 1, 2, 7, 5, 3, 2], [1, 3, 3, 1, -5, 2, 1], ], columns=['Group', 'A_x', 'A_y', 'B_m1', 'B_n1', 'B_m2', 'B_n2'], )
期望结果输出:
Group A_x A_y B_m1 B_n1 B_m2 B_n2 0 0 4 9 8 -10 7 5 1 0 4 9 8 -10 3 2 2 0 4 9 7 5 3 2 3 0 1 2 8 -10 7 5 4 0 1 2 8 -10 3 2 5 0 1 2 7 5 3 2 6 1 3 3 1 -5 2 1
已尝试用itertools.combinations结合循环实现,但希望得到更简洁的解决方案。
简洁解决方案
方法1:利用apply生成组合并展开
这种方法适合中小数据量,代码简洁直观:
from itertools import combinations import pandas as pd def generate_pairs(row): # 将B_m和B_n的元素一一配对 b_items = list(zip(row['B_m'], row['B_n'])) # 生成所有无重复的两两组合 combos = list(combinations(b_items, 2)) # 转换为目标格式的行数据 return pd.DataFrame([ [row['Group'], row['A_x'], row['A_y'], combo[0][0], combo[0][1], combo[1][0], combo[1][1]] for combo in combos ], columns=['Group', 'A_x', 'A_y', 'B_m1', 'B_n1', 'B_m2', 'B_n2']) # 对每行应用函数,合并所有结果 result = pd.concat(df.apply(generate_pairs, axis=1).tolist(), ignore_index=True) print(result)
方法2:先展开B类属性,再生成组合(适合大数据量)
如果数据量较大,逐行循环效率较低,可以先展开B类属性,再按分组生成组合:
from itertools import combinations import pandas as pd # 先展开B_m和B_n列,将列表元素拆分为单独行 df_expanded = df.explode(['B_m', 'B_n']).reset_index(drop=True) # 给每个分组内的B项添加索引,方便后续生成组合 df_expanded['b_idx'] = df_expanded.groupby(['Group', 'A_x', 'A_y']).cumcount() # 生成每个分组内的两两索引组合 def get_combo_indices(group): indices = group['b_idx'].tolist() return pd.DataFrame(list(combinations(indices, 2)), columns=['idx1', 'idx2']) combo_indices = df_expanded.groupby(['Group', 'A_x', 'A_y']).apply(get_combo_indices).reset_index() # 两次合并,分别获取每组组合对应的B属性 result = combo_indices.merge( df_expanded, left_on=['Group', 'A_x', 'A_y', 'idx1'], right_on=['Group', 'A_x', 'A_y', 'b_idx'] ).merge( df_expanded, left_on=['Group', 'A_x', 'A_y', 'idx2'], right_on=['Group', 'A_x', 'A_y', 'b_idx'], suffixes=('_1', '_2') ) # 筛选并重命名列,得到最终结果 result = result[['Group', 'A_x', 'A_y', 'B_m_1', 'B_n_1', 'B_m_2', 'B_n_2']].rename( columns={'B_m_1':'B_m1', 'B_n_1':'B_n1', 'B_m_2':'B_m2', 'B_n_2':'B_n2'} ).reset_index(drop=True) print(result)
内容的提问来源于stack exchange,提问作者Gozmit97
相关产品推荐
相关产品推荐

