Reputation: 1034
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
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
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
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