【问题标题】:How do I remove all zero elements from a NumPy array?如何从 NumPy 数组中删除所有零元素?
【发布时间】:2023-03-10 21:58:01
【问题描述】:

我有一个 rank-1 numpy.array 我想制作一个箱线图。但是,我想排除数组中所有等于零的值。目前,我通过循环数组来解决这个问题,如果不等于零,则将值复制到一个新数组中。但是,由于数组包含 86 000 000 个值,而且我必须多次这样做,这需要很大的耐心。

有没有更智能的方法来做到这一点?

【问题讨论】:

    标签: python arrays numpy filtering


    【解决方案1】:

    [i for i in Array if i != 0.0] 如果数字是浮点数 或 [i for i in SICER if i != 0] 如果数字是整数。

    【讨论】:

    • 您的解决方案可能不如 numpy 高效,同时处理这两种类型您可以这样做[i for i in Array if i > 0]
    【解决方案2】:

    我决定比较这里提到的不同方法的运行时间。为此,我使用了我的库 simple_benchmark

    array[array != 0] 的布尔索引似乎是最快(也是最短)的解决方案。

    对于较小的数组,与其他方法相比,MaskedArray 方法非常慢,但与布尔索引方法一样快。但是对于中等大小的数组,它们之间并没有太大区别。

    这是我使用的代码:

    from simple_benchmark import BenchmarkBuilder
    
    import numpy as np
    
    bench = BenchmarkBuilder()
    
    @bench.add_function()
    def boolean_indexing(arr):
        return arr[arr != 0]
    
    @bench.add_function()
    def integer_indexing_nonzero(arr):
        return arr[np.nonzero(arr)]
    
    @bench.add_function()
    def integer_indexing_where(arr):
        return arr[np.where(arr != 0)]
    
    @bench.add_function()
    def masked_array(arr):
        return np.ma.masked_equal(arr, 0)
    
    @bench.add_arguments('array size')
    def argument_provider():
        for exp in range(3, 25):
            size = 2**exp
            arr = np.random.random(size)
            arr[arr < 0.1] = 0  # add some zeros
            yield size, arr
    
    r = bench.run()
    r.plot()
    

    【讨论】:

      【解决方案3】:

      在这种情况下,您想使用掩码数组,它会保持数组的形状,并且会被所有 numpy 和 matplotlib 函数自动识别。

      X = np.random.randn(1e3, 5)
      X[np.abs(X)< .1]= 0 # some zeros
      X = np.ma.masked_equal(X,0)
      plt.boxplot(X) #masked values are not plotted
      
      #other functionalities of masked arrays
      X.compressed() # get normal array with masked values removed
      X.mask # get a boolean array of the mask
      X.mean() # it automatically discards masked values
      
      【解决方案4】:

      您可以使用布尔数组进行索引。对于 NumPy 数组A

      res = A[A != 0]
      

      你可以像上面那样使用Boolean array indexingbool类型转换,np.nonzero,或者np.where。以下是一些性能基准测试:

      # Python 3.7, NumPy 1.14.3
      
      np.random.seed(0)
      
      A = np.random.randint(0, 5, 10**8)
      
      %timeit A[A != 0]          # 768 ms
      %timeit A[A.astype(bool)]  # 781 ms
      %timeit A[np.nonzero(A)]   # 1.49 s
      %timeit A[np.where(A)]     # 1.58 s
      

      【讨论】:

        【解决方案5】:

        一行简单的代码就可以得到一个排除所有“0”值的数组:

        np.argwhere(*array*)
        

        示例:

        import numpy as np
        array = [0, 1, 0, 3, 4, 5, 0]
        array2 = np.argwhere(array)
        print array2
        
        [1, 3, 4, 5]
        

        【讨论】:

        • np.argwhere 只返回非零元素的索引
        • 所以这个数组,幸运的是,碰巧似乎满足了这个问题,但具有误导性。根据 argwhere 的结果,您可以重构非零数组,但这是一个额外的步骤。
        【解决方案6】:

        我建议您在这种情况下简单地使用NaN,在这种情况下,您希望忽略一些值,但仍希望保持过程统计尽可能有意义。所以

        In []: X= randn(1e3, 5)
        In []: X[abs(X)< .1]= NaN
        In []: isnan(X).sum(0)
        Out[: array([82, 84, 71, 81, 73])
        In []: boxplot(X)
        

        【讨论】:

        • 啊,这里用NaN确实比较合适,谢谢。因此,我不再需要将我的数据复制到具有不同大小的新数组,但我可以保留原始数组以及数组中的这样的位置。谢谢!
        • 您是否知道一种使用列表理解来循环它的方式?即我有一个字典a 其中a[k] 是一个NumPy 数组所以我想做[a[k][abs(a[k])&lt;.1]=float('NaN') for k in data] 但这似乎在循环中失败了,而只有在循环中执行命令似乎工作...
        • @rubae:我认为您应该提出一个与此列表理解问题相关的单独问题。不幸的是,弄清楚你的实际目标不再那么简单了:(。据我所知;不要被列表理解所迷惑,也许你只是在寻找像这样简单的东西:for k in data: a[k][abs(a[k])&lt; .1]= NaN ?
        【解决方案7】:

        对于 NumPy 数组 a,可以使用

        a[a != 0]
        

        提取不等于零的值。

        【讨论】:

        • 非常感谢,这确实工作得更快(!)。是否可以在更高级别的 NumMpy 数组或矩阵上执行类似的操作?因为这里出现了dimenions将不再正确匹配的问题...
        • @rubae:如果a 具有更高的维度,则结果将是一个扁平的(一维)数组。也可以删除全为零的列或行。
        • ...其中anp.array。这不适用于内置的 python 数组。
        猜你喜欢
        • 2021-06-11
        • 2014-11-24
        • 2019-08-31
        • 1970-01-01
        • 2019-05-01
        • 2019-11-28
        • 1970-01-01
        • 2015-04-02
        相关资源
        最近更新 更多