Tevfik Xung
Tevfik Xung

Reputation: 978

Python : How to dynamically combine any number of small matrices into one big matrix

I have these 4 matrices and I want to dynamically combine them into one big matrix by passing n: number of small matrix and output matrix row and column example:

[[[ 1  2]
  [ 3  4]]

 [[ 5  6]
  [ 7  8]]

 [[ 9 10]
  [11 12]]

 [[13 14]
  [15 16]]]

the output matrix:

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

I can do it manually using:

M = np.bmat( [[x1], [x2], [x3], [x4]] )

Upvotes: 0

Views: 2372

Answers (4)

hpaulj
hpaulj

Reputation: 231385

[edit - I'm assuming that the small arrays are created independently, though my example is based on splitting a (4,2,2) array. If they really are just planes of a 3d array, then some combination of 'reshape' and 'transpose' will work better. But even such a solution will produce a copy because the original values are rearranged.]

Lets make a list of 2x2 arrays (here from a 3d array). Squeeze is needed because this split produces (1,2,2) arrays:

    n = len(A)
    E = np.zeros((n,n))
In [330]: X=np.arange(1,17).reshape(4,2,2)

In [331]: xl=[np.squeeze(i) for i in np.split(X,4,0)]

In [332]: xl
Out[332]: 
[array([[1, 2],
        [3, 4]]), array([[5, 6],
        [7, 8]]), array([[ 9, 10],
        [11, 12]]), array([[13, 14],
        [15, 16]])]

Your bmat approach - corrected to produce the square arrangment

In [333]: np.bmat([[xl[0],xl[1]],[xl[2],xl[3]]])
Out[333]: 
matrix([[ 1,  2,  5,  6],
        [ 3,  4,  7,  8],
        [ 9, 10, 13, 14],
        [11, 12, 15, 16]])

A concatenation approach:

In [334]: np.vstack([np.hstack(xl[:2]),np.hstack(xl[2:])])
Out[334]: 
array([[ 1,  2,  5,  6],
       [ 3,  4,  7,  8],
       [ 9, 10, 13, 14],
       [11, 12, 15, 16]])

Since slicing works in hstack I could also use it in the bmat:

In [335]: np.bmat([xl[:2],xl[2:]])
Out[335]: 
matrix([[ 1,  2,  5,  6],
        [ 3,  4,  7,  8],
        [ 9, 10, 13, 14],
        [11, 12, 15, 16]])

Internally bmat (check its code) is using a version of the vstack of hstacks (contactenates on first and last axes). Effectively

In [366]: ll=[xl[:2], xl[2:]]

In [367]: np.vstack([np.hstack(row) for row in ll])
Out[367]: 
array([[ 1,  2,  5,  6],
       [ 3,  4,  7,  8],
       [ 9, 10, 13, 14],
       [11, 12, 15, 16]])

Some how you have to specify the arrangement of these n arrays. np.bmat(xl) produces a (2,8) matrix (so does hstack). np.vstack(xl) produces a (8,2) array.

It shouldn't be hard to extend this to work with a 3x3, 2x3, etc layout of subarrays. xl is a list of subarrays. Rework it into the desired list of lists of subarrays and apply bmat or the combination of stacks.


2 quick versions of 2x3 layout (a 4d xl array is easier to construct than a 2x3 nested list, but functionally will be the same:

In [369]: xl=np.arange(3*2*2*2).reshape((3,2,2,2))

In [370]: np.vstack([np.hstack(row) for row in xl])
Out[370]: 
array([[ 0,  1,  4,  5],
       [ 2,  3,  6,  7],
       [ 8,  9, 12, 13],
       [10, 11, 14, 15],
       [16, 17, 20, 21],
       [18, 19, 22, 23]])

In [371]: xl=np.arange(2*3*2*2).reshape((2,3,2,2))

In [372]: np.vstack([np.hstack(row) for row in xl])
Out[372]: 
array([[ 0,  1,  4,  5,  8,  9],
       [ 2,  3,  6,  7, 10, 11],
       [12, 13, 16, 17, 20, 21],
       [14, 15, 18, 19, 22, 23]])

Upvotes: 1

B. M.
B. M.

Reputation: 18628

You can combine transposition and reshape operations:

In [1878]: x=arange(24).reshape(4,3,2)

In [1879]: (_,n,m)=x.shape

In [1880]: x.reshape(2,2,n,m).transpose(0,2,1,3).reshape(2*n,2*m)
Out[1880]: 
array([[ 0,  1,  6,  7],
       [ 2,  3,  8,  9],
       [ 4,  5, 10, 11],
       [12, 13, 18, 19],
       [14, 15, 20, 21],
       [16, 17, 22, 23]])

Upvotes: 1

yourstruly
yourstruly

Reputation: 1002

I think (but dont know if its right), that its best to work inplace and avoid to create new objects with new methods each time - specifically when You are doing it in loop multiple times. These examples are only for 2d matrices. But it could be easilly implemented to more dimensions. Best would be to have one big array, if its really big, prolly in numpy.memmap array. Then work on its parts. Fastest indexing (second to pointers) would be on cython memoryviews...

import numpy as np

def combine_matrix(*args):  
    n=len(args)
    rows,cols=args[0].shape
    a=np.zeros((n,cols*rows))
    m=0
    for i in range(n/rows):
        for j in range(n/cols):
            a[i*rows:(i+1)*rows,j*cols:(j+1)*cols]=args[m]
            m+=1

    return a

def example1():
    print '#'*10
    a=np.arange(1,17)

    n=4
    rows,cols=n/2,n/2

    lst=[]
    for i in range(n):
        ai=a[i*n:(i+1)*n]
        ai.shape=rows,cols
        lst.append(ai)

    print lst
    print combine_matrix(*lst)

def example2():
    print '#'*10
    m=24
    a=np.arange(m)

    n=6
    rows,cols=m/n/2,n/2

    lst=[]
    for i in range(m/n):
        ai=a[i*n:(i+1)*n]
        ai.shape=rows,cols
        lst.append(ai)

    print lst
    print combine_matrix(*lst)

def example3():
    print '#'*10
    m,n=36,6
    a=np.arange(m)

    arrs=np.array_split(a,n)
    for i in range(n):
        ln=arrs[i].shape[0]
        arrs[i].shape=2,ln/2

    print combine_matrix(*arrs) 

example1()
example2()
example3()

Upvotes: 1

yourstruly
yourstruly

Reputation: 1002

2 minutes implementation (for question before edition, maybe usefull for someone):

import numpy as np

a=np.ones((10,10))
b=a*3
c=a*1
d=a*1.5

def combine_matrix(*args):  
    n=len(args)
    rows,cols=args[0].shape
    a=np.zeros((n,rows,cols))
    for i in range(n):
        a[i]=args[i]
    return a

print combine_matrix(a,b,c,d)

If sizes of arrays are huge there is place for improvement...

Upvotes: 1

Related Questions