pandas使用比较函数进行去重

5

有没有可能使用pandas.drop_duplicates与比较运算符一起使用,以便比较特定列中的两个对象以识别重复项?如果不行,那么有什么替代方法?

这是一个可以使用它的示例:

我有一个 pandas DataFrame,其中某一列的值为列表,并希望基于列 A 删除重复项。

import pandas as pd

df = pd.DataFrame( {'A': [[1,2],[2,3],[1,2]]} )
print df

给我

        A
0  [1, 2]
1  [2, 3]
2  [1, 2]

使用 pandas.drop_duplicates
df.drop_duplicates( 'A' )

给我一个 TypeError

[...]
TypeError: type object argument after * must be a sequence, not itertools.imap

然而,我的期望结果是
        A
0  [1, 2]
1  [2, 3]

我的比较函数会在这里:

def cmp(x,y):
    return x==y

但原则上它可能是其他东西,例如,

def cmp(x,y):
    return x==y and len(x)>1

如何以高效的方式基于比较函数去除重复项?

更进一步,如果我有更多列需要使用不同的比较函数进行比较,我该怎么办呢?

3个回答

6

如果我理解正确,您的问题是如何使用一个任意的函数来确定什么是重复项。为了强调这一点,我们可以说,如果两个列表的第一项的和加上第二项的平方在每种情况下都相同,则它们是重复的。

In [59]: In [118]: df = pd.DataFrame( {'A': [[1,2],[4,1],[2,3]]} )

(注意,第一个和第二个列表是等价的,但不是相同的。)
Python通常更喜欢关键函数而不是比较函数,因此在这里我们需要一个函数来说明列表的关键是什么;在这种情况下,它是lambda l:l [0] + l [1] ** 2
我们可以使用{{link2:groupby + first}}按关键函数的值分组,然后取每个组的第一个。
In [119]: df.groupby(df.A.apply(lambda l: l[0] + l[1]**2)).first()
Out[119]: 
         A
A         
5   [1, 2]
11  [2, 3]

编辑

随着问题的进一步编辑,这里有几个更多的例子使用

df = pd.DataFrame( {'A': [[1,2],[2,3],[1,2], [1], [1], [2]]} )

然后为

def cmp(x,y):
    return x==y

这可能是

In [158]: df.groupby(df.A.apply(tuple)).first()
Out[158]: 
             A
A             
(1,)       [1]
(1, 2)  [1, 2]
(2,)       [2]
(2, 3)  [2, 3]

用于

def cmp(x,y):
     return x==y and len(x)>1

这可能是

In [184]: class Key(object):
   .....:     def __init__(self):
   .....:         self._c = 0
   .....:     def __call__(self, l):
   .....:         if len(l) < 2:
   .....:             self._c += 1
   .....:             return self._c
   .....:         return tuple(l)
   .....:     

In [187]: df.groupby(df.A.apply(Key())).first()
Out[187]: 
             A
A             
1          [1]
2          [1]
3          [2]
(1, 2)  [1, 2]
(2, 3)  [2, 3]

另外,这也可以通过更简洁的方式实现:

In [190]: df.groupby(df.A.apply(lambda l: np.random.rand() if len(l) < 2 else tuple(l))).first()
Out[190]: 
                     A
A                     
0.112012068449     [2]
0.822889598152     [1]
0.842630848774     [1]
(1, 2)          [1, 2]
(2, 3)          [2, 3]

但是有些人不喜欢这些蒙特卡罗的东西。

需要小心处理 df.A.apply(sum) - 如果在其中加入 [0, 3][10, -5],你会感到困惑... - Jon Clements
1
啊,我明白了...虽然我很感激这个例子,但我认为使用sum作为示例有些误导人,因为根据OP的数据,[0, 3]并不直观地重复了[1, 2]...但是重新阅读后,我明白你的意思是要演示groupby/first - 我个人认为在这种情况下,使用tuplefrozenset可能会更好一些... - Jon Clements
@JonClements 感谢您提供这些有价值的观点 - 非常感谢!我会找一个更好的例子(没有更简单/更高效的替代方案)。 - Ami Tavory
如果我可以定义一个关键函数,基本上将每个列中的对象映射到一个值,并且比较是在这个关键值上进行的,那么这是一个不错的解决方案。但我对更一般的解决方案感兴趣。我已经更新了我的问题,并提供了一个替代比较函数,我想使用它。 - desiato
@desiato 使用这种方法,是的。当前Python中使用关键函数进行排序、分组等操作。标准文档解释了关键函数是比较函数的一般化,对此已经有很多了解。这是一种非常通用的技术。话虽如此,需要注意的是,在Pandas中,虽然这种技术可以使用,但有些情况下存在更短、更高效的方法。 - Ami Tavory
显示剩余4条评论

4

选项 1

df[~pd.DataFrame(df.A.values.tolist()).duplicated()]

在此输入图片描述

选项2

df[~df.A.apply(pd.Series).duplicated()]

这对于OP所问的情况非常高效和清晰。 - Ami Tavory
1
这是一个很好的解决方案,适用于所给的例子。但是我正在寻找更通用的解决方案,使用任意函数来识别列中的重复项。 - desiato

3

列表是不可哈希的。尝试将它们转换为可哈希类型,例如元组,然后您就可以继续使用drop_duplicates

df['A'] = df['A'].map(tuple)
df.drop_duplicates('A').applymap(list)

图片


使用函数实现的一种方法是基于计算系列对象的value_counts,因为重复的值会被聚合,我们只关心index部分(顺便说一下,这部分是唯一的),而不关心实际计数部分。

def series_dups(col_name):
    ser = df[col_name].map(tuple).value_counts(sort=False)
    return (pd.Series(data=ser.index.values, name=col_name)).map(list)

series_dups('A')

0    [1, 2]
1    [2, 3]
Name: A, dtype: object

如果您不想将值转换为 tuple,而是按原样处理这些值,可以执行以下操作: 玩具数据:
df = pd.DataFrame({'A': [[1,2], [2,3], [1,2], [3,4]], 
                   'B': [[10,11,12], [11,12], [11,12,13], [10,11,12]]})
df

Image

def series_dups_hashable(frame, col_names):
    for col in col_names:
        ser, indx = np.unique(frame[col].values, return_index=True)
        frame[col] = pd.Series(data=ser, index=indx, name=col)
    return frame.dropna(how='all')

series_dups_hashable(df, ['A', 'B'])   # Apply to subset/all columns you want to check

Image


1
这对于OP提出的问题非常高效和清晰。 - Ami Tavory
这是一个很好的解决方案,适用于我所举的例子。但是我正在寻找一些更通用的方法,使用任意函数来识别列中的重复项。 - desiato
@desiato:看看我的修改后的回答是否符合你的需求。 - Nickil Maveli
你假设我可以将列中的对象分别转换为可哈希对象。是否有可能不做出这种假设? - desiato
@desiato:是的,我现在才明白。(请参见编辑) - Nickil Maveli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接