【问题标题】:How to conditionally remove duplicates from a pandas dataframe如何有条件地从熊猫数据框中删除重复项
【发布时间】:2018-02-05 00:33:06
【问题描述】:

考虑以下数据框

import pandas as pd
df = pd.DataFrame({'A' : [1, 2, 3, 3, 4, 4, 5, 6, 7],
                   'B' : ['a','b','c','c','d','d','e','f','g'],
                   'Col_1' :[np.NaN, 'A','A', np.NaN, 'B', np.NaN, 'B', np.NaN, np.NaN],
                   'Col_2' :[2,2,3,3,3,3,4,4,5]})
df
Out[92]: 
    A  B Col_1  Col_2
 0  1  a   NaN      2
 1  2  b     A      2
 2  3  c     A      3
 3  3  c   NaN      3
 4  4  d     B      3
 5  4  d   NaN      3
 6  5  e     B      4
 7  6  f   NaN      4
 8  7  g   NaN      5

我想删除与列 'A' 'B' 相关的所有重复行。我想删除具有NaN 条目的条目(我知道对于所有重复项都会有一个NaN 和一个not-NaN 条目)。最终结果应该是这样的

    A  B Col_1  Col_2
 0  1  a   NaN      2
 1  2  b     A      2
 2  3  c     A      3
 4  4  d     B      3
 6  5  e     B      4
 7  6  f   NaN      4
 8  7  g   NaN      5

欢迎所有高效的单行者

【问题讨论】:

  • 在反对选民的辩护中,您可以选择一个简单的例子,df.drop_duplicates 给出了您不想要的答案。
  • 是的。我接受反对票:)

标签: python pandas dataframe duplicates


【解决方案1】:

如果目标是仅删除 NaN 重复项,则需要一个稍微复杂的解决方案。

首先,对ABCol_1 进行排序,因此NaNs 被移到每个组的底部。然后用keep=first调用df.drop_duplicates

out = df.sort_values(['A', 'B', 'Col_1']).drop_duplicates(['A', 'B'], keep='first')
print(out)

   A  B Col_1  Col_2
0  1  a   NaN      2
1  2  b     A      2
2  3  c     A      3
4  4  d     B      3
6  5  e     B      4
7  6  f   NaN      4
8  7  g   NaN      5

【讨论】:

  • 也许 :) 但这是否保证Col_1 的非NaN 值是保留的行?
  • @mortysporty 已编辑。
【解决方案2】:

这里有一个替代方案:

df[~((df[['A', 'B']].duplicated(keep=False)) & (df.isnull().any(axis=1)))]
#    A  B Col_1  Col_2
# 0  1  a   NaN      2
# 1  2  b     A      2
# 2  3  c     A      3
# 4  4  d     B      3
# 6  5  e     B      4
# 7  6  f   NaN      4
# 8  7  g   NaN      5

这使用按位“非”运算符~ 否定满足作为重复行的联合条件的行(参数keep=False 导致该方法对所有非唯一行计算为真)并包含至少一个空值。那么表达式df[['A', 'B']].duplicated(keep=False) 返回这个系列的地方:

# 0    False
# 1    False
# 2     True
# 3     True
# 4     True
# 5     True
# 6    False
# 7    False
# 8    False

...而表达式 df.isnull().any(axis=1) 返回此系列:

# 0     True
# 1    False
# 2    False
# 3     True
# 4    False
# 5     True
# 6    False
# 7     True
# 8     True

...我们将两者都用括号括起来(在索引操作中使用多个表达式时,Pandas 语法要求),然后将它们括在括号中再次,这样我们就可以否定整个表达式(即@ 987654328@),像这样:

~((df[['A','B']].duplicated(keep=False)) & (df.isnull().any(axis=1))) & (df['Col_2'] != 5)

# 0     True
# 1     True
# 2     True
# 3    False
# 4     True
# 5    False
# 6     True
# 7     True
# 8    False

您可以进一步使用逻辑运算符&|(“或”运算符)来构建更复杂的条件。与 SQL 一样,根据需要使用附加括号对条件进行分组;例如,使用df[ ( (X) & (Y) ) | (Z) ] 根据逻辑“条件 X AND 条件 Y 都为真,或 条件 Z 为真”进行过滤。

【讨论】:

  • 我认为您需要将 keep=False 传递给 duplicated 才能使其正常工作。
  • @ayhan 我只是在想同样的事情 :)
  • 嗨。如果我们想要摆脱除NaN 之外的其他值,这会起作用吗?我们可以在& 之后修正这个论点吗?
  • @mortysporty 是的,这基本上是正确的——不过,我应该警告,根据您测试该值的方式,如果您取消组合条件(即删除外部括号),以便您可以执行~(df.duplicated) & (df.Col_2 != 5) 之类的操作。如果您直接将df.Col_2 != 5 替换为上面的单行代码,它将被否定(即 True 变为 False,反之亦然),因为两个当前条件在 ~( ... ) 中分组的方式。
【解决方案3】:

或者你可以只使用first(),通过使用第一个,将返回第一个notnull值,所以原始输入的顺序并不重要。

df.groupby(['A','B']).first()

Out[180]: 
    Col_1  Col_2
A B             
1 a   NaN      2
2 b     A      2
3 c     A      3
4 d     B      3
5 e     B      4
6 f   NaN      4
7 g   NaN      5

【讨论】:

    猜你喜欢
    • 2018-11-09
    • 1970-01-01
    • 1970-01-01
    • 2020-03-23
    • 2017-10-04
    • 2021-07-18
    相关资源
    最近更新 更多