4

I have a 2-dimentional array, of which I need to extract sections (slices) into a new array:

original= numpy.ndarray( shape=(4,4) )
slices= numpy.ndarray( shape=(0,2) )
for x in range(3):
    slice= original[x:x+2,x:x+2] 
    slices=numpy.append(slices, slice,axis=0)

Is there a more efficient way to do this (getting rid of the python for cycle)?

----EDIT----

To clarify, I'm asking how to copy arbitrarily (but similarly) shaped 2D slices from arbitrary 2D indexes of an 2D array into another, vertically stacked - not particularly along the diagonal, or 2x2 sized.

2
  • Does the slice have to be square? Commented Oct 14, 2012 at 19:33
  • @Bitwise "arbitrarily (but similarly) shaped 2D slices", so, not necessarily square Commented Oct 20, 2012 at 17:09

2 Answers 2

3

There is a nifty trick with stride_tricks, you can find rolling window functions with different generality on SO and other (there currently is none in numpy itself), here is a version tailored to what you got:

def rolling_window(arr, window):
    """Very basic multi dimensional rolling window. window should be the shape of
    of the desired subarrays. Window is either a scalar or a tuple of same size
    as `arr.shape`.
    """
    shape = np.array(arr.shape*2)
    strides = np.array(arr.strides*2)
    window = np.asarray(window)
    shape[arr.ndim:] = window # new dimensions size
    shape[:arr.ndim] -= window - 1
    if np.any(shape < 1):
        raise ValueError('window size is too large')
    return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)

# Now:
view = rolling_window(arr, 2)
view[0,0] # first slice in your loop

Note that view holds the same data as the original array! Which can result in unexpected results. But you seem to want only the diagonal, you could do that with stride tricks as well to make sure you do not copy data if you want (next versions will create a view with diagonal, old ones always a copy):

diagonal = np.diagonal(view, axis1=0, axis2=1)
# unfortunatly now the first slice is diagonal[...,0], so just roll it to the start:
diagonal = np.rollaxis(diagonal, -1)

Now diagonal is the array you created in your for loop (on newer versions add a .copy() if you do not want a view).

Edit: Since the slices array is 2D and not 3D because you append, a reshape was missing here:

slices = diagonal.reshape(-1,2)

This might not be faster if you have such small arrays, but its constant (expect for the copying of the data in diagonal call) with the array size.

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

4 Comments

This simplifies the indexing, but how would one use it to achieve the final array ("slices" in the question) without using a for with numpy.append?
@goncalopp sorry I missed that while appending you are removing one dimension. This is only a reshape away though. Added it, though its a bit annoying maybe. I am not sure what you exactly want, but view can help you to get to the slices, from there it depends I guess...
nice! is there a way to generalize this to arbitrary indexes (not necessarily in the diagonal)? (see question edit)
@goncalopp I guess just any (fancy) indexing on thew view array or reshaped view array...
1

Here is your solution:

v = np.arange(0,original.shape[0],0.5).astype(int)
result = np.c_[ original[v[1:-1],v[:-2]] , original[v[1:-1],v[2:]] ]

works for any size of the square input matrix ("original" as you called it).

The idea is to create a "helper array" v, which is simply [0,0,1,1,2,2,3,3,...], and then use the observation that the indices you need are always simple slices of v.

Enjoy!

3 Comments

I'm still trying to understand that zigzagging on the second line... is there a way to do this on arbitrary slice sizes (not necessarily 2x2)? and for arbitrary slice positions, not necessarily along the diagonal.
@goncalopp arbitrary square slice size should be a straightforward extension. Arbitrary positions might be trickier - the bottom line is that you can use indexing to get slices, you just need to list the right order. In any case, your question was very specific and it wasn't clear which variables you want to generalize, so if you want something specific please put it in the question.
If I understood your method correctly, extracting n*n sized slices would require n arguments for np.c_? I edited the question for clarification

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.