Issue
I am trying to create a function that imprints a smaller array onto another. The dimensions and center are arbitrary. For example I may want to put a 3x3 on a 5x5 at center (1, 2) or I may want to put a 5x5 on a 100x100 at center (50, 30). Ignoring indexing errors and even arrays that have no center.
Example:
arr1 =
[2, 3, 5]
[1, 5, 6]
[1, 0, 1]
arr2 =
[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]
arr3 = imprintarray(arr1, arr2, (1, 2))
arr3 =
[0, 2, 3, 5, 0]
[0, 1, 5, 6, 0]
[0, 1, 0, 1, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
I call the first array the smallarray (the one that is imprinted) and the second array to be the map array (the bigger array who has its values modified)
My solution was to create a 3rd array with the target indexes on the the maparray and to iterate through accessing the smallarrays values and changing the elements of the maparray directly.
import numpy as np
maparray = np.zeros((9, 9))
smallarray = np.zeros((3, 3))
smallarray[:] = 2
def createindexarray(dimensions, center):
array = []
adjustment = (dimensions[0] * -0.5) + 0.5
for row in range(dimensions[0]):
elements = []
for col in range(dimensions[1]):
elements.append((row + center[0] + int(adjustment), col + center[1] + int(adjustment)))
array.append(elements)
return array
indexarray = createindexarray((3, 3), (3, 5))
for w, x in enumerate(smallarray):
for y, z in enumerate(x):
maparray[indexarray[w][y][0]][indexarray[w][y][1]] = smallarray[w][y]
It does feel like there should be a better way or more efficient way. I looked through numpy's documentation to see if I could find something like this but I could not find it. Thoughts? Even if you think this is the best way any tips on improving my Python would be much appreciated.
Solution
Given:
import numpy as np
maparray = np.zeros((9, 9))
smallarray = np.ones((3, 3)) * 2
We can simply assign to a slice:
# to assign with the top-left at (1, 2)
maparray[1:4, 2:5] = smallarray
which we can generalize:
def imprint(big, small, position):
y, x = position
h, w = small.shape
big[y:y+h, x:x+w] = small
If we need to wrap around the outside, then we can take a different approach, still without needing any conditional logic. Simply roll the big
array such that the "imprinting" location is at the top-left; assign the values; then roll back. Thus:
def imprint(big, small, position):
y, x = position
h, w = small.shape
copy = np.roll(big, (-y, -x), (0, 1))
copy[:h, :w] = small
big[:, :] = np.roll(copy, (y, x), (0, 1))
Let's test it:
>>> imprint(maparray, smallarray, (7, 7))
>>> maparray
array([[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[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., 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., 0., 0., 0., 0.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.]])
>>> imprint(maparray, smallarray, (2, 2))
>>> maparray
array([[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.]])
We see that we can correctly leave the results from previous imprints in place, and also wrap around the edge.
It also works with negative indices and large indices, wrapping around in the expected manner:
>>> maparray[:] = 0 # continuing from before
>>> imprint(maparray, smallarray, (-1, -1))
>>> maparray
array([[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[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., 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., 0., 0., 0., 0.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.]])
>>> # Keep in mind the first coordinate is for the first axis, which
>>> # displays vertically here (hence labelling it `y` in the code).
>>> imprint(maparray, smallarray, (40, 0))
>>> maparray
array([[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 2., 2., 0., 0., 0., 0., 0., 0.],
[2., 2., 2., 0., 0., 0., 0., 0., 0.],
[2., 2., 2., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.]])
It will presumably be more efficient to use conditional logic explicitly (left as an exercise here), but this approach is elegant and easy to understand.
Answered By - Karl Knechtel
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.