【问题标题】:Counting positive integer elements in a list with Python list comprehensions使用 Python 列表推导计算列表中的正整数元素
【发布时间】:2010-05-24 20:26:22
【问题描述】:

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

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

这似乎是一个不错的理解,但我不太喜欢“1”;这似乎是一个神奇的数字。有没有更 Pythonish 的方式来做到这一点?

【问题讨论】:

  • 计数非零元素与计数元素> 0不同。标题应相应修改
  • 我更新了您问题的标题,以反映其内容。我希望这对你来说很好。

标签: python list sum integer list-comprehension


【解决方案1】:

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

sum(x > 0 for x in frequencies)

这是因为boolint 的子类:

>>> isinstance(True,int)
True

True的值为1:

>>> True==1
True

但是,正如 Joe Golton 在 cmets 中指出的那样,这种解决方案并不是很快。如果您有足够的内存来使用中间临时列表,那么sth's solution 可能会更快。以下是比较各种解决方案的一些时间安排:

>>> 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 解决方案都要快。

【讨论】:

  • 变体:[x > 0 for x in frequency].count(True)
  • @Peter:请注意,您的建议在数据上循环了两次;一次构建输出列表,两次计算 True 值。
  • 依赖布尔评估被解释为 1 是 a) 可以说是糟糕的实践,并且 B) 慢得多。
  • +1 更易读。但是,我发现它需要大约 52% 的时间(我测试的函数计算了大量因素的数量)。因此,仅用于迭代次数很少的推导(
  • @JoeGolton:感谢您的评论。确实有更快的解决方案,例如 sth's,或者使用 NumPy。
【解决方案2】:

一个稍微更 Pythonic 的方法是使用生成器:

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

这避免了在调用sum()之前生成整个列表。

【讨论】:

  • +1 因为这是一种通常被忽视的理解方式。如果您在函数调用中评估列表推导,则可以省略 []
  • 如果没有任何元素符合条件,则中断。
  • @FogleBird:空生成器的sum() 返回 0。
  • 你是对的。我很困惑,在想min()max()
【解决方案3】:

您可以在过滤后的列表中使用len()

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

【讨论】:

  • 更好的是,使用生成器(strip [和])
  • 您可以使用过滤器使其看起来更清晰。 len(filter(lambda x: x > 0, 频率))
  • @Jonathan:如果您更喜欢filter() 或列表推导式,我会说这是一个品味问题,但通常列表推导式优于函数式编程风格。 (并且 OP 要求列表理解。)
  • OP 实际上只说他现在正在使用一个像样的列表理解,但没有特别要求一个。当然,你的主要观点仍然成立。
  • @JonathanSternberg:在 Python 3 中,该语法不起作用(您不能对过滤器对象执行 len())。
【解决方案4】:

这可行,但将bools 添加为ints 可能很危险。请对这段代码持保留态度(可维护性优先):

sum(k>0 for k in x)

【讨论】:

【解决方案5】:

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

len(arr) - arr.count(0)

【讨论】:

    【解决方案6】:

    这个怎么样?

    reduce(lambda x, y: x+1 if y > 0 else x, frequencies)

    编辑: 从@~unutbu 接受的答案中获得灵感:

    reduce(lambda x, y: x + (y > 0), frequencies)

    【讨论】:

    • 我希望我能得到一个评论来支持那次否决票,以便从我的错误中学习。请问?
    • 似乎有从 lambda 函数转向列表推导的趋势。
    • 我不是反对你的人;但是我认为人们倾向于对reduce 皱眉头,它被逐步淘汰等(通过 Guido 公告)。我喜欢reduce,但我也不赞成在这种情况下使用它,因为sum(x > 0…) 变体对我来说似乎更直接。
    【解决方案7】:

    我想指出,所有所说的都适用于列表。如果我们有一个 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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-07
      • 2014-02-05
      • 2023-02-25
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 2022-01-10
      • 1970-01-01
      相关资源
      最近更新 更多