0

Say when we have a randomly generated 2D 3x2 Numpy array a = np.array(3,2) and I want to change the value of the element on the first row & column (i.e. a[0,0]) to 10. If I do

a[0][0] = 10

then it works and a[0,0] is changed to 10. But if I do

a[np.arange(1)][0] = 10

then nothing is changed. Why is this?

I want to change some columns values of a selected list of rows (that is indicated by a Numpy array) to some other values (like a[row_indices][:,0] = 10) but it doesn't work as I'm passing in an array (or list) that indicates rows.

Jack Shi
  • 131
  • 5

2 Answers2

4

a[x][y] is wrong. It happens to work in the first case, a[0][0] = 10 because a[0] returns a view, hence doing resul[y] = whatever modifies the original array. However, in the second case, a[np.arange(1)][0] = 10, a[np.arange(1)] returns a copy (because you are using array indexing).

You should be using a[0, 0] = 10 or a[np.arange(1), 0] = 10

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • Thanks! Follow-up: do you know the reason for a[np.arange(1)] returning a copy instead of a view and a[np.arange(1), 0] returns the view instead? – Jack Shi Sep 01 '22 at 17:14
  • 1
    @JackShi because that is how numpy is designed, [see here for the relevant documentation](https://numpy.org/doc/stable/user/basics.indexing.html). Essentially, creating a view is easy in the former case, and cases involving slicing, because you just manipulate the strides and dimensions. See [this question](https://stackoverflow.com/questions/53097952/how-to-understand-numpy-strides-for-layman) for some information on how `numpy.ndarray` objects are actually represented in memory – juanpa.arrivillaga Sep 01 '22 at 17:17
  • Are you sure `a[np.arange(1), 0]` is a view? – hpaulj Sep 01 '22 at 18:57
  • @hpaulj no, it is not, I did not mean to imply that it is. But *assignment* to `a[x, y, ...] = whatever` will modify the array in-place. – juanpa.arrivillaga Sep 01 '22 at 19:50
  • @JackShi `a[np.arange(1), 0]` of `a[np.arange(1), 0] = 10` does not return anything, because you use assignment instead of getting values here. If you want to get value, you will also get a copy. – Mechanic Pig Sep 02 '22 at 06:19
1

Advanced indexing always returns a copy as a view cannot be guaranteed.

Advanced indexing always returns a copy of the data (contrast with basic slicing that returns a view).

If you replace np.arange(1) with something that returns a view (or equivalent slicing) then you get back to basic indexing, and hence when you chain two views, the change is reflected into the original array.

For example:

import numpy as np


arr = np.arange(2 * 3).reshape((2, 3))
arr[0][0] = 10
print(arr)
# [[10  1  2]
#  [ 3  4  5]]

arr = np.arange(2 * 3).reshape((2, 3))
arr[:1][0] = 10
print(arr)
# [[10 10 10]
#  [ 3  4  5]]

arr = np.arange(2 * 3).reshape((2, 3))
arr[0][:1] = 10
print(arr)
# [[10  1  2]
#  [ 3  4  5]]

etc.


If you have some row indices you want to use, to modify the array you can just use them, but you cannot chain the indexing, e.g:

arr = np.arange(5 * 3).reshape((5, 3))
row_indices = (0, 2)
arr[row_indices, 0] = 10
print(arr)
# [[10  1  2]
#  [ 3  4  5]
#  [10  7  8]
#  [ 9 10 11]
#  [12 13 14]]
norok2
  • 25,683
  • 4
  • 73
  • 99