【问题标题】:Python Multidimensional Arrays - most efficient way to count number of non-zero entriesPython多维数组 - 计算非零条目数量的最有效方法
【发布时间】:2011-05-16 17:17:46
【问题描述】:

你好,周六欢乐之夜,

我在 python 中四处走动,我很享受它。

假设我有一个 python 数组:

x = [1, 0, 0, 1, 3]

计算列表中所有非零元素的最快方法是什么(答案:3)?如果可能的话,我也想不使用 for 循环 - 可能是最简洁和简洁的方式,在概念上说一些类似

[counter += 1 for y in x if y > 0]

现在 - 我真正的问题是我有一个多维数组,而我真正想要避免的是执行以下操作:

for p in range(BINS):
    for q in range(BINS):
        for r in range(BINS):
            if (mat3D[p][q][r] > 0): some_feature_set_count += 1

从我看到的小蟒蛇中,我的直觉是有一种非常简洁的语法(和高效)的方式来做到这一点。

有什么想法吗?

【问题讨论】:

    标签: python arrays multidimensional-array


    【解决方案1】:

    对于一维情况:

    sum(1 for i in x if i)
    

    对于多维情况,可以嵌套:

    sum(sum(1 for i in row if i) for row in rows)
    

    或在一个结构中完成所有操作:

    sum(1 for row in rows
          for i in row if i)
    

    【讨论】:

    • +1 用于主导语法。 @原始海报:至于效率,您的代码尽可能高效。它具有任务所需的最小空间和时间复杂性(如果您使用 Python xrange 而不是 range)此外,我个人觉得您的代码虽然更长且更少 pythonesque,比任何句法技巧都清晰。
    • 同样有效的东西(因为True == 1)是sum(bool(i) for i in x) ..但它更适合与i>0等其他谓词一起计数
    • @Santiago 您应该为这两种选择计时。这可能会冒出 OP 的代码。
    • @THC4k:它会起作用,但效率不高(尤其是对于稀疏矩阵),因为它必须为每个元素产生一个值。
    • @Santiago:回到主题,列表推导式是 Python 的惯用语,我认为应该从第一天开始教用户如何使用它们,因为它们会产生更清晰、更高效的代码。我总是尽可能早地教人们这些东西,我发现,尽管他们必须经过短暂的心理调整,但他们总是在这个过程中成为更好的程序员,用更少的努力编写出更好的代码。
    【解决方案2】:

    如果您按照在 Python 中使用多维数组这一事实所建议的那样使用 numpy,则以下内容类似于 @Marcelo 的答案,但更简洁:

    >>> a = numpy.array([[1,2,3,0],[0,4,2,0]])
    >>> sum(1 for i in a.flat if i)
    5
    >>>
    

    【讨论】:

    • 如果你有numpy,你可以写np.sum(a)(假设是常规的import numpy as np)。
    【解决方案3】:

    如果你使用 numpy 并且你的 3D 数组是一个 numpy 数组,那么这个单线就可以解决问题:

    numpy.where(your_array_name != 0, 1, 0).sum()
    

    示例:

    In [23]: import numpy
    
    In [24]: a = numpy.array([ [[0, 1, 2], [0, 0, 7], [9, 2, 0]], [[0, 0, 0], [1, 4, 6], [9, 0, 3]], [[1, 3, 2], [3, 4, 0], [1, 7, 9]] ])
    
    In [25]: numpy.where(a != 0, 1, 0).sum()
    Out[25]: 18
    

    【讨论】:

      【解决方案4】:

      虽然可能不简洁,但这是我选择的解决方法,适用于任何维度:

      def sum(li):
        s = 0
        for l in li:
          if isinstance(l, list):
            s += sum(l)
          elif l:
            s += 1
        return s
      

      【讨论】:

      • 你真的不应该影响sum。这是一个内置的。
      【解决方案5】:
      def zeros(n):
          return len(filter(lambda x:type(x)==int and x!=0,n))+sum(map(zeros,filter(lambda x:type(x)==list,n)))
      

      不能说它是否是最快的方法,但它是递归的并且适用于 N 维列表。

      zeros([1,2,3,4,0,[1,2,3,0,[1,2,3,0,0,0]]]) => 10
      

      【讨论】:

        【解决方案6】:

        我会稍微改变马塞洛对以下问题的回答:

        len([x for x in my_list if x != 0])
        

        上面的 sum() 欺骗了我一秒钟,因为我认为他得到的是总值而不是计数,直到我看到 1 在开始时徘徊。我宁愿明确地使用 len()。

        【讨论】:

        • 这需要 O(N) 空间,这对于大型数组来说并不理想,而且它是无效的 Python 语法。我想你的意思是x != 0
        • 使用sum 来计算元素是非常惯用的。你应该习惯它,这样你就不必像在这里那样构建列表只是为了把它们扔掉。另外,我认为您的意思是 x is not 0 应该是 x != 0 本身应该是 x
        • 两个公平点,感谢您的提醒。更改了条件,但留下了 len,以便其他人可以看到不该做什么。列表推导是生成器吗?我可能忘记了这一点。
        【解决方案7】:

        使用链来减少数组查找:

        from itertools import chain
        BINS = [[[2,2,2],[0,0,0],[1,2,0]],
                [[1,0,0],[0,0,2],[1,2,0]],
                [[0,0,0],[1,1,1],[1,3,0]]]
        sum(1 for c in chain.from_iterable(chain.from_iterable(BINS)) if c > 0)
        14
        

        我没有对此进行任何性能检查。但它不使用任何重要的内存。 请注意,它使用的是生成器表达式,而不是列表推导。添加 [list comprehension] 语法将创建一个要求和的数组,而不是一次输入一个数字来求和。

        【讨论】:

          猜你喜欢
          • 2018-04-27
          • 1970-01-01
          • 1970-01-01
          • 2019-07-31
          • 2019-11-04
          • 2021-01-05
          • 2020-04-24
          • 2015-01-30
          • 1970-01-01
          相关资源
          最近更新 更多