1

Suppose I have a 2D array with shape (3, 3), call it a, and an array of zeros with shape (7, 7, 5, 5), call it b. I want to modify b in the following way:

for p in range(5):
    for q in range(5):
        b[p:p + 3, q:q + 3, p, q] = a

Given:

a = np.array([[4, 2, 2],
              [9, 0, 5],
              [9, 9, 4]])
b = np.zeros((7, 7, 5, 5), dtype=int)

b would end up something like:

>>> b[:, :, 0, 0]
array([[4, 2, 2, 0, 0, 0, 0],
       [9, 0, 5, 0, 0, 0, 0],
       [9, 9, 4, 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]])
>>> b[:, :, 0, 1]
array([[0, 4, 2, 2, 0, 0, 0],
       [0, 9, 0, 5, 0, 0, 0],
       [0, 9, 9, 4, 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]])
12
  • What have you actually tried? Commented Jul 24, 2021 at 4:54
  • I can't think of anything to do it with in numpy. np.lib.stride_tricks.sliding_window_view just returns the window, so I am not sure how to approach this. Commented Jul 24, 2021 at 5:03
  • Show a for-loop that actually works as a reference. Right now, your description is pretty ambiguous. Commented Jul 24, 2021 at 5:07
  • How about now ? Commented Jul 24, 2021 at 14:09
  • Have you run that code? Add The code to make a sample a and b of ones and zeros like you specified, and run it. I can help you improve the implementation of a well defined problem, but I can't tell you what your code should do. Commented Jul 24, 2021 at 14:11

1 Answer 1

1

One way to think about this to make a sliding window view of b (6D), slice out the parts you want (3D or 4D), and assign a to them.

However, there is a simpler way to do this altogether. The way a sliding window view works is by creating a dimension that steps along less than the full size of the dimension you are viewing. For example:

>>> x = np.array([1, 2, 3, 4])
array([1, 2, 3, 4])
>>> window = np.lib.stride_tricks.as_strided(
                 x, shape=(x.shape[0] - 2, 3),
                    strides=x.strides * 2)
[[1 2 3]
 [2 3 4]]

I'm deliberately using np.lib.stride_tricks.as_strided rather than np.lib.stride_tricks.sliding_window_view here because it has a certain flexibility that you need.

You can have a stride that is larger than the axis you are viewing, as long as you are careful. Contiguous arrays are more forgiving in this case, but by no means a requirement. An example of this is np.diag. You can implement it something like this:

>>> x = np.arange(12).reshape(3, 4)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> diag = np.lib.stride_tricks.as_strided(
               x, shape=(min(x.shape),),
                  strides=(sum(x.strides),))
array([ 0,  5, 10])

The trick is to make a view of only the parts of b you care about in a way that makes the assignment easy. Because of broadcasting rules, you will want the last two dimensions of the view to be a.shape, and the strides to be b.strides[:2], since that's where you want to place a.

The first two dimensions of the view will be responsible for making the copies of a. You want 25 copies, so the shape will be (5, 5). The strides are the trickier part. Let's take a look at a 2D case, just because that's easier to visualize, and then attempt to generalize:

>>> a0 = np.array([1, 2])
>>> b0 = np.zeros((4, 3), dtype=int)
>>> b0[0:2, 0] = b0[1:3, 1] = b0[2:4, 2] = a0

The goal is to make a view that strides along the diagonal of b0 in the first axis. So:

>>> np.lib.stride_tricks.as_strided(
        b0, shape=(b0.shape[0] - a0.shape[0] + 1, a0.shape[0]),
            strides=(sum(b0.strides), b0.strides[0]))[:] = a0
>>> b0
array([[1, 0, 0],
       [2, 1, 0],
       [0, 2, 1],
       [0, 0, 2]])

So that's what you do for b, but adding up every second dimension:

a = np.array([[4, 2, 2],
              [9, 0, 5],
              [9, 9, 4]])
b = np.zeros((7, 7, 5, 5), dtype=int)
vshape = (*np.subtract(b.shape[:a.ndim], a.shape) + 1,
          *a.shape)
vstrides = (*np.add(b.strides[:a.ndim], b.strides[a.ndim:]),
            *b.strides[:a.ndim])
np.lib.stride_tricks.as_strided(b, shape=vshape, strides=vstrides)[:] = a

TL;DR

def emplace_window(a, b):
    vshape = (*np.subtract(b.shape[:a.ndim], a.shape) + 1, *a.shape)
    vstrides = (*np.add(b.strides[:a.ndim], b.strides[a.ndim:]), *b.strides[:a.ndim])
    np.lib.stride_tricks.as_strided(b, shape=vshape, strides=vstrides)[:] = a

I've phrased it this way, because now you can apply it to any number of dimensions. The only expectations is that 2 * a.ndim == b.ndim and that b.shape[a.ndim:] == b.shape[:a.ndim] - a.shape + 1.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.