我有一个名为vals
的float的长度N列表和名为bits
的0和1的长度N列表。我想提取两个列表:vals
中与bits
中的0相对应的元素,以及与bits
中的1相对应的其余元素。我目前正在做:
valsbits = zip(vals,bits)
els0 = [v for v,b in valsbits if b == 0]
els1 = [v for v,b in valsbits if b == 1]
但一定有更好的办法。另外,我对许多不同的bits
向量执行此操作,因此可能有一个聪明的方法来完成整个操作。在
Tags:
您可以使用^{} ,它在选择器中生成与true相对应的元素。在
但是,这将需要复制
bits
并反转一个副本以选择零的元素,最终结果是:我相信使用一个简单的
^{pr2}$for
循环会更容易和更快,因为它只进行一次迭代:出于好奇,以下是一些包含各种解决方案的微基准测试:
显然
numpy
比其他的快得多,假设可以用numpy数组替换python列表。在似乎}稍微多一些。在
itertools.compress
是最快的非第三方解决方案,在1.44 ms
。第二快的是朴素的for
,在1.87
处有一个if-else
,其他的解决方案比{增加元素的数量我所看到的唯一变化是Jon Clement的}仍然比
defaultdict(list)
解和newtower的[[], []]
解变得比朴素的for
+if-else
(就像2%
快500000
)。compress
仍然比其他人快30%,而{compress
快4倍左右。在这种区别对你来说重要吗?如果不是(并配置文件以检查它是否是瓶颈!)我只考虑使用更具可读性的解决方案,这在很大程度上是主观的,由您决定。在
关于我得到的时间安排的最后一句话:
即使
compress
和您的双列表理解在列表上迭代两次,一个是最快的非第三方解决方案,另一个是最慢的。 这里您可以看到“python级循环”或“显式循环”与“C级循环”或“隐式循环”之间的区别。在itertools.compress
是在C
中实现的,这使得它可以迭代而不需要太多的intepreter开销。正如你所看到的,这有很大的不同。在您可以在
numpy
解决方案中看到更多这一点,它也执行两次迭代而不是一次迭代。在本例中,循环不仅是“在C级别”,而且还完全避免了调用pythonapi在数组上迭代,因为numpy
有自己的C数据类型。在这几乎是CPython中的一条规则:为了提高性能,尝试用隐式循环替换显式循环,使用内置函数或在C扩展中定义的函数。在
Guido van Rossum很清楚这一点,试着读他的An Optimization Anecdote。在
你可以在thisSO问题中找到另一个类似的例子(免责声明:接受的答案是我的。我利用了二分搜索和字符串相等(>;C级内置)来获得比纯python线性搜索更快的解决方案。在
您可以使用以下方法(或只使用
collections.defaultdict(list)
):它只扫描一次列表,并对多个真/假值进行分组,但需要为新列表提供辅助存储。在
您可以尝试使用
numpy
数组来获得更好的性能:相关问题 更多 >
编程相关推荐