rh1990
rh1990

Reputation: 940

Copying a smaller square array (m,m) from a larger one (n,n) in Python

Suppose I have an array:

from numpy import *
x = range(16)
x = reshape(x,(4,4))

print x
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]]

And I want to obtain a copy of a smaller array that is always centred in the middle of x by defining the size of that matrix (not the row/col indices)

E.g. if I want a 2x2 array from this, it would return:

[[5  6]
 [9 10]]

For my purposes, my initial array is larger (4096x4096) and I want to take a copy of the middle square array of sizes (128x128), (256x256), (512x512), (1024x1024), (2048x2048).

I also want to keep the original array, so I don't necessarily want to just slice away the rows/cols from the original, instead I want a copy of the original only trimmed, saved to a new variable.

I was thinking of defining some variables such as (for the 2x2 case):

rows_to_keep = [1,2]
cols_to_keep = [1,2]

and then use

x[np.ix_(rows_to_keep, columns_to_keep)]

But this becomes impractical when my rows_to_keep is a list of up to 2048 numbers, e.g. to copy a 128x128 square from the 4096x4096 original I can create a list of indices that starts at row/col 1984 and goes to 2112:

size_to_keep = 128
indices = np.linspace(0, size_to_keep, size_to_keep, endpoint=False)

rows_to_keep = [(4096/2)-(size_to_keep/2) + i for i in indices]
cols_to_keep = [(4096/2)-(size_to_keep/2) + i for i in indices]

copy_array = x[np.ix_(rows_to_keep, columns_to_keep)]    

But again this becomes messy/impractical. I was hoping there's a more pythonic way of doing this? Thanks in advance

Upvotes: 1

Views: 120

Answers (2)

norok2
norok2

Reputation: 26896

Since all your indexes are continuous you could simply use appropriate slices. Obviously, you cannot avoid computing the extrema, but that's just about it.

This could take the form (including some code to make sure it works for any integer value of size):

def get_center(arr, size):
    mask = tuple(
        slice(int(dim / 2 - size / 2), int(dim / 2 + size / 2))
        if 0 < size < dim else slice(None)
        for dim in arr.shape)
    return arr[mask].copy()

which can simply be used like:

import numpy as np
dim = 4
x = np.arange(dim * dim).reshape((dim, dim))
y = get_center(x, 2)
# [[ 5,  6],
#  [ 9, 10]]

and works as expected, but without taking up that much memory.

Of course, you may want to adjust your extrema to handle the odd case the way you like (it is not really defined in your question).

Upvotes: 3

Robin
Robin

Reputation: 1599

Numpy works with views which means that when you do

extracted_middle_square = x[1:2,1:2]

you get a view of x (not a copy, and x is not modified). If you want to create a new variable, then do

extracted_middle_square = x[1:2,1:2].copy()

This should solve your problem.

Upvotes: 2

Related Questions