如何将数据框列中的对转换为两个新列

问题描述 投票:0回答:2

我一直在尝试采用像下面的

df
这样的DataFrame,并将一些列(比如
B_m
B_n
)变成两列(称它们为
B_m1
B_m2
B_n1
B_n2
),对于该列中的每对值(类似于
itertools.combinations(col, r=2)
),它们共享相同的组号。因此,对于
B_m
Group
为 0 的行,我们应该得到
B_m1 = [-1, -1, 0]
B_m2 = [0, 1, 1]

df = pd.DataFrame(
    data=[
        [0, 4, 7, -1, 0.9],
        [0, 4, 7, 0, 0.3],
        [0, 4, 7, 1, 0.2],
        [1, 3, 3, 1, 0.5],
        [1, 3, 3, 0, 0.2],
        [2, 1, 8, 0, 0.6],
    ],
    columns=['Group', 'A_x', 'A_y', 'B_m', 'B_n'],
)
print(df)

如果给定组只有 1 行,则应将其删除。对于具有相同组号的 4 行或更多行,我们应该类似地找到所有不重复的重复项。 下图所示是预期结果。

expected = pd.DataFrame(
    data=[
        [0, 4, 7, -1, 0, 0.9, 0.3],
        [0, 4, 7, -1, 1, 0.9, 0.2],
        [0, 4, 7, 0, 1, 0.3, 0.2],
        [1, 3, 3, 1, 0, 0.5, 0.2],
    ],
    columns=['Group', 'A_x', 'A_y', 'B_m1', 'B_m2', 'B_n1', 'B_n2'],
)
print(expected)

我的第一次尝试不仅需要很长时间才能运行,使用一些笨拙的循环并创建一个新的 DataFrame(不优雅并且没有保存它),而且也没有成功。从那时起,我一直无法想出替代解决方案。

郑重声明,大约 3 个月前,我确实发布了一个类似但不同的问题,如果这有任何启发的话。

python pandas dataframe
2个回答
0
投票

这是一种使用

groupby.apply
concat
的方法:

from itertools import combinations

cols = ['B_m', 'B_n']
group = list(df.columns.difference(cols))

g = df.groupby(group, sort=False)

out = pd.concat((g[c].apply(lambda x: pd.DataFrame(list(combinations(x, r=2)))
                                        .rename(columns=lambda x: f'{c}{x+1}'))
                for c in cols), axis=1).reset_index(group)

输出:

   A_x  A_y  Group  B_m1  B_m2  B_n1  B_n2
0    4    7      0  -1.0   0.0   0.9   0.3
1    4    7      0  -1.0   1.0   0.9   0.2
2    4    7      0   0.0   1.0   0.3   0.2
0    3    3      1   1.0   0.0   0.5   0.2

0
投票

这是一种基于联接的方法,它依赖于平等联接产生平等组内结果的笛卡尔积的事实。一旦我们有了这个,就需要过滤到独特的组合(这是通过过滤生成的

group_count
列来完成的):

import pandas as pd

df = pd.DataFrame(
    data=[
        [0, 4, 7, -1, 0.9],
        [0, 4, 7, 0, 0.3],
        [0, 4, 7, 1, 0.2],
        [1, 3, 3, 1, 0.5],
        [1, 3, 3, 0, 0.2],
        [2, 1, 8, 0, 0.6],
    ],
    columns=['Group', 'A_x', 'A_y', 'B_m', 'B_n'],
)

combo_cols = ['B_m', 'B_n']

print(
    df.assign(group_count=lambda d: d.groupby('Group').cumcount())
    .pipe(lambda d:
          d.merge(
              d[['Group', 'group_count', *combo_cols]], on='Group', how='left', suffixes=('1', '2')
          )
    )
    .query('group_count1 < group_count2')
    .drop(columns=['group_count1', 'group_count2'])
    [['Group', 'A_x', 'A_y', 'B_m1', 'B_m2', 'B_n1', 'B_n2']]
)

#     Group  A_x  A_y  B_m1  B_m2  B_n1  B_n2
# 1       0    4    7    -1     0   0.9   0.3
# 2       0    4    7    -1     1   0.9   0.2
# 5       0    4    7     0     1   0.3   0.2
# 10      1    3    3     1     0   0.5   0.2
© www.soinside.com 2019 - 2024. All rights reserved.