没有循环的 if/else 的 Numpy 等价物

Posted

技术标签:

【中文标题】没有循环的 if/else 的 Numpy 等价物【英文标题】:Numpy equivalent of if/else without loop 【发布时间】:2018-01-27 19:49:04 【问题描述】:

在下面的代码中是否有任何 Pythonic 方法可以删除 for 循环和 if/else。

此代码遍历 NumPy 数组并检查条件并根据条件更改值。

>>> import numpy as np
>>> x=np.random.randint(100, size=(10,5))
>>> x
array([[79, 50, 18, 55, 35],
       [46, 71, 46, 95, 52],
       [97, 37, 71,  2, 79],
       [80, 96, 60, 85, 72],
       [ 6, 52, 63, 86, 38],
       [35, 50, 13, 93, 54],
       [69, 21,  4, 40, 53],
       [83,  7, 30, 16, 78],
       [18, 34, 91, 67, 89],
       [82, 16, 16, 24, 80]])

>>> for i in range(x.shape[0]):
    for j in range(x.shape[1]):
        if x[i,j]>50:
            x[i,j]=0
        elif x[i,j]<50:
            x[i,j]=1


>>> x
array([[ 0, 50,  1,  0,  1],
       [ 1,  0,  1,  0,  0],
       [ 0,  1,  0,  1,  0],
       [ 0,  0,  0,  0,  0],
       [ 1,  0,  0,  0,  1],
       [ 1, 50,  1,  0,  0],
       [ 0,  1,  1,  1,  0],
       [ 0,  1,  1,  1,  0],
       [ 1,  1,  0,  0,  0],
       [ 0,  1,  1,  1,  0]])

我想在没有循环和 if 语句的情况下做同样的事情。 由于数组的变化,像下面这样的东西不起作用:

>>> import numpy as np
>>> x=np.random.randint(100, size=(10,5))
>>> x
array([[ 2, 88, 27, 67, 29],
       [62, 44, 62, 87, 32],
       [80, 95, 31, 30, 33],
       [14, 41, 40, 95, 27],
       [53, 30, 35, 22, 98],
       [90, 39, 74, 28, 73],
       [10, 71,  0, 11, 37],
       [28, 25, 83, 24, 93],
       [30, 70, 15,  5, 79],
       [69, 43, 85, 68, 53]])
>>> x[x>50]=0
>>> x[x<50]=1
>>> x
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]])

更新 如果有更多的条件会发生什么:

   >>> import numpy as np
    >>> x=np.random.randint(100, size=(10,5))
    >>> x
    array([[87, 99, 70, 32, 28],
           [38, 76, 89, 17, 34],
           [28,  1, 40, 34, 67],
           [45, 47, 69, 78, 89],
           [14, 81, 46, 71, 97],
           [39, 45, 36, 36, 25],
           [87, 28,  1, 46, 99],
           [27, 98, 37, 36, 84],
           [55,  2, 23, 29,  9],
           [34, 79, 49, 76, 48]])
    >>> for i in range(x.shape[0]):
        for j in range(x.shape[1]):
            if x[i,j]>90:
                x[i,j]=9
            elif x[i,j]>80:
                x[i,j]=8
            elif x[i,j]>70:
                x[i,j]=7
            elif x[i,j]>60:
                x[i,j]=6
            elif x[i,j]>50:
                x[i,j]=5
            elif x[i,j]>40:
                x[i,j]=4
            else:
                x[i,j]=0


    >>> x
    array([[8, 9, 6, 0, 0],
           [0, 7, 8, 0, 0],
           [0, 0, 0, 0, 6],
           [4, 4, 6, 7, 8],
           [0, 8, 4, 7, 9],
           [0, 4, 0, 0, 0],
           [8, 0, 0, 4, 9],
           [0, 9, 0, 0, 8],
           [5, 0, 0, 0, 0],
           [0, 7, 4, 7, 4]])

【问题讨论】:

【参考方案1】:

很抱歉迟到了,只是想分享解决问题的另一种方法。

单线解决方案

x = np.where(x&gt;=50, 50, 1) + np.where(x&gt;50, -50, 0)

理由

我们可以对以下两个 numpy.where-matrices 求和:

对于矩阵 A:如果 x[i,j] >= 50,则设置值为 50,否则设置为 1,因为我们希望 x[i,j] 对于矩阵 B:如果 x[i,j] > 50,则设置值 -50,因此对于 x[i,j]>50,两个矩阵的总和将为相应的元素生成值 0。

通过计算 A+B,为条件 x>50(即 -50)和 x>=50(即 50)设置的值会产生所需的值(0 和 50),并且不会干扰为 x

为 UPDATE 更新

x = np.where(x>40, 4, 0) + np.where(x>50, 1, 0) + np.where(x>60, 1, 0) + np.where(x>70, 1, 0) + np.where(x>80, 1, 0) + np.where(x>90, 1, 0)

或者更短,如果我们可以依赖值总是小于 100 的事实(如果你想要整数,请更改 dtype):

x = np.where(x>40, np.floor(x/10), 0)

对我来说,这段代码可读性很强,但我可能不具有代表性。

【讨论】:

【参考方案2】:

单线器可以完成循环所做的一切:

x[x != 50] = x[x != 50] < 50

编辑:

对于您的扩展问题,您需要以下内容:

bins = [40, 50, 60, 70, 80, 90, 100]
out = np.digitize(x, bins, right = 1)
out[out.astype(bool)] += 3

【讨论】:

添加了对“新”问题的回复 认为您需要在那里进行更正 - right=Truenp.digitize。还有out[out!=0] += 3 对,这就是我试图 FGITW 你得到的结果>.<.>【参考方案3】:
np.where(x < 50, 0, 1)

这应该足够了。您不需要为 50 保留掩码值,因为 50 既不小于也不大于 50。希望这会有所帮助。

更新

#update
np.where(x < 40, 0, x)
np.where(x > (x - (x % 10)), x // 10, x)

【讨论】:

【参考方案4】:

一个 IF-ELIF

方法 #1 一种方法 -

keep_mask = x==50
out = np.where(x>50,0,1)
out[keep_mask] = 50

方法 #2 或者,对于原位编辑 -

replace_mask = x!=50
x[replace_mask] = np.where(x>50,0,1)[replace_mask]
# Or (x<=50).astype(int) in place of np.where(x>50,0,1)

Code-golf?如果你真的想玩code-golf/one-liner -

(x<=50)+(x==50)*49

多个 IF-ELIF

方法#1

对于涉及更多 if-elif 部分的更通用的情况,我们可以使用np.searchsorted -

out_x = np.where(x<=40,0, np.searchsorted([40,50,60,70,80,90], x)+3)

【讨论】:

如果还有更多 if/elif 呢? @pdshah 将取决于如何进一步设置 if/elif。给我们看一个样例? 将其添加到问题中。 @pdshah 查看多个 IF-ELIF 部分。【参考方案5】:
np.where(x < 50, 0, 1)

这应该足够了。您不需要为 50 保留掩码值,因为 50 既不小于也不大于 50。希望这会有所帮助。

【讨论】:

以上是关于没有循环的 if/else 的 Numpy 等价物的主要内容,如果未能解决你的问题,请参考以下文章

Python中的if else 和while else的用法

循环运行时出现 Java 逻辑错误:If/else 计数器计数不正确

快速在所有行中应用函数时如何在pandas numpy中使用if else

python入门(输入输出if else 判断流while循环for循环)

if else的使用以及如何从键盘获取数值

IF....Else循环