如何在Python中有效地过滤重复的行?

时间:2011-02-28 20:25:55

标签: python duplicates

我的问题看起来像是经典问题,但我无法在stackoverflow中找到完全相同的问题。我希望我的不是一个重复的问题。

我有一个大文件。该文件有许多行和固定列。我对所有列中的A列和B列感兴趣。目标是我想得到行,其中(1)行中的列A中的值也出现在其他行中,以及(2)有多个行具有相同的列A值,但是B列的不同值。

考虑下表。我对行1,3和5感兴趣,因为“a”出现在3行中,而列B中的值是不同的。相反,我对第2行和第4行不感兴趣,因为“b”出现两次,但其在B列中的对应值始终为“1”。同样,我对第6行不感兴趣,因为“c”只出现一次。

# A B C D
=========
1 a 0 x x
2 b 1 x x
3 a 2 x x
4 b 1 x x
5 a 3 x x
6 c 1 x x

要查找此类列,我会读取文件中的所有行,使用对象转换每一行,为对象创建列表,并使用以下算法查找有趣的列。该算法有效,但我的数据集需要时间。您有什么建议可以使算法有效吗?

def getDuplicateList(oldlist):
    # find duplicate elements
    duplicate = set()
    a_to_b = {}
    for elements in oldlist:
        a = elements.getA()
        b = elements.getB()
        if a in a_to_b:
            if b != a_to_b[a]:
                duplicate.add(a)
        a_to_b[a] = b 

    # get duplicate list
    newlist = []
    for elements in oldlist:
        a = elements.getA()
        if a in duplicate:
            newlist.append(a)

    return newlist

P.S。我添加了一些限制来澄清。

  1. 我使用的是Python 2.7
  2. 我需要“所有有趣的行”:duplicate有“一些”有趣的“a”。
  3. 订单很重要
  4. 实际上,数据是程序执行的内存访问。列A有内存访问,列B有一些我感兴趣的条件。如果内存访问在运行时有几个条件,那么我想研究一下内存访问的顺序。

5 个答案:

答案 0 :(得分:0)

原始订单是否需要维护?如果没有,它看起来与groupby非常相似,并且您可能会因使用内置方法而获得性能提升。

也许是这样的(未经测试!):

s = sorted(oldlist, key=lambda e: (e.getA(), e.getB()))
interesting = (g for k,g in itertools.groupby(s, lambda e: e.getA())
               if len(g) > 1)

答案 1 :(得分:0)

你的复杂性已经非常好了。你只是想在这里寻找线性加速。

有没有理由你不能只返回duplicate而不是做第二次循环?

如果您添加else,则可以避免重新插入a_to_b[a] = b

此外,磁盘I / O速度很慢,并且在等待读取时CPU有很多时间用于其他事情。由于你有很多这样做,你可以通过让一个线程找到重复项而另一个线程正在读取下一个文件来获得显着的加速。

答案 2 :(得分:0)

以下内容非常简单。它产生有趣行的A值;修改它以产生行很简单:

def isInteresting(rows):
    avals = {}
    for row in rows:
        bvals = avals.get(row.getA()) or set()
        bvals.add(rowgetB())
        avals[row.getA()] = bvals

    return [ aval
             for aval in avals.keys()
             if avals[aval] and len(avals[aval]) > 1 ]

答案 3 :(得分:0)

嗯,oldlist中元素的两次迭代可以用一次迭代代替。我相信在大多数情况下,这会提高算法的效率,特别是对于长列表。

如果newlist的顺序与您无关,我建议使用与您的算法具有相同结果的单循环替换。我已经对随机生成的百万元素列表进行了测试,它总是在大约一半的时间内运行:

def new_getDuplicateList(oldlist):
    # find duplicate elements
    newlist = []
    duplicate = set()
    a_to_b = {}
    for elements in oldlist:
        a = elements[0]
        b = elements[1]
        if a in duplicate:
            newlist.append(a)
        else:
            if a in a_to_b.keys():
                if not b in a_to_b[a]:
                    a_to_b[a].append(b)
                    duplicate.add(a)
                    extension = [a for i in a_to_b[a]]
                    newlist.extend(extension)
                else:
                    a_to_b[a].append(b)
            else:
                a_to_b[a] = [b]

    return newlist

(可能会使条件变得更漂亮。)修改它以输出整行而不仅仅是a值非常容易,只需将a替换为(a, b)必要时。另请注意,由于a_to_b dicts(现在包含列表),它比第一个算法消耗的内存更多。

答案 4 :(得分:0)

从列表中的不同项目创建对象可能会导致一些减速。在这里,我只是使用collections模块创建一个multiset,并让容器自己整理出不相关的项目。看看这对你有什么用。我假设你上面提供的确切文件格式。

import collections

def get_interesting_items(filename):
    multiset = collections.defaultdict(set)

    with open(filename) as f:
        # skip header lines
        f.readline()
        f.readline()

        # add all B items to Bset, indexed by A
        for line in f:
            _, a, b, _ = line.split(' ', 3)
            multiset[a].add(int(b))

        # generate all A, Bset pairs where Bset contains at least 2 items.
        for a, bset in multiset.iteritems():
            if len(bset) >= 2:
                yield a, bset

def main():
    for a, bset in get_interesting_items('myfile.txt'):
        print a, bset