Assigning Values to Specific Rows and Columns of Numpy Array at the Same Time
Question:
My Expected Output for Code below:
array([[ 0, 1, 2, 3],
[ 4, 999, 999, 7],
[ 8, 999, 999, 11],
[12, 13, 14, 15]])
Code:
import numpy as np
A = np.array(range(16)).reshape((4,4))
A[[1,3],:][:, [1,3]] = [[999,999],[999,999]]
print(A)
However, the values are unchanged. The output is
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
What should I do to get the desired output?
I am not allowed to use loops to assign each value manually.
Update:
Scenario 2: if I want A[[1,3], [1,3]] = inner, it throws an exception shape mismatch: value array of shape (2,2) could not be broadcast to indexing result of shape (2,). What should I do?
desired output for scenario 2:
array([[ 0, 1, 2, 3],
[ 4, 999, 6, 999],
[ 8, 9, 10, 11],
[12, 999, 14, 999]])
Answers:
Specific Solution
You can do the following to get the desired outcome:
import numpy as np
A = np.array(range(16)).reshape((4,4))
inner = np.array([[999,999],[999,999]])
border = 1
A[border:border + inner.shape[0],
border:border + inner.shape[1]] = inner
- The inner array is defined separately for clarity.
- The point of
border
is to define how far away from the border you are.
- The output of
A
is:
array([[ 0, 1, 2, 3],
[ 4, 999, 999, 7],
[ 8, 999, 999, 11],
[ 12, 13, 14, 15]])
Generalized 2-Dimensional Solution
In the example above, it happens to be case that the inner-array is the same distance in both directions of the outer-array but you can define and adjust two border
parameters to generalize the solution. For example, if the original array was larger (10×10) and you wanted to imbed a (3×3) array in side of it in an arbitrary location, you can do the following:
import numpy as np
A = np.array(range(100)).reshape((10,10))
inner = np.array([[999,999,999],[999,999,999],[999,999,999]])
border_1 = 1
border_2 = 4
A[border_1:border_1 + inner.shape[0],
border_2:border_2 + inner.shape[1]] = inner
The output of A is:
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 10, 11, 12, 13, 999, 999, 999, 17, 18, 19],
[ 20, 21, 22, 23, 999, 999, 999, 27, 28, 29],
[ 30, 31, 32, 33, 999, 999, 999, 37, 38, 39],
[ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
Extra Tip
Instead of manually writing the array of 999’s, you can take advantage of np.full
.
np.full((3, 3), 999)
produces…
array([[999, 999, 999],
[999, 999, 999],
[999, 999, 999]])
This could make writing the inner array much faster.
I’ll try to demonstrate how to index blocks of an array.
In [52]: arr = np.arange(16).reshape(4,4);arr
Out[52]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
Indexing with 2 lists (that are the same size), selects a "diagonal":
In [53]: arr[[1,3],[1,3]]
Out[53]: array([ 5, 15])
In effect that is selecting arr[1,1]
and arr[3,3]
, pairing up one values from each list. You can’t assign a (2,2) block to that (2,) shape.
You selected a block with a double indexing:
In [54]: arr[[1,3],:][:,[1,3]]
Out[54]:
array([[ 5, 7],
[13, 15]])
That works for fetching, but not for assignment. The first indexing returns a copy, so an further attempt to assign a value will modify that copy, not the original array.
In the comment I suggested indexing with two lists:
In [55]: arr[[[1],[3]],[1,3]]
Out[55]:
array([[ 5, 7],
[13, 15]])
More generally we use a helper function to convert two 1d lists into arrays that broadcast
against each other:
In [56]: np.ix_([1,3],[1,3])
Out[56]:
(array([[1],
[3]]),
array([[1, 3]]))
That tuple of arrays can select our block, and can be used for setting:
In [57]: arr[_]
Out[57]:
array([[ 5, 7],
[13, 15]])
You may want to review the concept of broadcasting
. It’s as important when indexing as it is when doing math. The same rules apply, for example to add the two arrays in [56]:
In [58]: Out[56][0]+Out[56][1]
Out[58]:
array([[2, 4],
[4, 6]])
For this pair of indexing lists, we could use slices to select the same block:
In [59]: arr[1::2, 1::2]
Out[59]:
array([[ 5, 7],
[13, 15]])
[59] is an example basic
indexing, while all the others are advanced
indexing. I’ve given an important link to the indexing documentation.
Finally to demonstrate the assignment:
In [60]: arr[[[1],[3]],[1,3]] = [[100,110],[120,130]]
In [61]: arr
Out[61]:
array([[ 0, 1, 2, 3],
[ 4, 100, 6, 110],
[ 8, 9, 10, 11],
[ 12, 120, 14, 130]])
In [62]: arr[1::2, 1::2] = -1
In [63]: arr
Out[63]:
array([[ 0, 1, 2, 3],
[ 4, -1, 6, -1],
[ 8, 9, 10, 11],
[12, -1, 14, -1]])
My Expected Output for Code below:
array([[ 0, 1, 2, 3],
[ 4, 999, 999, 7],
[ 8, 999, 999, 11],
[12, 13, 14, 15]])
Code:
import numpy as np
A = np.array(range(16)).reshape((4,4))
A[[1,3],:][:, [1,3]] = [[999,999],[999,999]]
print(A)
However, the values are unchanged. The output is
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
What should I do to get the desired output?
I am not allowed to use loops to assign each value manually.
Update:
Scenario 2: if I want A[[1,3], [1,3]] = inner, it throws an exception shape mismatch: value array of shape (2,2) could not be broadcast to indexing result of shape (2,). What should I do?
desired output for scenario 2:
array([[ 0, 1, 2, 3],
[ 4, 999, 6, 999],
[ 8, 9, 10, 11],
[12, 999, 14, 999]])
Specific Solution
You can do the following to get the desired outcome:
import numpy as np
A = np.array(range(16)).reshape((4,4))
inner = np.array([[999,999],[999,999]])
border = 1
A[border:border + inner.shape[0],
border:border + inner.shape[1]] = inner
- The inner array is defined separately for clarity.
- The point of
border
is to define how far away from the border you are. - The output of
A
is:
array([[ 0, 1, 2, 3],
[ 4, 999, 999, 7],
[ 8, 999, 999, 11],
[ 12, 13, 14, 15]])
Generalized 2-Dimensional Solution
In the example above, it happens to be case that the inner-array is the same distance in both directions of the outer-array but you can define and adjust two border
parameters to generalize the solution. For example, if the original array was larger (10×10) and you wanted to imbed a (3×3) array in side of it in an arbitrary location, you can do the following:
import numpy as np
A = np.array(range(100)).reshape((10,10))
inner = np.array([[999,999,999],[999,999,999],[999,999,999]])
border_1 = 1
border_2 = 4
A[border_1:border_1 + inner.shape[0],
border_2:border_2 + inner.shape[1]] = inner
The output of A is:
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[ 10, 11, 12, 13, 999, 999, 999, 17, 18, 19],
[ 20, 21, 22, 23, 999, 999, 999, 27, 28, 29],
[ 30, 31, 32, 33, 999, 999, 999, 37, 38, 39],
[ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
Extra Tip
Instead of manually writing the array of 999’s, you can take advantage of np.full
.
np.full((3, 3), 999)
produces…
array([[999, 999, 999],
[999, 999, 999],
[999, 999, 999]])
This could make writing the inner array much faster.
I’ll try to demonstrate how to index blocks of an array.
In [52]: arr = np.arange(16).reshape(4,4);arr
Out[52]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
Indexing with 2 lists (that are the same size), selects a "diagonal":
In [53]: arr[[1,3],[1,3]]
Out[53]: array([ 5, 15])
In effect that is selecting arr[1,1]
and arr[3,3]
, pairing up one values from each list. You can’t assign a (2,2) block to that (2,) shape.
You selected a block with a double indexing:
In [54]: arr[[1,3],:][:,[1,3]]
Out[54]:
array([[ 5, 7],
[13, 15]])
That works for fetching, but not for assignment. The first indexing returns a copy, so an further attempt to assign a value will modify that copy, not the original array.
In the comment I suggested indexing with two lists:
In [55]: arr[[[1],[3]],[1,3]]
Out[55]:
array([[ 5, 7],
[13, 15]])
More generally we use a helper function to convert two 1d lists into arrays that broadcast
against each other:
In [56]: np.ix_([1,3],[1,3])
Out[56]:
(array([[1],
[3]]),
array([[1, 3]]))
That tuple of arrays can select our block, and can be used for setting:
In [57]: arr[_]
Out[57]:
array([[ 5, 7],
[13, 15]])
You may want to review the concept of broadcasting
. It’s as important when indexing as it is when doing math. The same rules apply, for example to add the two arrays in [56]:
In [58]: Out[56][0]+Out[56][1]
Out[58]:
array([[2, 4],
[4, 6]])
For this pair of indexing lists, we could use slices to select the same block:
In [59]: arr[1::2, 1::2]
Out[59]:
array([[ 5, 7],
[13, 15]])
[59] is an example basic
indexing, while all the others are advanced
indexing. I’ve given an important link to the indexing documentation.
Finally to demonstrate the assignment:
In [60]: arr[[[1],[3]],[1,3]] = [[100,110],[120,130]]
In [61]: arr
Out[61]:
array([[ 0, 1, 2, 3],
[ 4, 100, 6, 110],
[ 8, 9, 10, 11],
[ 12, 120, 14, 130]])
In [62]: arr[1::2, 1::2] = -1
In [63]: arr
Out[63]:
array([[ 0, 1, 2, 3],
[ 4, -1, 6, -1],
[ 8, 9, 10, 11],
[12, -1, 14, -1]])