pandas drop_duplicates使用比较功能

时间:2016-09-15 08:31:04

标签: python pandas

是否有可能将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 个答案:

答案 0 :(得分:3)

选项1

or

enter image description here

选项2

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

答案 1 :(得分:3)

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

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

(请注意,第一个和第二个列表是等效的,但不相同。)

Python通常是prefers key functions to comparison functions,所以在这里我们需要一个函数来说明列表的关键是什么;在这种情况下,它是lambda l: l[0] + l[1]**2

我们可以使用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]

但是有些人不喜欢这些蒙特卡罗的事情。

答案 2 :(得分:3)

Lists本质上是不可取的。尝试将它们转换为可用类型,例如tuples,然后您可以继续使用drop_duplicates

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

Image

使用函数实现它的一种方法是基于计算系列对象的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