使用Python列表理解计算列表中的正整数元素

使用Python列表理解计算列表中的正整数元素,python,list,sum,integer,list-comprehension,Python,List,Sum,Integer,List Comprehension,我有一个整数列表,需要计算其中有多少大于0。 我目前正在做一个列表理解,如下所示: sum([1 for x in frequencies if x > 0]) 这似乎是一个不错的理解,但我真的不喜欢“1”;这似乎有点像一个神奇的数字。有没有更像蟒蛇的方法 您可以在筛选列表上使用len(): len([x for x in frequencies if x > 0]) 一种稍微更具python风格的方法是使用生成器: sum(1 for x in frequencies if x

我有一个整数列表,需要计算其中有多少大于0。
我目前正在做一个列表理解,如下所示:

sum([1 for x in frequencies if x > 0])
这似乎是一个不错的理解,但我真的不喜欢“1”;这似乎有点像一个神奇的数字。有没有更像蟒蛇的方法

您可以在筛选列表上使用
len()

len([x for x in frequencies if x > 0])

一种稍微更具python风格的方法是使用生成器:

sum(1 for x in frequencies if x > 0)

这样可以避免在调用
sum()

之前生成整个列表。如果要减少内存量,可以使用生成器避免生成临时列表:

sum(x > 0 for x in frequencies)
这是因为
bool
int
的子类:

>>> isinstance(True,int)
True
True
的值为1:

>>> True==1
True
然而,正如Joe Golton在评论中指出的,这个解决方案不是很快。如果您有足够的内存来使用中间临时列表,那么可能会更快。以下是比较各种解决方案的一些时间安排:

>>> frequencies = [random.randint(0,2) for i in range(10**5)]

>>> %timeit len([x for x in frequencies if x > 0])   # sth
100 loops, best of 3: 3.93 ms per loop

>>> %timeit sum([1 for x in frequencies if x > 0])
100 loops, best of 3: 4.45 ms per loop

>>> %timeit sum(1 for x in frequencies if x > 0)
100 loops, best of 3: 6.17 ms per loop

>>> %timeit sum(x > 0 for x in frequencies)
100 loops, best of 3: 8.57 ms per loop
请注意,timeit结果可能因Python、操作系统或硬件的版本而异

当然,如果你在一个大的数字列表上做数学,你可能应该使用NumPy:

>>> frequencies = np.random.randint(3, size=10**5)
>>> %timeit (frequencies > 0).sum()
1000 loops, best of 3: 669 us per loop
NumPy数组需要的内存比同等的Python列表少,计算速度比任何纯Python解决方案都快。

这怎么样

reduce(λx,y:x+1,如果y>0,其他x,频率)

编辑: 灵感来自@~unutbu的公认答案:


reduce(λx,y:x+(y>0),频率)

这是可行的,但将
bool
s添加为
int
s可能会有危险。请对这段代码持保留态度(首先考虑可维护性):


如果数组只包含>=0的元素(即所有元素都是0或正整数),则可以只计算零并从数组长度中减去该数字:

len(arr) - arr.count(0)

我想指出,所有这些都适用于清单。如果我们有一个numpy数组, 有些解决方案的速度至少要快40倍

总结给出的所有解决方案和效率测试,再加上添加一些(必须修改reduce代码才能在Python 3中运行),请注意最后一个答案是用micros,而不是millis:

复制可复制格式的代码:

import random
import functools
frequencies = [random.randint(0,2) for i in range(10**5)]
from collections import Counter
import numpy as np

%timeit len([x for x in frequencies if x > 0])   # sth
%timeit sum([1 for x in frequencies if x > 0])
%timeit sum(1 for x in frequencies if x > 0)
%timeit sum(x > 0 for x in frequencies)
%timeit functools.reduce(lambda x, y: x + (y > 0), frequencies)
%timeit Counter(frequencies)

#'-------Numpy-----------------------')
%timeit ((np.array(frequencies))>0).sum()
npf=np.array(frequencies)
#'-------Numpy without conversion ---')
%timeit (npf>0).sum()


更好的是,使用生成器(条带[和]),您可以使用过滤器使其看起来更清晰。len(filter(lambda x:x>0,frequencies))@Jonathan:如果你喜欢
filter()
或列表理解,我会说这是一个品味问题,但通常列表理解比函数式编程更受欢迎。(OP要求提供列表理解。)OP实际上只说他现在正在使用一个(s)合适的列表理解,但没有特别要求。当然,你的主要观点仍然成立。@JonathanSternberg:在Python 3中,这种语法不起作用(你不能对筛选器对象执行
len()
)。一个变体:[x>0表示x的频率]。count(True)@Peter:注意,你的建议在数据上循环了两次;一次用于构建输出列表,两次用于计算真值。依赖于将布尔计算解释为1是a)可以说是糟糕的做法,而B)要慢得多。+1稍微更具可读性。然而,我发现它需要花费大约52%的时间(我测试的函数大量计算了因子的数量)。所以只用于迭代次数很少的理解(<10000?)。@JoeGolton:谢谢你的评论。事实上,有更快的解决方案,比如sth,或者使用NumPy。我希望我能在投票时得到一个评论,从我的错误中吸取教训。拜托?现在似乎有一种趋势,从lambda函数转向列表理解。我不是那个否决你们的人;然而,我认为人们倾向于对减少、逐步淘汰等(通过圭多公告)表示不满。我喜欢
reduce
,但我也不赞成在这种情况下使用它,因为
sum(x>0…
变量对我来说似乎更简单。+1因为这是一种通常被忽略的理解方式。如果在函数调用中评估列表理解,如果没有任何元素符合条件,则可以忽略
[]
。中断。@FogleBird:空生成器的
sum()
返回0。你是对的。我被搞糊涂了,想到了
min()
max()
计数非零元素与计数元素>0是不同的。标题应该相应地修改。我更新了你问题的标题,以便它反映问题的内容。我希望这对您没问题。在Python 2和Python 3中,将布尔值添加为整数是可以保证工作的:
import random
import functools
frequencies = [random.randint(0,2) for i in range(10**5)]
from collections import Counter
import numpy as np

%timeit len([x for x in frequencies if x > 0])   # sth
%timeit sum([1 for x in frequencies if x > 0])
%timeit sum(1 for x in frequencies if x > 0)
%timeit sum(x > 0 for x in frequencies)
%timeit functools.reduce(lambda x, y: x + (y > 0), frequencies)
%timeit Counter(frequencies)

#'-------Numpy-----------------------')
%timeit ((np.array(frequencies))>0).sum()
npf=np.array(frequencies)
#'-------Numpy without conversion ---')
%timeit (npf>0).sum()