【问题标题】:What's the fastest way to compare two large lists of 1's & 0's and return the difference count/percentage?比较两个大的 1 和 0 列表并返回差异计数/百分比的最快方法是什么?
【发布时间】:2011-04-04 04:12:58
【问题描述】:

我需要一种方法来快速返回两个大列表之间的差异数。每个列表项的内容为 1 或 0(单个整数),每个列表中的项数将始终为 307200。

这是我当前代码的示例:

list1 = <list1> # should be a list of integers containing 1's or 0's
list2 = <list2> # same rule as above, in a slightly different order

diffCount = 0
for index, item in enumerate(list1):
    if item != list2[index]:
        diffCount += 1

percent = float(diffCount) / float(307200)

上述方法有效,但对于我的目的来说太慢了。我想知道是否有更快的方法来获取列表之间的差异数量,或者差异项目的百分比?

我在这个站点上查看了一些类似的线程,但它们似乎都与我想要的略有不同,并且 set() 示例似乎不适用于我的目的。 :P

【问题讨论】:

  • 不要认为使用 int 可以做得更好。如果它们是字节,您可以对它们进行异或,但我不知道如何从整数中获得更好的性能。
  • @bdares,您的回复。如果它们是字符串而不是 int 会有帮助吗?比较时的字符和列表顺序在这里真的很重要。
  • 第6行应该是if item != list2[index]吗?我不确定i 来自哪里。
  • @bdares,这是一个错字,list2[i] 自然应该是 list2[index] ;)
  • @hwiechers 你是对的。这是一个错字。

标签: python performance list compare


【解决方案1】:

如果您使用 NumPy 数组而不是列表,您至少可以获得 10 倍的加速。

import random
import time
import numpy as np
list1 = [random.choice((0,1)) for x in xrange(307200)]
list2 = [random.choice((0,1)) for x in xrange(307200)]
a1 = np.array(list1)
a2 = np.array(list2)

def foo1():
    start = time.clock()
    counter = 0
    for i in xrange(307200):
        if list1[i] != list2[i]:
            counter += 1
    print "%d, %f" % (counter, time.clock()-start)

def foo2():
    start = time.clock()
    ct = np.sum(a1!=a2)
    print "%d, %f" % (ct, time.clock()-start)

foo1() #153490, 0.096215
foo2() #153490, 0.010224

【讨论】:

  • 在我的机器上,对它们进行异或运算大约快三倍,np.sum(a1 ^ a2)
  • @Jeff OP 应该能够将他们的代码更改为始终使用数组而不是 Python 列表,然后您不必担心通过 np 将列表转换为数组所花费的时间。数组()
  • @Paul,谢谢你的建议。你是对的, numpy.array 确实比列表工作得快得多。问题是还必须考虑从列表到 numpy 数组的转换。当我在启动后将 a1 = np.array(list1) & a2 = np.array(list2) 添加到您的 foo 函数时,总时间平均要高得多:0.199788、0.165243、0.164008 等。它最终使我的时间加倍原始示例。 :(
  • @Jay P,我马上试试你的模组。
  • @AWainb。 ImageCore 对象可以很好地转换为 Numpy 数组。我一直都这样做。 (我什至在对您的原始帖子的回答中也这样做了)它们都使用相同的缓冲区结构,翻译几乎是即时的。只需执行以下操作:np.array(img)
【解决方案2】:

如果可能,使用Paul/JayP's answer of using numpy(带异或),如果只能使用python的stdlib,列表推导中的itertools' izip似乎最快:

import random
import time
import numpy
import itertools
list1 = [random.choice((0,1)) for x in xrange(307200)]
list2 = [random.choice((0,1)) for x in xrange(307200)]
a1 = numpy.array(list1)
a2 = numpy.array(list2)

def given():
  diffCount = 0
  for index, item in enumerate(list1):
      if item != list2[index]:
          diffCount += 1
  return diffCount

def xrange_iter():
  counter = 0
  for i in xrange(len(list1)):
    if list1[i] != list2[i]:
      counter += 1
  return counter

def np_not_eq():
  return numpy.sum(a1!=a2)

def np_xor():
  return numpy.sum(a1^a2)

def np_not_eq_plus_array():
  arr1 = numpy.array(list1)
  arr2 = numpy.array(list2)
  return numpy.sum(arr1!=arr2)

def np_xor_plus_array():
  arr1 = numpy.array(list1)
  arr2 = numpy.array(list2)
  return numpy.sum(arr1^arr2)

def enumerate_comprehension():
  return len([0 for i,x in enumerate(list1) if x != list2[i]])

def izip_comprehension():
  return len([0 for a,b in itertools.izip(list1, list2) if a != b])

def zip_comprehension():
  return len([0 for a,b in zip(list1, list2) if a != b])

def functional():
  return sum(map(lambda (a,b): a^b, zip(list1,list2)))

def bench(func):
  diff = []
  for i in xrange(100):
    start = time.clock()
    result = func()
    stop = time.clock()
    diff.append(stop - start)
  print "%25s -- %d, %f" % (func.__name__, result, sum(diff)/float(len(diff)))

bench(given)
bench(xrange_iter)
bench(np_not_eq)
bench(np_xor)
bench(np_not_eq_plus_array)
bench(np_xor_plus_array)
bench(enumerate_comprehension)
bench(zip_comprehension)
bench(izip_comprehension)
bench(functional)

我得到了这个(在 Python 2.7.1 上,Snow Leopard):

                    given -- 153618, 0.046746
              xrange_iter -- 153618, 0.049081
                np_not_eq -- 153618, 0.003069
                   np_xor -- 153618, 0.001869
     np_not_eq_plus_array -- 153618, 0.081671
        np_xor_plus_array -- 153618, 0.080536
  enumerate_comprehension -- 153618, 0.037587
        zip_comprehension -- 153618, 0.083983
       izip_comprehension -- 153618, 0.034506
               functional -- 153618, 0.117359

【讨论】:

  • 感谢您的努力,但您也忘记考虑 np.array(list) 调用必须包含在基准测试中这一事实。一旦将其添加到组合中,您的示例就会更高:给定 = 0.218102, xrange_iter = 0.217878, np_not_eq = 0.164513, np_xor = 0.161727, enumerate_comprehension = 0.216057, zip_comprehension = 0.302612, izip_comprehension = 0.2111请将数组创建添加到您的 bench 函数中,并请注意 list_comprehension、zip_iter 和 izip_iter 名称错误。 ;)
  • 这仍然是一个很好的例子,因为它展示了很多实现相同目标的方法。 :P 我在结果中注意到的一件事是,这两个基于 np 的函数返回的差异计数 (153163) 比其他函数 (153476) 低,知道为什么吗?
  • @AWainb 感谢您的更正。我已经修复了它们(我希望)。我没有考虑 np.array(list) 因为转换是一次性操作,但只有那些使用 numpy 的应该受到惩罚,而不是所有其他的。我创建了另外两个 numpy 测试来显示性能下降 np.array 的原因。我不是 numpy 专家,但是如果数组中有浮点数,整个数组会被强制为浮点数,这可能会导致一些不匹配。
【解决方案3】:

我实际上不知道这是否更快,但您可以尝试一些 python 提供的“函数式”方法。循环由内部手动编码的子程序运行通常会更好。

类似这样的:

diffCount = sum(map(lambda (a,b): a^b, zip(list1,list2)))

【讨论】:

  • 我刚刚对此函数进行了测试,但不幸的是,它所花费的时间几乎是@Amber 方法的两倍。不管怎样,谢谢你的建议! :D
  • 如果可能,宁愿使用built-in 而不是lambda。在我的机器上使用map(operator.xor, list1, list2)lambda 版本快大约1.7 倍。
【解决方案4】:
>>> import random
>>> import time
>>> list1 = [random.choice((0,1)) for x in xrange(307200)]
>>> list2 = [random.choice((0,1)) for x in xrange(307200)]
>>> def foo():
...   start = time.clock()
...   counter = 0
...   for i in xrange(307200):
...     if list1[i] != list2[i]:
...       counter += 1
...   print "%d, %f" % (counter, time.clock()-start)
...
>>> foo()
153901, 0.068593

百分之七秒对您的应用程序来说太慢了吗?

【讨论】:

  • @Amber,我会在几分钟内告诉你,够公平吗?? :P 我知道这听起来很小,但是这段代码是在我的网络摄像头的帧之间运行的。比较时间越长,视频就越不稳定。出于好奇,xrange 与常规范围有何不同?
  • xrange 返回一个“xrange 对象”而不是列表,并且不会将数据加载到内存中,仅在需要时创建数据,这在使用非常大的范围时会有所不同内存不足的机器或从未使用过该范围的所有元素时。 (来自 python 文档)
  • @AWainb xrange() 是一个迭代器,它不像 range() 那样预先创建一个实际的列表。这意味着它不需要分配所需的大块内存,并且在许多情况下可以产生更快的代码。
  • @Amber,请解释一下。使用 xrange 而不是 range 对其进行了测试,我可能应该复制上面的代码以正确反映我的代码,在我的版本中,我已经使用 range,在比较 xrange 和 range 结果之后它们看起来是相同的;也就是说,是的,显然百分之七秒太慢了。 :( 基本上我是从我的摄像头捕捉图像,与之前的图像进行比较,然后将图像放在我的 gui 中的 wxPanel 中。没有比较代码,我的 gui 中有一个实时摄像头提要,它可以完美地更新每个新帧。
  • @AWainb - 如果您不尝试在每一帧之间运行运动检测差异,您也许可以进行线程化 - 相反,比较每一帧,例如第 10 帧。
【解决方案5】:

我也会尝试以下仅限 stdlib 的方法:

import itertools as it, operator as op

def list_difference_count(list1, list2):
    return sum(it.starmap(op.ne, it.izip(list1, list2)))

>>> list_difference_count([1, 2, 3, 4], [1, 2, 1, 2])
2

这适用于len(list1) == len(list2)。如果您确定列表项始终为整数,则可以将op.xor 替换为op.ne(可以提高性能)。

差异百分比为:float(list_difference_count(l1, l2))/len(l1)

【讨论】:

    【解决方案6】:

    list3 和 list4 是等价的。不同的方法得到相同的结果

    list_size=307200
    list1=[random.choice([0,1]) for i in range(list_size)]
    list2=[random.choice([0,1]) for i in range(list_size)]
    
    list3=[]
    for i in range(list_size):
        list3.append(list1[i]&list2[i])
    
    #print(list3)
    print(sum(list3)/list_size)
    

    #或

    list4=[x&y for (x,y) in list(zip(list1,list2))]
    print(sum(list4)/list_size)
     
    

    【讨论】:

      猜你喜欢
      • 2014-02-26
      • 1970-01-01
      • 1970-01-01
      • 2013-08-08
      • 2020-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多