1

I am trying to draw a "one" on a numpy array np.zeros((28, 28)) by assigning certain rows and columns to 255. I have written the following code:

one = np.zeros((28, 28))
one[12:15][5:23] = 255

The output I'm getting after this is a simple array of zeros with no changes. Can anyone please explain this strange behavior?

Bonus

If you interchange [12:15] and [5:23], rows 17 to 19 are filled with 255.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264

3 Answers3

5

With one[12:15][5:23] you first select rows from 12 to 15 (3 rows), then you select rows 5 to 23 from these 3 rows. These rows do not exists, then you do not update anything.

The syntax for updating rows 12 to 15 crossed with columns 5 to 23 is

one[12:15,5:23] = 255
jpl
  • 367
  • 2
  • 11
2

The notation you are using is valid but does something very different from what you expect. Numpy indices consist of tuples, one element per dimension. The index you are trying to get looks like this

one[(12:15, 5:23)] = 255

For convenience, python allows you to remove the parentheses, so almost everyone would write

one[12:15, 5:23] = 255

It's useful to remember that that's just shorthand for a tuple containing slice objects, because sometimes you have to build it externally.

Let's look at what your original index actually does. Each bracket expression in python is a call to __getitem__ or __setitem__. Your index can be rewritten as

view = one[12:15]  # __getitem__
view[5:23] = 255   # __setitem__

You are allowed to pass in a tuple that is shorter than the number of dimensions. In that case, the leading dimensions are indexed, and all the remaining dimensions are implicit grabbed in their entirety. What that means practically is that you are effectively doing

view = one[12:15, :]
view[5:23, :] = 255

view is a slice of rows 12-14, inclusive. It is a 3x28 array. In keeping with python convention, numpy allows you to assign to indices past the end of an array, silently doing nothing. view has only 3 rows, so assigning rows 5-22 does nothing.

Hopefully this makes the situation in your edit clear. If you grab rows 5-22, you get an 18x28 slice of ones, offset by 5 rows. This slice has rows 12-14, so you end up setting rows 12+5, 13+5 and 14+5 in the original array.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
0

To expand on jpl's answer: your syntax would be correct if you were dealing with python's list data structure. But you are dealing with numpy's ndarray data structure so it is not and jpl syntax is correct.

Paula Thomas
  • 1,152
  • 1
  • 8
  • 13
  • The syntax is perfectly valid. Why is it not working? – Mad Physicist Mar 29 '20 at 18:20
  • It is not working because it is operating on a numpy array and is therefore invalid, if it was operating on a python list it would be valid. – Paula Thomas Mar 29 '20 at 20:19
  • 1
    `x[slice1][slice2]` works with both lists and arrays. Python calculates `temp=x[slice1]`, and follows with `temp1 = temp[sllice2]`. A slice of a list is still a list; a slice of an array has the same number of dimensions. `x[i][j]` for scalar indices is different, with a reduction in nesting or dimensionality at each step. – hpaulj Mar 29 '20 at 21:12