pandas DataFrame 对每组的单元格重新排序

pandas DataFrame re-order cells for each group

我有一个 3 组的数据框,例如:

group   value1   value2   value3
1       A1       A2       A3
1       B1       B2       B3
1       C1       C2       C3
2       D1       D2       D3
2       E1       E2       E3
2       F1       F2       F3
...

我想根据 'positions' 的固定规则对每个组中的单元格重新排序,并对所有组重复相同的操作。

此 'fixed' 规则的工作方式如下:

输入:

group   value1       value2       value3
1       position1    position2    position3
1       position4    position5    position6
1       position7    position8    position9

输出:

group   value1       value2       value3
1       position1    position8    position6
1       position4    position2    position9
1       position7    position5    position3

最终数据框应该看起来像(如果这有意义的话):

group   value1   value2   value3
1       A1       C2       B3
1       B1       A2       C3
1       C1       B2       A3
2       D1       F2       E3
2       E1       D2       F3
2       F1       E2       D3
...

如果数据帧只有一组,我知道如何重新排序它们——基本上创建一个临时变量来存储值,通过 .loc 获取每个单元格,并用所需的值覆盖每个单元格。

然而,即使我们只有一组 3 行,这仍然是一种明显愚蠢和乏味的方式。

我的问题是:我们能不能

  1. 找到一个通用的操作来根据单元格在组中的相对位置重新排列单元格
  2. 对所有组重复此操作?

这是一个使用 numpy 索引并对每个组进行重塑的提议。

设置:

假设您的原始 df 和位置数据帧如下:

d = {'group': [1, 1, 1, 2, 2, 2],
 'value1': ['A1', 'B1', 'C1', 'D1', 'E1', 'F1'],
 'value2': ['A2', 'B2', 'C2', 'D2', 'E2', 'F2'],
 'value3': ['A3', 'B3', 'C3', 'D3', 'E3', 'F3']}
out_d = {'group': [1, 1, 1, 2, 2, 2], 
         'value1': ['position1', 'position4', 'position7',
                    'position1', 'position4', 'position7'], 
         'value2': ['position8', 'position2', 'position5',
                  'position8', 'position2', 'position5'], 
         'value3': ['position6', 'position9', 'position3', 
                    'position6', 'position9', 'position3']}
df = pd.DataFrame(d)
out = pd.DataFrame(out_d)

print("Original dataframe :\n\n",df,"\n\n Position dataframe :\n\n",out)

Original dataframe :

   group value1 value2 value3
0      1     A1     A2     A3
1      1     B1     B2     B3
2      1     C1     C2     C3
3      2     D1     D2     D3
4      2     E1     E2     E3
5      2     F1     F2     F3 

 Position dataframe :

    group     value1     value2     value3
0      1  position1  position8  position6
1      1  position4  position2  position9
2      1  position7  position5  position3
3      2  position1  position8  position6
4      2  position4  position2  position9
5      2  position7  position5  position3

工作解决方案:

方法一::创建函数并在df.groupby.apply

中使用
#remove letters and extract only position numbers and subtract 1 
#since python indexing starts at 0

o = out.applymap(lambda x: int(''.join(re.findall('\d+',x)))-1 if type(x)==str else x)

#Merge this output with original dataframe
df1 = df.merge(o,on='group',left_index=True,right_index=True,suffixes=('','_pos'))
# Build a function which rearranges the df based on the position df:
def fun(x):
    c = x.columns.str.contains("_pos")
    return pd.DataFrame(np.ravel(x.loc[:,~c])[np.ravel(x.loc[:,c])]
                        .reshape(x.loc[:,~c].shape),
                         columns=x.columns[~c])

output = (df1.groupby("group").apply(fun).reset_index("group")
          .reset_index(drop=True))
print(output)

   group value1 value2 value3
0      1     A1     C2     B3
1      1     B1     A2     C3
2      1     C1     B2     A3
3      2     D1     F2     E3
4      2     E1     D2     F3
5      2     F1     E2     D3

方法二:遍历每组并重新排列:

o = out.applymap(lambda x: int(''.join(re.findall('\d+',x)))-1 if type(x)==str else x)
df1 = df.merge(o,on='group',left_index=True,right_index=True,
               suffixes=('','_pos')).set_index("group")
idx = df1.index.unique()


l = []
for i in idx:
    v = df1.loc[i]
    c = v.columns.str.contains("_pos")
    l.append(np.ravel(v.loc[:,~c])[np.ravel(v.loc[:,c])].reshape(v.loc[:,~c].shape))
final = pd.DataFrame(np.concatenate(l),index=df1.index,
        columns=df1.columns[~c]).reset_index()

print(final)

   group value1 value2 value3
0      1     A1     C2     B3
1      1     B1     A2     C3
2      1     C1     B2     A3
3      2     D1     F2     E3
4      2     E1     D2     F3
5      2     F1     E2     D3