np.where() -- NumPy




np.where(condition, [x, y])

这里三个参数, 其中必写参数是 condition(判断条件), 后边的 x 和 y 是可选参数. 那么这三个参数都有怎样的要求呢?

  • condition:array_like,bool ,当为 True 时,产生 x,否则产生 y
    • 简单说, 对第一个参数的要求是这样的, 首先是数据类型的要求, 类似于数组或者布尔值, 当判断条件为真时返回 x 中的值, 否则返回 y 中的值
  • x,y:array_like,可选, 要从中选择的值。 x,y 和 condition 需要可广播到某种形状
    • x 和 y 是可选参数, 并且对这两个参数的数据类型要求只有类似数组这一条, 当条件判断为 true 或者 false 时从这两个类似数组的容器中取数.
  • 还有很重要的一点就是对三个参数的要求是都可以广播, 也就是说对参数的形状做出了要求, 对 condition 来说, 是布尔值的时候无所谓形状, 当时类似数组的容器时,与 x 和 y 的形状需要满足可广播的条件.



demo

情况一 : 三个参数完整

In [5]: np.where(
            [[True, False], [True, True]], 
            [[1,2],[3,4]], 
            [[9,8],[7,6]])
   ...:
Out[5]:
array([[1, 8],
       [3, 4]])


完全符合 np.where 的语法要求, 包含了 condition, x 和 y.

官方文档说, 对 condition 进行判断, 如果判断结果为 true 则取 x 中的值, 否则取 y 中的值. 可以看出 condition 是和 x 以及 y 形状相同的二维类数组形式, 根据条件进行判断:

  • 条件中第 0 个元素中的第 0 个元素是 true, 那么取 x 中的相对应元素 1;
  • 条件中第 0 个元素中的第 1 个元素是 false, 那么取 y 中的相对应元素 8;
  • 条件中第 1 个元素中的第 0 个元素是 ture, 那么取 x 中相对应的元素 3;
  • 条件中第 1 个元素中的第 1 个元素是 ture,那么取 x 中相对应的元素 4;


所以最后的结果中取出的元素是 1,8,3,4.

有些刚入门的小可爱可能会对前边条件中的 true 和 false 有疑问, 感觉没有进行什么判断吖, 其实这里是直接给了判断结果, 在我们判断两个值大小的时候最终的结果就是 true 或者 false 对不对.


情况二 : 只有 condition 参数


In [6]: np.where([[0,1],[1,0]])
Out[6]: (array([0, 1]), array([1, 0]))


需要注意的是, 返回的结果是符合条件的位置信息!


看起来这里的 condition 还不如上边例子的 condition 好理解, 这个 0 和 1 是怎么回事儿呢?

在 python 中,  除了 0, 空和 false, 其他值都是 true, 所以这里的 condition 实际上和这样写没什么两样 np.where([[False, True], [True, False]]). 上边例子中, 根据条件的判断结果选择从 x 或者 y 中取值, 现在没有 x 和 y 了,最终结果返回的就是判断结果为 true 的元素所在的位置信息.


来验证下, 结果中的两个数组分别反映的是判断结果为 true 的元素坐在的行信息和列信息, 也就是判断结果为真的元素所在的位置时第 0 个元素中的第 1 个位置和第 1 个元素中的第 0 个位置, 这个例子有点迷惑性, 不好理解, 我写了另一个更显的例子看一下

In [8]: np.where([[0,1,1],[1,0,1]])
Out[8]: (array([0, 0, 1, 1]), array([1, 2, 0, 2]))


这个例子中判断结果为 true 的元素有四个, 就是那四个 1, 所以返回的结果就是四个位置信息.


其他常用使用方式

上边的两个例子很适合讲解原理用,然而实际工作中还有其他常用的使用方式


新建一个数组:

x = np.arange(9.).reshape(3, 3)
x

返回结果:

array([[0., 1., 2.],
[3., 4., 5.],
[6., 7., 8.]])

筛选数组中符合条件的元素:

np.where( x > 5 )

返回结果:

(array([2, 2, 2], dtype=int64), array([0, 1, 2], dtype=int64))

我们通过前边的例子知道只有一个条件参数时, 返回的结果是判断结果为 true 的元素的位置信息, 那么值大于 5 的元素在原数组中的位置就是第 2 个元素中的第 0 个元素, 第 2 个元素中的第 1 个元素, 第 2 个元素中的第 2 个元素, 对应的是 6,7,8 这三个元素.


新的问题来了, 我不想要位置信息, 我想要元素值信息怎么办?

答案很简单, 切片出来啊, 比如我想要元素值大于 3 的所有元素:

x[np.where( x > 3.0 )]

返回结果:

array([4., 5., 6., 7., 8.])


可以看出, 切出的元素值是一维的数组, 如果我想要切出的结果保持原数组的形状该怎么办呢?

答案来了:

np.where(x < 5, x, -1)

返回结果:

array([[ 0., 1., 2.],
[ 3., 4., -1.],
[-1., -1., -1.]])

其实这里很简单, 就是把三个参数都写全了, 如果判断结果为真就从 x (也就是原数组) 中取数, 否则从 y 中取数,这里的 y 指定了一个数, 也就是所有判断结果为 false 的元素全部填充为同一个特定值, 保证了结果的形状和原数组形状一样



除了上面进行大小的判断, 也可以进行元素是否包含在指定容器中的判断

这是数组 x :

array([[0., 1., 2.],
[3., 4., 5.],
[6., 7., 8.]])

新定义的目标列表和判断:

goodvalues = [3, 4, 7]
ix = np.isin(x, goodvalues)
ix

返回结果:

array([[False, False, False],
[True, True, False],
[False, True, False]])

先通过 np.isin 来判断 x 中的元素是否在指定列表中, 然后再通过 np.where 查找符合条件的位置信息:

np.where(ix)

返回结果:

(array([1, 1, 2], dtype=int64), array([0, 1, 1], dtype=int64))

当然, 如果不想要位置信息, 需要元素值信息, 可以直接切片出来, 但是要搞清楚对谁进行切片哈, 这里是判断 x 中的哪些元素同时包含于 goodvalues 中, 所以是对 x 进行切片

x[np.where(ix)]

返回结果:

array([3., 4., 7.])



reference

https://zhuanlan.zhihu.com/p/83208224