0

I am trying to re-center a set of image-like data where I know the appropriate offset in the x- and y- dimensions. Similarly to this question I want to do something like a "roll" but instead of assuming periodic boundary conditions I want to fill in the "empty" positions in the array with zeros.

My question is more general than the one that I have linked because I would like to shift an array in arbitrary directions. In the accepted solution, one must always know what direction the image is being rolled, in order to cut off the appropriate edge. In particular, here is the accepted solution which involves shifting the array x by 1.

import numpy as np
x = np.array([[1, 2, 3],[4, 5, 6]])

print np.pad(x,((0,0),(1,0)), mode='constant')[:, :-1]

However to do the reverse operation, one would have to hard code the following change:

import numpy as np
x = np.array([[1, 2, 3],[4, 5, 6]])

print np.pad(x,((0,0),(0,1)), mode='constant')[:, 1:] 
// changed argument of pad
// and array indices

Is there a simple way to implement both at once?

0

1 Answer 1

1

The core of pad for your mode is:

newmat = narray.copy()

# API preserved, but completely new algorithm which pads by building the
# entire block to pad before/after `arr` with in one step, for each axis.
if mode == 'constant':
    for axis, ((pad_before, pad_after), (before_val, after_val)) \
            in enumerate(zip(pad_width, kwargs['constant_values'])):
        newmat = _prepend_const(newmat, pad_before, before_val, axis)
        newmat = _append_const(newmat, pad_after, after_val, axis)

So it's that pad_width tuple (of tuples) that determines the action, either prepending or appending the constant pad. Notice that it iterates on axes.

Your own code probably will be just as fast, since pad isn't doing anything magical (or compiled) here.

The prepend function does a concatenate like:

np.concatenate((np.zeros(padshape, dtype=arr.dtype), arr),
                          axis=axis)

See np.lib.arraypad.py for details.

So for each nonzero pad amount it concatenates on a zeros block of the desired shape.


In [280]: x = np.array([[1, 2, 3],[4, 5, 6]])

Your two pad versions:

In [281]: np.pad(x,((0,0),(1,0)), mode='constant')[:, :-1]
Out[281]: 
array([[0, 1, 2],
       [0, 4, 5]])
In [282]:  np.pad(x,((0,0),(0,1)), mode='constant')[:, 1:]
Out[282]: 
array([[2, 3, 0],
       [5, 6, 0]])

The direct insert equivalents:

In [283]: res = np.zeros_like(x)
In [284]: res[:,1:] = x[:,:-1]
In [285]: res
Out[285]: 
array([[0, 1, 2],
       [0, 4, 5]])
In [286]: res = np.zeros_like(x)
In [287]: res[:,:-1] = x[:,1:]
In [288]: res
Out[288]: 
array([[2, 3, 0],
       [5, 6, 0]])

You can do the same thing on the first axis. The general expression is

res = np.zeros_like(x)
idx1 = (slice(...), slice(...))
idx2 = (slice(...), slice(...))
res[idx1] = x[idx2]

where the idx tuples depend on the roll axis and direction.

e.g.

idx1 = (slice(None), slice(1,None))
idx2 = (slice(None), slice(None,-1))

With 2 axes and 2 directions, that's 4 pairs.

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.