如何加快在数据框列中查找重复项

时间:2017-03-03 02:27:35

标签: python pandas numpy

我希望找到数据框列中出现重复值序列的索引。我希望结果是一个列表列表,其中每个子列表都是重复值索引的单独序列。

我当前的代码有效但速度很慢(10,000行数据帧中10%重复的apx 15毫秒):

import pandas as pd
import numpy as np
import time

# Given a dataframe and column, return a list of lists where each sublist
# contains indexes of the sequential duplicates
def duplicate_ranges(df, c):
    return to_ranges(df[c].shift(1) == df[c])

# Take a pandas Series of booleans and return a list of lists where each 
# sub-list is the indexs of sequential true values in the list
def to_ranges(s):
    r = []
    g = []
    for k, v in s.items():
        if v == True:
            g.append(k)
        elif len(g) > 0:
            r.append(g)
            g = []
    if len(g) > 0:
        r.append(g)
    return r

def bench_it(n):
    data = {"A": np.random.randint(10, 10000)}
    idxs = pd.date_range(start='2000-01-01', periods=10000)
    df = pd.DataFrame(data, index=idxs)
    t = time.time()
    for _ in range(0, n):
        r = duplicate_ranges(df, 'A')
    t = time.time() - t
    print("{:d} iterations took {:.1f} msec".format(n, 1000*t))

bench_it(1000)

据我所知,所有时间都花在to_ranges()的主循环中。我对熊猫和numpy很新,有没有人可以建议一种方法来加快速度呢?

1 个答案:

答案 0 :(得分:2)

这是一种利用scipy.sparse中的有效操作的快速方法:

from scipy.sparse import csr_matrix

def duplicate_ranges(df, c):
    index, values = df.index.values, df[c].values

    data = values
    indices = np.arange(len(values))
    indptr = np.concatenate([[0], np.where(np.diff(values) != 0)[0] + 1,
                             [len(values)]])

    M = csr_matrix((index, indices, indptr))[np.diff(indptr) > 1]
    M.sort_indices()
    return np.split(M.data, M.indptr[1:-1])

它比其他方法快一个数量级,因为它避免了整个数组上的Python循环(尽管split()函数中有一些Python循环,只在数组的子集中调用)。

旧答案:

以下是解决此问题的快捷方法:

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

def duplicate_ranges(df, c):
    index, values = df.index.values, df[c].values
    ranges = np.split(index, np.where(np.diff(values) != 0)[0] + 1)
    return [list(r) for r in ranges if len(r) > 1]

duplicate_ranges(df, 'A')
# [[2, 3, 4], [6, 7], [8, 9]]

由于它避免了嵌套循环并且只需要通过整个列,因此它应该比其他方法快得多。