Scale/resize a square matrix into a larger size whilst retaining the grid structure/pattern (Python)

Viewed 120
arr = [[1 0 0]    # 3x3
       [0 1 0]
       [0 0 1]]

largeArr = [[1 1 0 0 0 0]   # 6x6
            [1 1 0 0 0 0]
            [0 0 1 1 0 0]
            [0 0 1 1 0 0]
            [0 0 0 0 1 1]
            [0 0 0 0 1 1]]

Like above, I want to retain the same 'grid' format whilst increasing the dimensions of the 2D array. How would I go about doing this? I assume the original matrix can only be scaled up by an integer n.

4 Answers

You could use scipy.ndimage.zoom

In [3]: arr = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])

In [4]: ndimage.zoom(arr, 2, order=0, grid_mode=True, mode="nearest")
Out[4]: 
array([[1, 1, 0, 0, 0, 0],
       [1, 1, 0, 0, 0, 0],
       [0, 0, 1, 1, 0, 0],
       [0, 0, 1, 1, 0, 0],
       [0, 0, 0, 0, 1, 1],
       [0, 0, 0, 0, 1, 1]])

You can use repeat() twice:

arr.repeat(2, 0).repeat(2, 1)

This outputs:

[[1. 1. 0. 0. 0. 0.]
 [1. 1. 0. 0. 0. 0.]
 [0. 0. 1. 1. 0. 0.]
 [0. 0. 1. 1. 0. 0.]
 [0. 0. 0. 0. 1. 1.]
 [0. 0. 0. 0. 1. 1.]]

You can use numba if performance is of importance (similar post) with no python jitting and in parallel mode if needed (this code can be written faster by some optimizations):

@nb.njit      # @nb.njit("int64[:, ::1](int64[:, ::1], int64)", parallel =True)
def numba_(arr, n):
    res = np.empty((arr.shape[0] * n, arr.shape[0] * n), dtype=np.int64)
    for i in range(arr.shape[0]):     # for i in nb.prange(arr.shape[0])
        for j in range(arr.shape[0]):
            res[n * i: n * (i + 1), n * j: n * (j + 1)] = arr[i, j]
    return res

So, as an example:

arr = [[0 0 0 1 1]
       [0 1 1 1 1]
       [1 1 0 0 1]
       [0 0 1 0 1]
       [0 1 1 0 1]]

res (n=3):
[[0 0 0 0 0 0 0 0 0 1 1 1 1 1 1]
 [0 0 0 0 0 0 0 0 0 1 1 1 1 1 1]
 [0 0 0 0 0 0 0 0 0 1 1 1 1 1 1]
 [0 0 0 1 1 1 1 1 1 1 1 1 1 1 1]
 [0 0 0 1 1 1 1 1 1 1 1 1 1 1 1]
 [0 0 0 1 1 1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 0 0 0 0 0 0 1 1 1]
 [1 1 1 1 1 1 0 0 0 0 0 0 1 1 1]
 [1 1 1 1 1 1 0 0 0 0 0 0 1 1 1]
 [0 0 0 0 0 0 1 1 1 0 0 0 1 1 1]
 [0 0 0 0 0 0 1 1 1 0 0 0 1 1 1]
 [0 0 0 0 0 0 1 1 1 0 0 0 1 1 1]
 [0 0 0 1 1 1 1 1 1 0 0 0 1 1 1]
 [0 0 0 1 1 1 1 1 1 0 0 0 1 1 1]
 [0 0 0 1 1 1 1 1 1 0 0 0 1 1 1]]

Performances (perfplot)

In my benchmarks, numba will be the fastest (for large n, parallel mode will be better), after that BrokenBenchmark answer will be faster than scipy.ndimage.zoom. In the benchmarks, f is arr.shape[0] and n is the repeating count:

enter image description here

This can be done using Pillow (fork of PIL) as follows:

from PIL import Image
import numpy as np
n = 3 # repeatation
im = Image.fromarray(arr)
up_im = im.resize((im.width*n, im.height*n),resample=Image.NEAREST)
up_arr = np.array(up_im)

Example:

arr = np.array(
  [[0, 0, 0, 1, 1],
   [0, 1, 1, 1, 1],
   [1, 1, 0, 0, 1],
   [0, 0, 1, 0, 1],
   [0, 1, 1, 0, 1]])

res (n=3):
np.array(
  [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1],
   [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
   [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
   [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
   [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
   [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
   [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
   [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
   [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
   [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
   [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
   [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
   [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1]])

numba vs PIL all

numba vs PIL with f=70

numba is by far the most performant in terms of speed. As the matrix size increases, PIL takes much more time.

Related