7

I am trying to replace part of a 2D numpy array named "S" as a function of i and j. Given S as:

>>> S
Out[1]: 
array([[ 1.,  0.,  0.],
      [ 0.,  3.,  0.],
      [ 0.,  0.,  9.]]

for i= 0 and j= 1, I can access elements row i and j and column i and j using the following syntax:

>>> S[:, [i, j]][[i, j], :]
Out[2]: 
array([[ 1.,  0.],
      [ 0.,  3.]])

Now when I try to replace the same elements of array S with another array of same dimensions (tmp_arr) python does not give an error but it also does not do anything meaning that the elements of S remain unchanged and no error message is displayed.

>>> tmp_arr
Out[3]: 
array([[ 555.,  0.],
       [ 0.,  555.]])

>>> S[:, [i, j]][[i, j], :] = tmp_arr

and what I get is the same matrix:

>>> S
Out[4]: 
array([[ 1.,  0.,  0.],
      [ 0.,  3.,  0.],
      [ 0.,  0.,  9.]])

Obviously the following would work but I am looking for an elegant solution:

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]

I appreciate your comments and experiences.

2 Answers 2

4

You could use np.ix_ to construct the desired index arrays:

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

Using np.ix_ is good for making assignments to S, but note there are faster ways to select the subarray:

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

But unlike these other methods, S[np.ix_(...)] = ... does not use chained indexing, so S.__setitem__ gets called and the assignment affects S. In contrast, S[:, [i, j]] returns a copy of a subarray of S, so assigning to S[:, [i, j]][[i, j], :] affects only this copy of the subarray, not S itself. Since no reference to this copy of the subarray is maintained, Python throws away the copy after the assignment is made, so the assignment is lost. That is why chained indexing is no good for assigning to S.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you @unutbu for the answer. This definitely makes sense.
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.]])
>>> 

3 Comments

To make sure he only grabs the 4 items he is after, you would have to do a[i:j+1:j-i, i:j+1:j-i], wouldn't you? And of course make sure that j > i.
Please note that i ,j can be non-sequential. For instance i=0 and j=2. In other words if I were to replace the S[0,0], S[0,2], S[2,0] and S[2,2] then I don't think that the above solution would work. Please correct me if I am wrong.
@Arian, you are correct - my solution would replace a contiguous range. Not my first mis-understanding unfortunately.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.