Issue
I am iterating and modifying 2×2 blocks on a 2d grid. What is a numpy/scipy way to do this more efficiently than with basic python cycling?
transitions = {b'\x00\x00\x00\x00': np.array([True, True, True, True]), …}
for y, x in itertools.product(range(yshift, ny, 2), range(xshift, nx, 2)):
grid[y:y+2, x:x+2].flat = transitions[grid[y:y+2, x:x+2].tobytes()]
Solution
One example, starting from a lookup table using directly the shape of the subarrays you want to change
lut=np.zeros((2,2,2,2,2,2)) # 4 1st "2" are the booleans 0/1 values for the 4 2x2 values. And the two last 2,2 are the 2x2 result to replace them with
lut[0,0,0,0]=[[1,1],[1,1]] # Translated from your sole example
lut[0,0,0,1]=[[1,1],[1,0]]
lut[0,0,1,0]=[[1,1],[0,1]]
lut[1,1,0,0]=[[1,1],[1,1]] # Just some random examples. Rest can stay 0
A=np.random.randint(0,2,(10,10)) # just 10x10 0/1 values
# Here starts the trick
Av=np.lib.stride_tricks.as_strided(A, shape=(5,5,2,2), strides=(A.strides[0]*2, A.strides[1]*2, A.strides[0], A.strides[1]))
# Av is another view of A, seen as a 5x5 array of 2x2 arrays
# Could have done that by "reshape". Just, want to be sure it is a view
# (so no copy of data. And what ever I do to Av, is done on A)
Av[:] = lut[Av[...,0,0], Av[...,0,1],Av[...,1,0], Av[...,1,1]]
Example Result
#Before
[[0 1 0 1 1 1 1 1 1 0]
[0 1 1 1 1 1 1 1 0 0]
[0 0 1 1 0 1 0 1 1 0]
[0 0 0 1 0 1 1 0 1 1]
[0 1 1 1 0 1 1 1 0 0]
[0 0 0 0 0 1 1 0 0 0]
[1 0 1 1 1 0 1 0 0 0]
[0 1 1 0 1 0 0 0 1 0]
[1 1 0 1 0 1 0 1 1 0]
[0 1 1 1 1 1 1 1 1 1]]
# After
[[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]
[1 1 0 0 0 0 0 0 0 0]
[1 1 0 0 0 0 0 0 0 0]
[0 0 1 1 0 0 0 0 1 1]
[0 0 1 1 0 0 0 0 1 1]
[0 0 0 0 0 0 0 0 1 1]
[0 0 0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0 0]]
Of course, this can be done only a part of A
Av[2:4,2:4] = lut[Av[2:4,2:4,0,0], Av[2:4,2:4,0,1], Av[2:4,2:4,1,0], Av[2:4,2:4,1,1]]
To avoid having to repeat the four Av
in the fancy indexing, we can recycle a bit your intial idea (not faster. And need to make asumptions on dtype, which I don't. Plus, I am pretty sure that some numpy fancy indexing gurus could find a way to do it in pure numpy indexing), we can translate the 4 1/0 into a 0-15 index
lut2=np.zeros((16,2,2))
lut2[0]=[[1,1],[1,1]] # Translated from your sole example
lut2[1]=[[1,1],[1,0]]
lut2[2]=[[1,1],[0,1]]
lut2[12]=[[1,1],[1,1]] # Just some random examples. Rest can stay 0
Av=np.lib.stride_tricks.as_strided(A, shape=(5,5,2,2), strides=(A.strides[0]*2, A.strides[1]*2, A.strides[0], A.strides[1]))
# 0 to 15 index - it is tempting to try to do it with to_bytes or the like. But the blocks are not contiguous, even
# with a strides, data.
idx = (Av*[[8,4],[2,1]]).sum(axis=(2,3))
Av[:] = lut2[idx]
Answered By - chrslg
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.