我一直在尝试采用像下面的
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 个月前,我确实发布了一个类似但不同的问题,如果这有任何启发的话。
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
这是一种基于联接的方法,它依赖于平等联接产生平等组内结果的笛卡尔积的事实。一旦我们有了这个,就需要过滤到独特的组合(这是通过过滤生成的
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