Related
I have a 2D NumPy array filled with zeroes (placeholder values). I would like to add a 1D array filled with ones and zeroes to a part of it. eg.
2D array:
array([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]])
1D array:
array([1, 0, 1])
Desired end product: I want the array starting in position [2, 1]
array([[0, 0, 0, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 0, 0, 0]])
Or an insertion in any other position it could reasonably fit in. I have tried to do it with boolean masks but have not had any luck creating one in the correct shape. I have also tried flattening the 2D array, but couldn't figure out how to replace the values in the correct space.
You can indeed flatten the array and create a sequence of positions where you will insert your 1D array segment:
>>> pos = [1, 2]
>>> start = x.shape[1]*pos[0] + pos[1]
>>> seq = start + np.arange(len(segment))
>>> seq
array([7, 8, 9])
Then, you can either index the flattened array:
>>> x_f = x.flatten()
>>> x_f[seq] = segment
>>> x_f.reshape(x.shape)
array([[0, 0, 0, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 0, 0, 0]])
Alternatively, you can np.ravel_multi_index to get seq and apply np.unravel_index on it.
>>> seq = np.arange(len(segment)) + np.ravel_multi_index(pos, x.shape)
array([7, 8, 9])
>>> indices = np.unravel_index(seq, x.shape)
(array([1, 1, 1]), array([2, 3, 4]))
>>> x[indices] = segment
>>> x
array([[0, 0, 0, 0, 0],
[0, 0, 1, 0, 1],
[0, 0, 0, 0, 0]])
I have a numpy array A = np.array([[0, 1, 0, 0, 0],[0, 0, 1, 0, 0],[1, 0, 1, 1, 1]])
I want to slice B = np.array([[0, 1, 0],[0, 0, 1],[1, 0, 1]]) from A and get the rest of A as
a numpy array which is C = np.array([[0, 0], [0, 0],[1,1]])
I am looking for a general code as my matrix B changes at different iterations inside A.
Thanks
In numpy, it is actually quite easy, like what you can do for this problem is this:
import numpy as np
k = 3 #k is used to slice the array, here k = 3
A = np.array([[0, 1, 0, 0, 0],[0, 0, 1, 0, 0],[1, 0, 1, 1, 1]])
C = np.array([a[-i:] for i in range(k, len(a)])
A = np.array([a[i:i+k] for i in range(0, len(a), k)])
Hope this helps.
I am trying to modify the diagonal values of a 6 x 5 2D numpy array (It's an exercise in this scipy tutorial: http://scipy-lectures.org/intro/numpy/array_object.html#basic-visualization). I'm supposed to change the values of a diagonal from zeroes to 2,3,4,5,6. Since it's a 6 x 5 matrix, there's not really a "main" diagonal, and so I need to change the diagonal starting from the second row ([1][0]) to [5][4]. They suggest reading the docstring for diag. I did, and I still can't figure out how to do this. Any suggestions?
You can just slice an array, and fill_diagonal of that:
In [13]: import numpy as np
In [14]: a = np.zeros((6,5), int)
In [15]: np.fill_diagonal(a[1:], [2,3,4,5,6])
In [16]: a
Out[16]:
array([[0, 0, 0, 0, 0],
[2, 0, 0, 0, 0],
[0, 3, 0, 0, 0],
[0, 0, 4, 0, 0],
[0, 0, 0, 5, 0],
[0, 0, 0, 0, 6]])
I have a matrix M with values 0 through N within it. I'd like to unroll this matrix to create a new matrix A where each submatrix A[i, :, :] represents whether or not M == i.
The solution below uses a loop.
# Example Setup
import numpy as np
np.random.seed(0)
N = 5
M = np.random.randint(0, N, size=(5,5))
# Solution with Loop
A = np.zeros((N, M.shape[0], M.shape[1]))
for i in range(N):
A[i, :, :] = M == i
This yields:
M
array([[4, 0, 3, 3, 3],
[1, 3, 2, 4, 0],
[0, 4, 2, 1, 0],
[1, 1, 0, 1, 4],
[3, 0, 3, 0, 2]])
M.shape
# (5, 5)
A
array([[[0, 1, 0, 0, 0],
[0, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[0, 0, 1, 0, 0],
[0, 1, 0, 1, 0]],
...
[[1, 0, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 1, 0, 0, 0],
[0, 0, 0, 0, 1],
[0, 0, 0, 0, 0]]])
A.shape
# (5, 5, 5)
Is there a faster way, or a way to do it in a single numpy operation?
Broadcasted comparison is your friend:
B = (M[None, :] == np.arange(N)[:, None, None]).view(np.int8)
np.array_equal(A, B)
# True
The idea is to expand the dimensions in such a way that the comparison can be broadcasted in the manner desired.
As pointed out by #Alex Riley in the comments, you can use np.equal.outer to avoid having to do the indexing stuff yourself,
B = np.equal.outer(np.arange(N), M).view(np.int8)
np.array_equal(A, B)
# True
You can make use of some broadcasting here:
P = np.arange(N)
Y = np.broadcast_to(P[:, None], M.shape)
T = np.equal(M, Y[:, None]).astype(int)
Alternative using indices:
X, Y = np.indices(M.shape)
Z = np.equal(M, X[:, None]).astype(int)
You can index into the identity matrix like so
A = np.identity(N, int)[:, M]
or so
A = np.identity(N, int)[M.T].T
Or use the new (v1.15.0) put_along_axis
A = np.zeros((N,5,5), int)
np.put_along_axis(A, M[None], 1, 0)
Note if N is much larger than 5 then creating an NxN identity matrix may be considered wasteful. We can mitigate this using stride tricks:
def read_only_identity(N, dtype=float):
z = np.zeros(2*N-1, dtype)
s, = z.strides
z[N-1] = 1
return np.lib.stride_tricks.as_strided(z[N-1:], (N, N), (-s, s))
This question already has answers here:
Increment Numpy array with repeated indices
(3 answers)
Closed 4 years ago.
I want to modify an empty bitmap by given indicators (x and y axis).
For every coordinate given by the indicators the value should be raised by one.
So far so good everything seems to work. But if I have some similar indicators in my array of indicators it will only raise the value once.
>>> img
array([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]])
>>> inds
array([[0, 0],
[3, 4],
[3, 4]])
Operation:
>>> img[inds[:,1], inds[:,0]] += 1
Result:
>>> img
array([[1, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 1, 0]])
Expected result:
>>> img
array([[1, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 2, 0]])
Does someone have an idea how to solve this? Preferably a fast approach without the use of loops.
This is one way. Counting algorithm courtesy of #AlexRiley.
For performance implications of relative sizes of img and inds, see #PaulPanzer's answer.
# count occurrences of each row and return array
counts = (inds[:, None] == inds).all(axis=2).sum(axis=1)
# apply indices and counts
img[inds[:,1], inds[:,0]] += counts
print(img)
array([[1, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 2, 0]])
You could use numpy.add.at with a bit of manipulation to get the indices ready.
np.add.at(img, tuple(inds[:, [1, 0]].T), 1)
If you have larger inds arrays, this approach should remain fast... (though Paul Panzer's solution is faster)
Two remarks on the other two answers:
1) #jpp's can be improved by using np.unique with the axis and return_counts keywords.
2) If we translate to flat indexing we can use np.bincount which often (but not always, see last test case in benchmarks) is faster than np.add.at.
Thanks #miradulo for initial version of benchmarks.
import numpy as np
def jpp(img, inds):
counts = (inds[:, None] == inds).all(axis=2).sum(axis=1)
img[inds[:,1], inds[:,0]] += counts
def jpp_pp(img, inds):
unq, cnts = np.unique(inds, axis=0, return_counts=True)
img[unq[:,1], unq[:,0]] += cnts
def miradulo(img, inds):
np.add.at(img, tuple(inds[:, [1, 0]].T), 1)
def pp(img, inds):
imgf = img.ravel()
indsf = np.ravel_multi_index(inds.T[::-1], img.shape[::-1])
imgf += np.bincount(indsf, None, img.size)
inds = np.random.randint(0, 5, (3, 2))
big_inds = np.random.randint(0, 5, (10000, 2))
sml_inds = np.random.randint(0, 1000, (5, 2))
from timeit import timeit
for f in jpp, jpp_pp, miradulo, pp:
print(f.__name__)
for i, n, a in [(inds, 1000, 5), (big_inds, 10, 5), (sml_inds, 10, 1000)]:
img = np.zeros((a, a), int)
print(timeit("f(img, i)", globals=dict(img=img, i=i, f=f), number=n) * 1000 / n, 'ms')
Output:
jpp
0.011815106990979984 ms
2623.5026352020213 ms
0.04642329877242446 ms
jpp_pp
0.041291153989732265 ms
5.418520100647584 ms
0.05826510023325682 ms
miradulo
0.007099648006260395 ms
0.7788308983435854 ms
0.009103797492571175 ms
pp
0.0035401539935264736 ms
0.06540440081153065 ms
3.486583800986409 ms