使用索引替换二维numpy数组的部分内容
我正在尝试根据 i 和 j 的值来替换一个名为 "S" 的二维 numpy 数组的一部分。给定的 S 数组如下:
>>> S
Out[1]:
array([[ 1., 0., 0.],
[ 0., 3., 0.],
[ 0., 0., 9.]]
当 i=0 和 j=1 时,我可以使用以下语法访问第 i 行和第 j 列的元素:
>>> S[:, [i, j]][[i, j], :]
Out[2]:
array([[ 1., 0.],
[ 0., 3.]])
现在,当我尝试用另一个相同大小的数组 (tmp_arr) 替换 S 数组中的相同元素时,Python 没有报错,但也没有任何变化,这意味着 S 中的元素保持不变,并且没有显示错误信息。
>>> tmp_arr
Out[3]:
array([[ 555., 0.],
[ 0., 555.]])
>>> S[:, [i, j]][[i, j], :] = tmp_arr
结果我得到的还是同样的矩阵:
>>> S
Out[4]:
array([[ 1., 0., 0.],
[ 0., 3., 0.],
[ 0., 0., 9.]])
显然,下面的方法可以工作,但我希望找到一个更优雅的解决方案:
S[i, i] = tmp_arr[0, 0]
S[i, j] = tmp_arr[0, 1]
S[j, i] = tmp_arr[1, 0]
S[j, j] = tmp_arr[1, 1]
感谢大家的评论和经验分享。
2 个回答
在编程中,有时候我们会遇到一些问题,可能是因为代码写得不够清晰,或者是我们对某些概念理解得不够透彻。比如,有人可能在使用某个函数时,发现它的表现和预期不一样,这时候就需要仔细检查代码,看看是不是哪里出错了。
另外,编程的过程中,调试是一个很重要的环节。调试就是找出代码中的错误并修复它们。这个过程可能会让人感到沮丧,但只要耐心一点,通常都能找到问题所在。
总之,编程就像解谜一样,有时候需要反复尝试和思考,才能找到正确的解决方案。
>>> a
array([[ 1., 0., 0.],
[ 0., 3., 0.],
[ 0., 0., 9.]])
>>> i, j = 0 , 1
>>> a[i:j+1,i:j+1] = np.arange(100, 104).reshape(2,2)
>>> a
array([[ 100., 101., 0.],
[ 102., 103., 0.],
[ 0., 0., 9.]])
>>>
你可以使用 np.ix_
来构建你想要的索引数组:
In [91]: S[np.ix_([i,j],[i,j])]
Out[91]:
array([[1, 0],
[0, 3]])
In [92]: tmp_arr = np.eye(2)*555
In [93]: tmp_arr
Out[93]:
array([[ 555., 0.],
[ 0., 555.]])
In [94]: S[np.ix_([i,j],[i,j])] = tmp_arr
In [95]: S
Out[95]:
array([[555, 0, 0],
[ 0, 555, 0],
[ 0, 0, 9]])
使用 np.ix_
对 S
进行赋值是个不错的选择,但要注意,还有更快的方法可以选择子数组:
In [99]: %timeit S.take([i, j], axis=1).take([i, j], axis=0)
100000 loops, best of 3: 3.32 µs per loop
In [97]: %timeit S[:, [i, j]][[i, j], :]
100000 loops, best of 3: 8.8 µs per loop
In [96]: %timeit S[np.ix_([i,j],[i,j])]
100000 loops, best of 3: 13 µs per loop
不过,与其他方法不同的是,S[np.ix_(...)] = ...
不使用 链式索引,所以会调用 S.__setitem__
,这会直接影响到 S
。而 S[:, [i, j]]
返回的是 子数组的副本,所以如果你对 S[:, [i, j]][[i, j], :]
进行赋值,只会影响这个副本,而不会改变 S
本身。由于没有保留对这个副本的引用,Python 在赋值后会把这个副本丢掉,所以赋值就会失效。这就是为什么链式索引不适合用来对 S
进行赋值的原因。