spiridon_the_sun_rotator
spiridon_the_sun_rotator

Reputation: 1034

How do I create a numpy N-dimensional array of zeros, with only a single element equal to one?

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?

Upvotes: 3

Views: 4444

Answers (3)

Paul Panzer
Paul Panzer

Reputation: 53029

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

Upvotes: 1

cs95
cs95

Reputation: 402263

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

Upvotes: 4

user2357112
user2357112

Reputation: 280207

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.

Upvotes: 3

Related Questions