Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make special diagonal matrix in Numpy

I am trying to make a numpy array that looks like this:

[a b c       ]
[  a b c     ]
[    a b c   ]
[      a b c ] 

So this involves updating the main diagonal and the two diagonals above it.

What would be an efficient way of doing this?

like image 735
Tom Bennett Avatar asked Aug 02 '13 21:08

Tom Bennett


3 Answers

You can use np.indices to get the indices of your array and then assign the values where you want.

a = np.zeros((5,10))
i,j = np.indices(a.shape)

i,j are the line and column indices, respectively.

a[i==j] = 1.
a[i==j-1] = 2.
a[i==j-2] = 3.

will result in:

array([[ 1.,  2.,  3.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  1.,  2.,  3.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  1.,  2.,  3.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  1.,  2.,  3.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  1.,  2.,  3.,  0.,  0.,  0.]])
like image 166
Saullo G. P. Castro Avatar answered Oct 22 '22 05:10

Saullo G. P. Castro


This is an example of a Toeplitz matrix - you can construct it using scipy.linalg.toeplitz:

import numpy as np
from scipy.linalg import toeplitz

first_row = np.array([1, 2, 3, 0, 0, 0])
first_col = np.array([1, 0, 0, 0])

print(toeplitz(first_col, first_row))
# [[1 2 3 0 0 0]
#  [0 1 2 3 0 0]
#  [0 0 1 2 3 0]
#  [0 0 0 1 2 3]]
like image 21
ali_m Avatar answered Oct 22 '22 03:10

ali_m


import numpy as np

def using_tile_and_stride():
    arr = np.tile(np.array([10,20,30,0,0,0], dtype='float'), (4,1))
    row_stride, col_stride = arr.strides
    arr.strides = row_stride-col_stride, col_stride
    return arr

In [108]: using_tile_and_stride()
Out[108]: 
array([[ 10.,  20.,  30.,   0.,   0.,   0.],
       [  0.,  10.,  20.,  30.,   0.,   0.],
       [  0.,   0.,  10.,  20.,  30.,   0.],
       [  0.,   0.,   0.,  10.,  20.,  30.]])

Other, slower alternatives include:

import numpy as np

import numpy.lib.stride_tricks as stride

def using_put():
    arr = np.zeros((4,6), dtype='float')
    a, b, c = 10, 20, 30
    nrows, ncols = arr.shape
    ind = (np.arange(3) + np.arange(0,(ncols+1)*nrows,ncols+1)[:,np.newaxis]).ravel()
    arr.put(ind, [a, b, c])
    return arr

def using_strides():
    return np.flipud(stride.as_strided(
        np.array([0, 0, 0, 10, 20, 30, 0, 0, 0], dtype='float'), 
        shape=(4, 6), strides = (8, 8)))

If you use using_tile_and_stride, note that the array is only appropriate for read-only purposes. Otherwise, if you were to try to modify the array, you might be surprised when multiple array locations change simultaneously:

In [32]: arr = using_tile_and_stride()

In [33]: arr[0, -1] = 100

In [34]: arr
Out[34]: 
array([[  10.,   20.,   30.,    0.,  100.],
       [ 100.,   10.,   20.,   30.,    0.],
       [   0.,    0.,   10.,   20.,   30.],
       [  30.,    0.,    0.,   10.,   20.]])

You could work around this by returning np.ascontiguousarray(arr) instead of just arr, but then using_tile_and_stride would be slower than using_put. So if you intend to modify the array, using_put would be a better choice.

like image 4
unutbu Avatar answered Oct 22 '22 04:10

unutbu