使用索引替换二维numpy数组的部分内容

7 投票
2 回答
4419 浏览
提问于 2025-04-18 16:27

我正在尝试根据 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 个回答

0

在编程中,有时候我们会遇到一些问题,可能是因为代码写得不够清晰,或者是我们对某些概念理解得不够透彻。比如,有人可能在使用某个函数时,发现它的表现和预期不一样,这时候就需要仔细检查代码,看看是不是哪里出错了。

另外,编程的过程中,调试是一个很重要的环节。调试就是找出代码中的错误并修复它们。这个过程可能会让人感到沮丧,但只要耐心一点,通常都能找到问题所在。

总之,编程就像解谜一样,有时候需要反复尝试和思考,才能找到正确的解决方案。

>>> 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.]])
>>> 
4

你可以使用 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 进行赋值的原因。

撰写回答