【问题标题】:Change every single element in Numpy object更改 Numpy 对象中的每个元素
【发布时间】:2017-07-06 17:10:59
【问题描述】:

我有一个带有随机 N*M 个元素的 Numpy 对象,并且我还有两个数字 A 和 B。

现在我想访问这个 N*M 数组中的每个元素并进行更改,即如果元素 > 0,则将此元素替换为 A(即元素

我知道有一种简单的方法可以实现这种方法,即使用 for 循环访问每个元素,但速度很慢。

我们可以使用更多花哨的代码来实现吗?

【问题讨论】:

  • 试试numpy.clip
  • np.where

标签: numpy


【解决方案1】:

布尔掩码赋值将更改原地值:

In [493]: arr = np.random.randint(-10,10,(5,7))
In [494]: arr
Out[494]: 
array([[ -5,  -6,  -7,  -1,  -8,  -8, -10],
       [ -9,   1,  -3,  -9,   3,   8,  -1],
       [  6,  -7,   4,   0,  -4,   4,  -2],
       [ -3, -10,  -2,   7,  -4,   2,   2],
       [ -5,   5,  -1,  -7,   7,   5,  -7]])
In [495]: arr[arr>0] = 100
In [496]: arr[arr<0] = -50
In [497]: arr
Out[497]: 
array([[-50, -50, -50, -50, -50, -50, -50],
       [-50, 100, -50, -50, 100, 100, -50],
       [100, -50, 100,   0, -50, 100, -50],
       [-50, -50, -50, 100, -50, 100, 100],
       [-50, 100, -50, -50, 100, 100, -50]])

我刚刚在

中给出了类似的答案

python numpy: iterate for different conditions without using a loop

【讨论】:

    【解决方案2】:

    IIUC:

    narr = np.random.randint(-100,100,(10,5))
    array([[ 70, -20,  96,  73, -94],
           [ 42,  35, -55,  56,  54],
           [ 97, -16,  24,  32,  78],
           [ 49,  49, -11, -82,  82],
           [-10,  59, -42, -68, -70],
           [ 95,  23,  22,  58, -38],
           [ -2, -64,  27, -33, -95],
           [ 98,  42,   8, -83,  85],
           [ 23,  51, -99, -82,  -7],
           [-28, -11, -44,  95,  93]])
    A = 1000
    B = -999
    

    使用np.where:

    np.where(narr > 0, A, np.where(narr < 0, B , narr))
    

    输出:

    array([[1000, -999, 1000, 1000, -999],
           [1000, 1000, -999, 1000, 1000],
           [1000, -999, 1000, 1000, 1000],
           [1000, 1000, -999, -999, 1000],
           [-999, 1000, -999, -999, -999],
           [1000, 1000, 1000, 1000, -999],
           [-999, -999, 1000, -999, -999],
           [1000, 1000, 1000, -999, 1000],
           [1000, 1000, -999, -999, -999],
           [-999, -999, -999, 1000, 1000]])
    

    【讨论】:

    • 这很有帮助!但是原来的数组narr并没有改变,所以我需要分配新的内存来保存这个新的数组。我们可以在原地做这个改变吗?
    【解决方案3】:

    因为您提到您对计算速度感兴趣,所以我针对您的问题对几种不同方法进行了速度比较。

    test.py:

    import numpy as np
    
    A = 100
    B = 50
    
    def createArray():
      array = np.random.randint(-100,100,(500,500))
      return array
    
    def replace(x):
        return A if x > 0 else B
    
    def replace_ForLoop():
        """Simple for-loop."""
        array = createArray()
        for i in range(array.shape[0]):
            for j in range(array.shape[1]):
                array[i][j] = replace(array[i][j])
    
    def replace_nditer():
        """Use numpy.nditer to iterate over values."""
        array = createArray()
        for elem in np.nditer(array, op_flags=['readwrite']):
            elem[...] = replace(elem)
    
    def replace_masks():
        """Use boolean masks."""
        array = createArray()
        array[array>0] = A
        array[array<0] = B
    
    def replace_vectorize():
        """Use numpy.vectorize"""
        array = createArray()
        vectorfunc = np.vectorize(replace)
        array = vectorfunc(array)
    
    def replace_where():
        """Use numpy.where"""
        array = createArray()
        array = np.where(array > 0, A, np.where(array < 0, B , array))
    

    注意:使用嵌套 for 循环、np.nditer 和布尔掩码的变体就地工作,最后两个不工作。

    时序对比:

    > python -mtimeit -s'import test' 'test.replace_ForLoop()'                     
    10 loops, best of 3: 185 msec per loop
    > python -mtimeit -s'import test' 'test.replace_nditer()' 
    10 loops, best of 3: 294 msec per loop
    > python -mtimeit -s'import test' 'test.replace_masks()' 
    100 loops, best of 3: 5.8 msec per loop
    > python -mtimeit -s'import test' 'test.replace_vectorize()'
    10 loops, best of 3: 55.3 msec per loop
    > python -mtimeit -s'import test' 'test.replace_where()'    
    100 loops, best of 3: 5.42 msec per loop
    

    使用循环确实很慢。 numpy.nditer 甚至更慢,这让我感到惊讶,因为文档称它为 efficient multi-dimensional iterator object to iterate over arraysnumpy.vectorizeis essentially a for-loop,但仍然设法比天真的实现快三倍。 根据 hpaulj 的回答,Scott Boston 提出的 np.where 变体比使用布尔掩码稍快。但是,它确实需要更多内存,因为它不会就地修改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-26
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多