Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fill larger matrix from smaller matrix

I have to fill a larger 2D array with blocks made from a smaller 2D array but still take into account a smaller amount of rows of columns than the width of the block at the edge of the new big 2D array. For example to create the array on the right from the array on the left using a 3x3 block.

[[ 1  2  3  4]         [[  1.   1.   1.   2.   2.   2.   3.   3.   3.   4.   4.]
 [ 5  6  7  8]    ->    [  1.   1.   1.   2.   2.   2.   3.   3.   3.   4.   4.]
 [ 9 10 11 12]]         [  1.   1.   1.   2.   2.   2.   3.   3.   3.   4.   4.]
                        [  5.   5.   5.   6.   6.   6.   7.   7.   7.   8.   8.]
                        [  5.   5.   5.   6.   6.   6.   7.   7.   7.   8.   8.]
                        [  5.   5.   5.   6.   6.   6.   7.   7.   7.   8.   8.]
                        [  9.   9.   9.  10.  10.  10.  11.  11.  11.  12.  12.]]

I implemented this as seen below but I'm looking for a smarter way of doing it.

# Smaller array
a=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])

# The size of the blocks for filling in the matrix
blockSize = 3

# The number of columns and rows of the new matrix (b)
cols = 11
rows = 7
b = np.zeros([rows, cols])

for i in range(0, rows, blockSize):
    ii = i/blockSize
    if i + blockSize < rows:
        numRows = blockSize
    else:
        numRows = rows - i
    for j in range(0, cols, blockSize):
        jj= j/blockSize
        if j + blockSize < cols:
            numCols = blockSize
        else:
            numCols = cols - j

        b[i:i+numRows,j:j+numCols] = a[ii,jj]
like image 789
Janusz Avatar asked Mar 15 '23 09:03

Janusz


2 Answers

You could repeat in two stages; once for the columns, once for the rows.

a = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
blockSize = 3
cols = 11
rows = 7

Each time, a list of the number of repetitions for each element is calculated from blockSize and either rows or cols (e.g. [3, 3, 3, 2] for the columns). This creates an array of the required size:

>>> result = a.repeat([blockSize]*(cols//blockSize) + [cols % blockSize], axis=1)
>>> result = result.repeat([blockSize]*(rows//blockSize) + [rows % blockSize], axis=0)
>>> result
array([[ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4],
       [ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4],
       [ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4],
       [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8],
       [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8],
       [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8],
       [ 9,  9,  9, 10, 10, 10, 11, 11, 11, 12, 12]])

An alternative would be to use something like np.kron to create blocks of each element and then slice the array to the required size. However, this creates an array first that may be much larger than needed (and may be inefficient with memory).

np.kron(a, np.ones((blockSize, blockSize)))[:rows, :cols]
like image 162
Alex Riley Avatar answered Mar 24 '23 03:03

Alex Riley


You can use np.repeat to repeat your matrix then use a simple slicing to remove the extra rows and np.delete to remove the column.

So as a more general way you can use following function :

>>> def array_crator(arr,new_shape):
...       repeat_dims=np.divide(new_shape,arr.shape)+1
...       return np.delete(a.repeat(repeat_dims[1],axis=1).repeat(repeat_dims[0],axis=0),np.s_[new_shape[1]:],1)[:new_shape[0]]

Demo:

>>> a=np.arange(1,13).reshape(3,4)
>>> array_crator(a,(7,11))
array([[ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4],
       [ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4],
       [ 1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4],
       [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8],
       [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8],
       [ 5,  5,  5,  6,  6,  6,  7,  7,  7,  8,  8],
       [ 9,  9,  9, 10, 10, 10, 11, 11, 11, 12, 12]])

Another example :

>>> a=np.arange(2,22).reshape(4,5)
>>> a
array([[ 2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16],
       [17, 18, 19, 20, 21]])
>>> array_crator(a,(7,11))
array([[ 2,  2,  2,  3,  3,  3,  4,  4,  4,  5,  5],
       [ 2,  2,  2,  3,  3,  3,  4,  4,  4,  5,  5],
       [ 7,  7,  7,  8,  8,  8,  9,  9,  9, 10, 10],
       [ 7,  7,  7,  8,  8,  8,  9,  9,  9, 10, 10],
       [12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15],
       [12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15],
       [17, 17, 17, 18, 18, 18, 19, 19, 19, 20, 20]])
>>> array_crator(a,(9,17))
array([[ 2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,  5,  6],
       [ 2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,  5,  6],
       [ 2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  5,  5,  5,  5,  6],
       [ 7,  7,  7,  7,  8,  8,  8,  8,  9,  9,  9,  9, 10, 10, 10, 10, 11],
       [ 7,  7,  7,  7,  8,  8,  8,  8,  9,  9,  9,  9, 10, 10, 10, 10, 11],
       [ 7,  7,  7,  7,  8,  8,  8,  8,  9,  9,  9,  9, 10, 10, 10, 10, 11],
       [12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16],
       [12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16],
       [12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16]])
like image 31
Mazdak Avatar answered Mar 24 '23 02:03

Mazdak