问题描述
我正在尝试使用以下命令替换/覆盖数组中的值:
import numpy as np
test = np.array([[4,5,0],[0,6]])
test
Out[20]:
array([[4.,5.,0.],[0.,0.,6.]])
test[np.where(test[...,0] != 0)][...,1:3] = np.array([[10,11]])
test
Out[22]:
array([[4.,6.]])
但是,正如在Out22中看到的那样,数组测试尚未修改。因此,我得出结论,不可能仅覆盖一部分数组或仅覆盖几个单元格。
但是,在其他情况下,有可能覆盖数组的几个单元格。例如,在下面的代码中:
test = np.array([[1,2,3]])
test
Out[11]:
array([[1.,2.,3.]])
test[test>0]
Out[12]:
array([1.,3.])
test[test>0] = np.array([4,6])
test
Out[14]:
array([[4.,6.]])
因此,我有2个问题:
1-为什么要第一个命令
test[np.where(test[...,1:3] = np.array([10,11])
不允许修改数组测试吗?为什么不允许访问数组单元并覆盖它们?
2-考虑到我的代码我需要使用上面的命令选择单元格,我该如何使其工作?
非常感谢!
解决方法
我帮你一个。这确实有效:
test[...,1:3][np.where(test[...,0] != 0)] = np.array([[10,11]])
array([[ 4,10,11],[ 0,0],6]])
为什么?它是两个因素的组合-numpy
索引编制和.__setitem__
调用。
python
解释器会向后读取行。当到达=
时,它将尝试在最左侧的位置调用.__setitem__
。 __setitem__
是对象的一种方法(希望如此),并具有两个输入,即目标和索引(无论在[...]
之前是什么)。
a[b] = c #is intepreted as
a.__setitem__(b,c)
现在,当我们在numpy
中建立索引时,我们可以通过三种基本方法来做到这一点。
- 切片(返回视图)
- “高级索引”(返回副本)
- “简单索引”(还返回副本)
“高级”索引和“简单”索引之间的主要区别是numpy
数组的__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[...,11]]) #is intepreted as
(test[...,1:3]).__setitem__( np.where(test[...,0] != 0),11]]) )
test[...,1:3]
是一个视图,因此它仍指向相同的内存。现在setitem
在test[...,1:3]
中寻找与np.where(test[...,0] != 0)
相对应的位置,并将其设置为等于np.array([[10,11]])
。一切正常。
您也可以这样做:
test[np.where(test[...,1:3] = np.array([10,11])
现在,由于所有索引都放在一组括号中,因此它在这些索引上调用test.__setitem__
,这也可以正确设置数据。
更简单(也是大多数pythonic)将是:
test[test[...,0] != 0,11])