在 pandas DataFrame 中查找重复行的索引

24

如何使用 pandas 在不迭代每一行的情况下找到给定 DataFrame 中相同行的索引?

虽然可以使用 unique = df[df.duplicated()] 找到所有唯一的行,然后使用 unique.iterrows() 迭代唯一条目,并借助 pd.where() 提取相等条目的索引,但什么是 pandas 的方法呢?

示例:给定以下结构的 DataFrame:

  | param_a | param_b | param_c
1 | 0       | 0       | 0
2 | 0       | 2       | 1
3 | 2       | 1       | 1
4 | 0       | 2       | 1
5 | 2       | 1       | 1
6 | 0       | 0       | 0

输出:

[(1, 6), (2, 4), (3, 5)]
2个回答

31
使用参数duplicated,并将keep=False用于所有重复行,然后按所有列进行groupby操作,并将索引值转换为元组,最后将输出的Series转换为list
df = df[df.duplicated(keep=False)]

df = df.groupby(list(df)).apply(lambda x: tuple(x.index)).tolist()
print (df)
[(1, 6), (2, 4), (3, 5)]
如果您想查看重复的值:
df1 = (df.groupby(df.columns.tolist())
       .apply(lambda x: tuple(x.index))
       .reset_index(name='idx'))
print (df1)
   param_a  param_b  param_c     idx
0        0        0        0  (1, 6)
1        0        2        1  (2, 4)
2        2        1        1  (3, 5)

5
我尝试了你的解决方案,虽然它可以处理玩具示例,但是当我在我的数据框上尝试使用它时,它抛出了错误*AttributeError: 'DataFrame' object has no attribute 'tolist'*。你的解决方案是否只适用于仅包含数字的DF(我的DF条目大多是字符串)? - user2954167
1
当所有行都是唯一的,导致空数据框时,我会收到AttributeError错误。通过在第一行中使用“df = df.loc[df.duplicated(subset=['a','b','c'],keep=False),['a','b','c']]”进行修复。 - chepyle

6

方法一

这是一个向量化的方法,受到这篇文章的启发。

def group_duplicate_index(df):
    a = df.values
    sidx = np.lexsort(a.T)
    b = a[sidx]

    m = np.concatenate(([False], (b[1:] == b[:-1]).all(1), [False] ))
    idx = np.flatnonzero(m[1:] != m[:-1])
    I = df.index[sidx].tolist()       
    return [I[i:j] for i,j in zip(idx[::2],idx[1::2]+1)]

示例运行 -


In [42]: df
Out[42]: 
   param_a  param_b  param_c
1        0        0        0
2        0        2        1
3        2        1        1
4        0        2        1
5        2        1        1
6        0        0        0

In [43]: group_duplicate_index(df)
Out[43]: [[1, 6], [3, 5], [2, 4]]

方法二

对于整数编号的数据框,我们可以将每一行缩减为一个标量,这样就能使用1D数组进行更高效的操作,例如:

def group_duplicate_index_v2(df):
    a = df.values
    s = (a.max()+1)**np.arange(df.shape[1])
    sidx = a.dot(s).argsort()
    b = a[sidx]

    m = np.concatenate(([False], (b[1:] == b[:-1]).all(1), [False] ))
    idx = np.flatnonzero(m[1:] != m[:-1])
    I = df.index[sidx].tolist() 
    return [I[i:j] for i,j in zip(idx[::2],idx[1::2]+1)]

运行时测试

其他方法 -

def groupby_app(df): # @jezrael's soln
    df = df[df.duplicated(keep=False)]
    df = df.groupby(df.columns.tolist()).apply(lambda x: tuple(x.index)).tolist()
    return df

时间 -

In [274]: df = pd.DataFrame(np.random.randint(0,10,(100000,3)))

In [275]: %timeit group_duplicate_index(df)
10 loops, best of 3: 36.1 ms per loop

In [276]: %timeit group_duplicate_index_v2(df)
100 loops, best of 3: 15 ms per loop

In [277]: %timeit groupby_app(df) # @jezrael's soln
10 loops, best of 3: 25.9 ms per loop

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