【问题标题】:Why replacing values in numpy array does not always work为什么替换 numpy 数组中的值并不总是有效
【发布时间】:2021-02-15 23:27:48
【问题描述】:

我正在尝试使用以下命令替换/覆盖数组中的值:

import numpy as np
test = np.array([[4,5,0],[0,0,0],[0,0,6]])
test
Out[20]:
array([[4., 5., 0.],
       [0., 0., 0.],
       [0., 0., 6.]])

test[np.where(test[...,0] != 0)][...,1:3] = np.array([[10,11]])
test
Out[22]:
array([[4., 5., 0.],
       [0., 0., 0.],
       [0., 0., 6.]])

但是,正如在 Out22 中看到的那样,数组 test 并没有被修改。所以我的结论是不可能简单地覆盖数组的一部分或几个单元格。

尽管如此,在其他情况下,可以覆盖数组的几个单元格。例如,在下面的代码中:

test = np.array([[1,2,0],[0,0,0],[0,0,3]])
test
Out[11]:
array([[1., 2., 0.],
       [0., 0., 0.],
       [0., 0., 3.]])

test[test>0]
Out[12]:
array([1., 2., 3.])

test[test>0] = np.array([4,5,6])
test
Out[14]:
array([[4., 5., 0.],
       [0., 0., 0.],
       [0., 0., 6.]])

因此,我的 2 个问题:

1- 为什么第一个命令

test[np.where(test[...,0] != 0)][...,1:3] = np.array([10,11])

不允许修改数组 test ?为什么它不允许访问数组单元并覆盖它们?

2- 考虑到我的代码需要使用上面的命令选择单元格,我该如何让它工作?

非常感谢!

【问题讨论】:

  • test[where...] 步骤会进行复制。以下[...]=... 修改了该副本,而不是原始文件。 view 传递更改,但不传递副本。

标签: python arrays numpy overwrite


【解决方案1】:

我会给你一个。这确实工作:

test[...,1:3][np.where(test[...,0] != 0)] = np.array([[10,11]])

array([[ 4, 10, 11],
       [ 0,  0,  0],
       [ 0,  0,  6]])

为什么?这是两个因素的组合 - numpy 索引和 .__setitem__ 调用。

python 解释器会向后读取行。当它到达= 时,它会尝试调用最左边的.__setitem____setitem__ (希望)是对象的一种方法,并且有两个输入,目标和索引(在它之前的 [...] 之间)。

a[b] = c  #is intepreted as
a.__setitem__(b, c)

现在,当我们在numpy 中建立索引时,我们可以通过三种基本方式来实现。

  • 切片(返回视图)
  • '高级索引'(返回副本)
  • '简单索引'(也返回副本)

“高级”和“简单”索引之间的一个主要区别是numpy array 的__setitem__ 函数可以解释高级索引。而views 表示数据地址是相同的,所以我们不需要__setitem__ 来获取它们。

所以:

test[np.where(test[...,0] != 0)][...,1:3] = np.array([[10,11]])  #is intepreted as

(test[np.where(test[...,0] != 0)]).__setitem__( slice([...,1:3]), 
                                                np.array([[10,11]]))

但是,由于np.where(test[...,0] != 0)advanced index(test[np.where(test[...,0] != 0)]) 返回一个副本,然后该副本丢失,因为它从未被分配。它确实需要我们想要的元素并将它们设置为[10,11],但结果会丢失在缓冲区中的某个地方。

如果我们这样做:

test[..., 1:3][np.where(test[..., 0] != 0)] = np.array([[10, 11]]) #is intepreted as

(test[..., 1:3]).__setitem__( np.where(test[...,0] != 0), np.array([[10,11]]) )

test[...,1:3] 是一个视图,所以它仍然指向同一个内存。现在setitemtest[...,1:3] 中查找与np.where(test[...,0] != 0) 对应的位置,并将它们设置为等于np.array([[10,11]])。一切正常。

您也可以这样做:

test[np.where(test[...,0] != 0), 1:3] = np.array([10, 11])

现在,由于所有索引都在一组括号中,它在这些索引上调用 test.__setitem__,这也正确设置了数据。

更简单(也是最像 Python 的)会是:

test[test[...,0] != 0, 1:3] = np.array([10,11])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-24
    • 2019-08-02
    • 2016-11-11
    • 2012-10-26
    相关资源
    最近更新 更多