为什么替换numpy数组中的值并不总是有效

问题描述

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

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]是一个视图,因此它仍指向相同的内存。现在setitemtest[...,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])