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]])
Asked By: bkehufi3evsd

||

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.

Answered By: Eric Peña

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]])
Answered By: hpaulj