Pythonic可迭代的差异

时间:2011-08-14 17:29:41

标签: python python-2.7

我已经编写了一些代码来查找一个可迭代而不是另一个迭代的所有项目,反之亦然。我最初使用内置的设置差异,但计算速度相当慢,因为每组中存储了数百万个项目。因为我知道最多会有几千个差异我写下了以下版本:

def differences(a_iter, b_iter):
    a_items, b_items = set(), set()

    def remove_or_add_if_none(a_item, b_item, a_set, b_set):
        if a_item is None:
            if b_item in a_set:
                a_set.remove(b_item)
            else:
                b_set.add(b)

    def remove_or_add(a_item, b_item, a_set, b_set):
        if a in b_set:
            b_set.remove(a)
            if b in a_set:
                a_set.remove(b)
            else:
                b_set.add(b)
            return True
        return False

    for a, b in itertools.izip_longest(a_iter, b_iter):
        if a is None or b is None:
            remove_or_add_if_none(a, b, a_items, b_items)
            remove_or_add_if_none(b, a, b_items, a_items)
            continue

        if a != b:
            if remove_or_add(a, b, a_items, b_items) or \
               remove_or_add(b, a, b_items, a_items):
                continue
            a_items.add(a)
            b_items.add(b)

    return a_items, b_items

但是,上面的代码似乎并不是非常pythonic所以我正在寻找替代方案或改进建议。

3 个答案:

答案 0 :(得分:2)

这是一个更加pythonic的解决方案:

a, b = set(a_iter), set(b_iter)

return a - b, b - a

Pythonic并不意味着快速,而是优雅和可读。

这是一个可能更快的解决方案:

a, b = set(a_iter), set(b_iter)

# Get all the candidate return values
symdif = a.symmetric_difference(b)

# Since symdif has much fewer elements, these might be faster
return symdif - b, symdif - a

现在,关于在Python中编写自定义“快速”算法而不是使用内置操作:这是一个非常糟糕的主意。

集合运算符经过大量优化,用C语言编写,通常比Python快得多。 您可以在C(或Cython)中编写算法,但请记住,Python的集合算法是由世界级的天才编写和优化的。 除非你非常擅长优化,否则可能不值得付出努力。另一方面,如果您确实设法加快了速度,请分享您的代码;我敢打赌它本身就有可能进入Python。

要获得更实际的方法,请尝试取消对Python代码的调用。例如,如果您的对象具有自定义相等运算符,请找出删除它的方法。

但不要抱有希望。处理数百万条数据总是需要很长时间。我不知道你在哪里使用它,但也许最好让计算机忙一分钟而不是花时间优化设置算法?

答案 1 :(得分:0)

我认为您的代码已损坏 - 请使用[1,1][1,2]进行尝试,然后您将1置于一个集合中,而不是另一个集合。

> print differences([1,1],[1,2])                                                   
(set([1]), set([2]))

你可以追溯到if a != b测试的效果(假设有关于简单集差异中不存在的排序)。

没有那个可能会丢弃许多值的测试,我认为你的方法不会比内置集更快。这个论点是这样的:你真的需要在内存中创建一个集来保存所有数据(你的错误来自于不这样做)。一个天真的集合方法创建两个集合。所以你能做的最好的事情是节省一半的时间,你还必须在python中完成可能有效的c代码的工作。

答案 2 :(得分:0)

我认为python set操作将是你从标准库中获得的最佳性能。

也许这是你选择的特定实现问题,而不是数据结构和伴随操作本身。这是一个替代实现,可以为您提供更好的性能。

对于序列较大的序列比较任务,尽可能避免将构成序列的对象放入用于比较的容器中 - 更好地使用索引。如果序列中的对象是无序的,则对它们进行排序。

例如,我使用NumPy这个数字python库来完成这些任务:

# a, b are 'fake' index arrays of type boolean
import numpy as NP
a, b  = NP.random.randint(0, 2, 10), NP.random.randint(0, 2, 10)
a, b = NP.array(a, dtype=bool), NP.array(b, dtype=bool)

# items a and b have in common:
NP.sum(NP.logical_and(a, b))

# the converse (the differences)
NP.sum(NP.logical_or(a, b))