3

Is there a short, one-line way to create numpy array (which may have several dimensions) which has one in a certain position, and zeros in all the others? For the 1-D array, one can do the following to create an array with a 1 on k^{th} position:

np.eye(1, N, k = k)

How can this be generalized to the higher-dimensional case?

3
  • So if the array is not 1-d, where os the one and where the 0 ? Is the one unique at first position, of first position of every last-dimension array. Please give example Commented Jul 2, 2020 at 21:00
  • ...a "cute" way? Commented Jul 2, 2020 at 21:00
  • @azro something like this : np.array([[0, 0, 1], [0, 0, 0]]) - the only one nonzero element is located at [0, 2]. In general, if the array is N-dimensional, all posititons, except some [i_1, i_2, \ldots, i_n] are zero, and in this position there is 1. Commented Jul 2, 2020 at 21:24

3 Answers 3

4

For example if you need a 3x5 matrix with the 1 at index (2, 3), just make a 1D array, then reshape it:

M, N = 3, 5
i, j = 2, 3
np.eye(1, M * N, k=(i+1) * M + j).reshape(M, N)

array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0.]])

It may help to know that every multi-dimensional numpy array is internally represented as a 1D array, with some wrapper logic to handle strides and indexing. That means this solution here can also generalise to any dimension with the appropriate arithmetic. Here's a generalization:

def make_nd_array_with(dims, index):
    return (np.eye(1, 
                   np.prod(dims), 
                   k=(((np.array(index[:-1]) + 1) * dims[:-1]).sum() + index[-1]))
              .reshape(*dims))

make_nd_array_with((M,N), (i,j))

array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0.]])

Note that this addresses your constraint of wanting to do this in a single line, but the generally canonical solution is to create an array of zeros and set a single value, as the comments and other answer mentions.

arr = np.zeros(M, N)
arr[i, j] = 1
Sign up to request clarification or add additional context in comments.

1 Comment

Note that this assumes row major ordering for calculating indices.
3

Rather than using eye or reshape, it's much, much clearer to just write a function that calls zeros and sets the element you want:

def mostly_zeros(shape, nonzero_position, dtype=float, nonzero_element=1):
    retval = numpy.zeros(shape, dtype=dtype)
    retval[nonzero_index] = nonzero_element
    return retval

Then you can call mostly_zeros(shape=(4, 5), nonzero_position=(2, 2)) to get a mostly-zero array of shape (4, 5) with a 1.0 at position (2, 2). This will be a lot less of a maintenance headache than eye.


Alternatively, you could write a function that sets items and returns the array:

def chainable_setitem(obj, index, val):
    obj[index] = val
    return obj

Then you can do chainable_setitem(numpy.zeros((4, 5)), (2, 2), 1) to get a 4x5 array with a 1.0 at position 2, 2 and zeros elsewhere.

1 Comment

This is clearly the canonical answer, the op added a constraint to do this in a single line which my answer addresses. But this is the right answer.
1

Since you ask for a one-liner:

np.bincount([np.ravel_multi_index(pos,shp)],None,np.prod(shp)).reshape(shp)

Example

shp = 3,4
pos = 1,2
np.bincount([np.ravel_multi_index(pos,shp)],None,np.prod(shp)).reshape(shp)
# array([[0, 0, 0, 0],
#        [0, 0, 1, 0],
#        [0, 0, 0, 0]])

Admittedly this is much better for 1D where it simplifies to

np.bincount([pos],None,length)

than for nD

1 Comment

For 1D case looks better using np.eye: np.eye(<N>)[<pos>]

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.