【问题标题】:Using numpy.where() to iterate through a matrix使用 numpy.where() 遍历矩阵
【发布时间】:2016-03-17 16:38:05
【问题描述】:

numpy.where()有些东西我不明白:

假设我有一个 2D numpy ndarray:

import numpy as np
twodim =  np.array([[1, 2, 3, 4],  [1, 6, 7, 8], [1, 1, 1, 12],  [17, 3, 15, 16], [17, 3, 18, 18]])

现在,想创建一个函数来“检查”这个 numpy 数组的各种条件。

array([[ 1,  2,  3,  4],
       [ 1,  6,  7,  8],
       [ 1,  1,  1, 12],
       [17,  3, 15, 16],
       [17,  3, 18, 18]])

例如,该数组中的哪些条目 (A) 偶数 (B) 大于 7 (C) 可被 3 整除?

我想为此使用numpy.where(),并遍历该数组的每个条目,最终找到符合所有条件的元素(如果存在这样的条目):

   even_entries = np.where(twodim % 2 == 0)
   greater_seven = np.where(twodim > 7 )
   divisible_three = np.where(twodim % 3 == 0)

如何做到这一点?我不确定如何遍历布尔值...

我可以通过

访问矩阵 (i,j) 的索引
np.argwhere(even_entries)

我们可以做类似的事情

import numpy as np
twodim =  np.array([[1, 2, 3, 4],  [1, 6, 7, 8], [1, 1, 1, 12],  [17, 3, 15, 16], [17, 3, 18, 18]])
even_entries = np.where(twodim % 2 == 0)
greater_seven = np.where(twodim > 7 )
divisible_three = np.where(twodim % 3 == 0)
for row in even_entries:
    for item in row:
        if item: #equivalent to `if item == True`
                for row in greater_seven:
                    for item in row:
                        if item: #equivalent to `if item == True`
                            for row in divisible_three:
                                for item in row:
                                    if item: #equivalent to `if item == True`
                                        # something like print(np.argwhere())

有什么建议吗?

EDIT1:下面的好主意。正如@hpaulj 提到的“您的测试会产生一个与 twodim 形状相同的布尔矩阵” 这是我在玩弄时遇到的一个问题——并非所有条件都会产生与我的起始矩阵相同形状的矩阵。例如,假设我正在比较数组元素的左侧或右侧(即水平方向)是否有匹配的数组

twodim[:, :-1] == twodim[:, 1:]

这导致一个 (5,3) 布尔数组,而我们的原始矩阵是一个 (5,4) 数组

array([[False, False, False],
       [False, False, False],
       [ True,  True, False],
       [False, False, False],
       [False, False,  True]], dtype=bool)

如果我们在垂直方向上做同样的事情,结果是一个 (4,4) 布尔数组,而原始矩阵是 (5,4)

twodim[:-1] == twodim[1:]

array([[ True, False, False, False],
       [ True, False, False, False],
       [False, False, False, False],
       [ True,  True, False, False]], dtype=bool) 

如果我们想知道哪些条目有个垂直和水平对,那么弄清楚我们所处的维度并非易事。

【问题讨论】:

  • 不要使用where。我不知道为什么新的 NumPy 用户会继续使用它,但这并不是一个好主意。通过直接使用布尔掩码,您可以更轻松地完成这项工作。

标签: python numpy iteration where


【解决方案1】:

如果你想找到所有三个条件都满足的地方:

import numpy as np
twodim =  np.array([[1, 2, 3, 4],  [1, 6, 7, 8], [1, 1, 1, 12],  [17, 3, 15, 16], [17, 3, 18, 18]])

mask = (twodim % 2 == 0) & (twodim > 7) & (twodim % 3 =0)

print(twodim[mask])

[12 18 18]

不确定行中的所有元素是否必须满足条件并找到这些行,或者您是否想要单个元素。

【讨论】:

  • 我很乐意这样做。不幸的是,正如我在上面编辑的那样,在某些条件下很难创建这样的掩码。维度未正确广播在一起。
  • @ShanZhengYang:然后把重叠的部分一起切到&,或者在创建蒙版的过程中进行切片(所以twodim[appropriate:piece] > 7)。尝试处理 where 输出会更慢更尴尬。
【解决方案2】:
import numpy as np
twodim =  np.array([[1, 2, 3, 4],  [1, 6, 7, 8], [1, 1, 1, 12],  [17, 3, 15, 16], [17, 3, 18, 18]])
condition = (twodim % 2. == 0.) & (twodim > 7.) & (twodim % 3. ==0.)
location = np.argwhere(condition == True) 


for i in location: 
     print i, twodim[i[0],i[1]],

>>> [2 3] 12 [4 2] 18 [4 3] 18

【讨论】:

    【解决方案3】:

    您的测试会生成一个与twodim 形状相同的布尔矩阵:

    In [487]: mask3 = twodim%3==0
    In [488]: mask3
    Out[488]: 
    array([[False, False,  True, False],
           [False,  True, False, False],
           [False, False, False,  True],
           [False,  True,  True, False],
           [False,  True,  True,  True]], dtype=bool)
    

    正如其他答案所指出的,您可以在逻辑上组合测试 - 与和或。

    np.wherenp.nonzero 相同(在此使用中),只是返回 True 值的坐标 - 作为 2 个数组的元组。

    In [489]: np.nonzero(mask3)
    Out[489]: 
    (array([0, 1, 2, 3, 3, 4, 4, 4], dtype=int32),
     array([2, 1, 3, 1, 2, 1, 2, 3], dtype=int32))
    

    argwhere 返回相同的值,但作为转置的二维数组。

    In [490]: np.argwhere(mask3)
    Out[490]: 
    array([[0, 2],
           [1, 1],
           [2, 3],
           [3, 1],
           [3, 2],
           [4, 1],
           [4, 2],
           [4, 3]], dtype=int32)
    

    masktuple 都可用于直接索引您的数组:

    In [494]: twodim[mask3]
    Out[494]: array([ 3,  6, 12,  3, 15,  3, 18, 18])
    In [495]: twodim[np.nonzero(mask3)]
    Out[495]: array([ 3,  6, 12,  3, 15,  3, 18, 18])
    

    argwhere 不能直接用于索引,但可能更适合迭代,特别是如果您需要索引和值:

    In [496]: for i,j in np.argwhere(mask3):
       .....:     print(i,j,twodim[i,j])
       .....:     
    0 2 3
    1 1 6
    2 3 12
    3 1 3
    3 2 15
    4 1 3
    4 2 18
    4 3 18
    

    where 相同的东西需要zip

    for i,j in zip(*np.nonzero(mask3)): print(i,j,twodim[i,j])
    

    但通常在numpy 中,我们尽量避免迭代。如果你可以直接使用twodim[mask],你的代码会快很多。

    布尔掩码的逻辑组合比where 索引的组合更容易生成。要使用索引,我可能会求助于set 操作(联合、相交、差异)。


    对于缩减大小的测试,您必须决定如何将其映射到原始数组(和其他测试)。例如

    A (5,3) 掩码(列之间的差异):

    In [505]: dmask=np.diff(twodim, 1).astype(bool)
    In [506]: dmask
    Out[506]: 
    array([[ True,  True,  True],
           [ True,  True,  True],
           [False, False,  True],
           [ True,  True,  True],
           [ True,  True, False]], dtype=bool)
    

    它可以索引原始数组的3列

    In [507]: twodim[:,:-1][dmask]
    Out[507]: array([ 1,  2,  3,  1,  6,  7,  1, 17,  3, 15, 17,  3])
    In [508]: twodim[:,1:][dmask]
    Out[508]: array([ 2,  3,  4,  6,  7,  8, 12,  3, 15, 16,  3, 18])
    

    也可以和另外一个掩码的3列组合:

    In [509]: dmask & mask3[:,:-1]
    Out[509]: 
    array([[False, False,  True],
           [False,  True, False],
           [False, False, False],
           [False,  True,  True],
           [False,  True, False]], dtype=bool)
    

    以布尔数组形式组合测试仍然比where 索引更容易。

    【讨论】:

    • @ hpaulj "你的测试产生了一个与 twodim 形状相同的布尔矩阵" 这是我在玩弄时遇到的问题 --- 并非所有条件都会产生与我的形状相同的矩阵起始矩阵。见上方编辑中的 cmets
    • 我添加了你的例子。
    • 谢谢。上面还有一些我没有关注的东西。我们的原始矩阵是 (5,4),dmask 是 (5,3)。我们组合的最终蒙版不应该是形状(5,4)吗?行之间的区别是什么,即形状(4,4)。所以,要知道哪些条目有横向合作伙伴和纵向合作伙伴。什么是“最终面具”?
    • 现在想想,你的方法是对的。最终矩阵“掩码”的形状应为 (4,3)。我仍然有点困惑在这个设置中这些对的位置......
    • argwhere 对来自缩小尺寸的掩码。将它们映射到原始数组可能需要使用索引调整,例如 twodim[i, j+1]
    猜你喜欢
    • 2016-09-27
    • 2016-02-12
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 2018-04-09
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    相关资源
    最近更新 更多