Python 分组并删除熊猫中的配对记录

Python 分组并删除熊猫中的配对记录,python,pandas,Python,Pandas,我有一个这样的数据框 col1 col2 col3 col4 a1 b1 c1 + a1 b1 c1 + a1 b2 c2 + a1 b2 c2 - a1 b2 c2 + 如果在col1、col2和col3中有两个值相同的记录,并且在col4中有相反的符号,则应将它们从数据帧中删除 输出: col1 col2 col

我有一个这样的数据框

col1    col2    col3    col4
a1      b1      c1      +
a1      b1      c1      +
a1      b2      c2      +
a1      b2      c2      -
a1      b2      c2      +
如果在
col1
col2
col3
中有两个值相同的记录,并且在
col4
中有相反的符号,则应将它们从数据帧中删除

输出:

col1    col2    col3    col4
a1      b1      c1      +
a1      b1      c1      +
a1      b2      c2      +
  col1 col2 col3 col4
0   a1   b1   c1    +
1   a1   b1   c1    +
4   a1   b2   c2    +
   col1 col2 col3 col4
0    a1   b1   c1    +
1    a1   b1   c1    +
4    a1   b2   c2    +
11   a1   b3   c3    +
12   a1   b3   c3    +
到目前为止,我尝试了pandas
duplicated
groupby
,但没有找到配对。如何做到这一点?

我认为需要对计数组定义所有
4
列,然后使用helper
Series
再次分组定义
+-
组并与
集进行比较:

s = df.groupby(['col1','col2','col3', 'col4']).cumcount()
df = df[~df.groupby(['col1','col2','col3', s])['col4']
           .transform(lambda x: set(x) == set(['+','-']))]
print (df)
  col1 col2 col3 col4
0   a1   b1   c1    +
1   a1   b1   c1    +
6   a1   b2   c2    +
为了更好地理解,请创建新列:

df['help'] = df.groupby(['col1','col2','col3', 'col4']).cumcount()
print (df)
  col1 col2 col3 col4  help
0   a1   b1   c1    +     0
1   a1   b1   c1    +     1
2   a1   b2   c2    +     0
3   a1   b2   c2    -     0
4   a1   b2   c2    +     1

df = df[~df.groupby(['col1','col2','col3', 'help'])['col4']
           .transform(lambda x: set(x) == set(['+','-']))]
print (df)
  col1 col2 col3 col4  help
0   a1   b1   c1    +     0
1   a1   b1   c1    +     1
4   a1   b2   c2    +     1

考虑到“如果有两条记录在col1、col2和col3中具有相同的值,并且在col4中具有相反的符号,则应将其从数据帧中删除”的评论,则:

1) 识别并删除重复项:
df.drop_duplicates()

2) 按前三列对它们进行分组:
df.groupby(['col1','col2','col3'])

3) 只保留大小为1的组(否则,这意味着我们有“+”和“-”)两个:
.filter(lambda组:len(group)==1)

一应俱全:


df.drop_duplicates().groupby(['col1','col2','col3']).filter(lambda g:len(g)==1)
首先,按
col1
col2
col3
对数据帧进行分组。然后,应用方法,该方法将减去组中在
col4
中具有不同符号的行

在此方法中,将
col4
+
的值替换为1,将
-
的值替换为-1。然后对
col4
中的值求和(让我们调用变量,它使该和保持有符号行计数)。只有两种可能的结果,要么是
+
行占主导地位(正和值),要么是
-
行占主导地位(负和值)。因此,您可以返回新的数据帧,带有
signed_row_count
sign-in
col4
的行数,或者
signed_row_count
sign-in
col4
的行数,具体取决于总和的符号

以下是代码:

df = pd.DataFrame(
    [['a1', 'b1', 'c1', '+'], ['a1', 'b1', 'c1', '+'], ['a1', 'b2', 'c2', '+'], ['a1', 'b2', 'c2', '-'], ['a1', 'b2', 'c2', '+']], 
    columns=['col1', 'col2', 'col3', 'col4']
)
print(df)
#   col1 col2 col3 col4
# 0   a1   b1   c1    +
# 1   a1   b1   c1    +
# 2   a1   b2   c2    +
# 3   a1   b2   c2    -
# 4   a1   b2   c2    +

def subtract_rows(df):
    signed_row_count = df['col4'].replace({'+': 1, '-': -1}).sum()

    if signed_row_count >= 0:
       result = pd.DataFrame([df.iloc[0][['col1', 'col2', 'col3']].tolist() + ['+']] * signed_row_count, columns=df.columns)
    else:
        result = pd.DataFrame([df.iloc[0][['col1', 'col2', 'col3']].tolist() + ['-']] * abs(signed_row_count), columns=df.columns)

    return result

reduced_df = (df.groupby(['col1', 'col2', 'col3'])
                .apply(subtract_rows)
                .reset_index(drop=True))
print(reduced_df)
#   col1 col2 col3 col4
# 0   a1   b1   c1    +
# 1   a1   b1   c1    +
# 2   a1   b2   c2    +
以下是我的尝试:

df[df.assign(ident=df.assign(count=df.col4.eq('+').astype(int))\
  .groupby(['col1','col2','col3','count']).cumcount())\
  .groupby(['col1','col2','col3','ident']).transform(lambda x: len(x) < 2)['col4']]
在更稳健的测试集上:

df = pd.DataFrame(
    [['a1', 'b1', 'c1', '+'], ['a1', 'b1', 'c1', '+'], ['a1', 'b2', 'c2', '+'], ['a1', 'b2', 'c2', '-'], ['a1', 'b2', 'c2', '+'],
     ['a1','b3','c3','+'],['a1','b3','c3','-'],['a1','b3','c3','-'],['a1','b3','c3','-'],['a1','b3','c3','+'],['a1','b3','c3','+'],['a1','b3','c3','+'],['a1','b3','c3','+']], 
    columns=['col1', 'col2', 'col3', 'col4']
)
输入数据帧:

   col1 col2 col3 col4
0    a1   b1   c1    +
1    a1   b1   c1    +
2    a1   b2   c2    +
3    a1   b2   c2    -
4    a1   b2   c2    +
5    a1   b3   c3    +
6    a1   b3   c3    -
7    a1   b3   c3    -
8    a1   b3   c3    -
9    a1   b3   c3    +
10   a1   b3   c3    +
11   a1   b3   c3    +
12   a1   b3   c3    +

df[df.assign(ident=df.assign(count=df.col4.eq('+').astype(int))\
  .groupby(['col1','col2','col3','count']).cumcount())\
  .groupby(['col1','col2','col3','ident']).transform(lambda x: len(x) < 2)['col4']]

如果是成对的(+、-),你想一直保留“+”、“-”还是你不在乎?@Guybrush-它可以是任何字符串来替换+,-你编辑了你的问题,现在我很困惑:你到底想做什么?如果存在(a,b,c,-)行,则删除(a,b,c,d)?@GYBRUSH-如果有两条记录在col1,col2和col3中具有相同的值,并且在col4中具有相反的符号,则应将它们从DataFrame中删除。如果负号始终在a+之前和之后。如果您有(+--+++)您想删除所有对的左(+)还是只删除正左(-++)旁边的对?我认为您需要更广泛的测试集和预期的输出。我想在col1、col2、col3中找到重复项,只有在这之后,如果找到成对项,我需要删除记录。我的预期输出与您的不一样answer@MohamedThasinah-因此,对于每个组,需要删除
+/-
对吗?如果找到+/-对,则删除它。或者保持原样。例如,如果一组中+符号包含5次,而-symbol包含4次,则应删除4对,并保留一个+符号same@MohamedThasinah-你是对的,答案被改变了。