使用numpy生成带状矩阵

时间:2018-09-23 07:26:10

标签: python numpy matrix

我正在使用以下代码从生成器g创建带状矩阵:

def banded(g, N):
    """Creates a `g` generated banded matrix with 'N' rows"""
    n=len(g)
    T = np.zeros((N,N+n-1))
    for x in range(N):
        T[x][x:x+n]=g
    return T

用法很简单:

banded([1,2,3], 3)

它返回

[1, 2, 3, 0, 0]
[0, 1, 2, 3, 0]
[0, 0, 1, 2, 3]

它通常用于求解具有给定模具的有限差分模型,例如(-1, 1)

是否有更好的方法来生成该模具?我为此找不到任何好的NumPy函数。

更好的是,我的意思是更快,使用更少的内存,从python中删除循环并发送到Numpy堆栈。任何(或全部)都是改进。

3 个答案:

答案 0 :(得分:2)

您可以使用scipy.sparse.diags

输入:

diags([1, 2, 3], [0, 1, 2], shape=(3,5)).toarray()

输出:

array([[ 1.,  2.,  3.,  0.,  0.],
      [ 0.,  1.,  2.,  3.,  0.],
      [ 0.,  0.,  1.,  2.,  3.]])

第二个列表[0,1,2]是偏移列表。它告诉您希望某个元素与对角线的偏移量。

答案 1 :(得分:2)

这里有一个np.lib.stride_tricks.as_strided,可以为我们提供一个2D视图,可以看到填充了1D版本的输入的零,并且这样的内存效率很高,因此性能也很高。这个技巧已经探索了无数次-12

因此,实现应为-

def sliding_windows(a, W):
    a = np.asarray(a)
    p = np.zeros(W-1,dtype=a.dtype)
    b = np.concatenate((p,a,p))
    s = b.strides[0]
    strided = np.lib.stride_tricks.as_strided
    return strided(b[W-1:], shape=(W,len(a)+W-1), strides=(-s,s))

样品运行-

In [99]: a = [1,2,3]

In [100]: sliding_windows(a, W=3)
Out[100]: 
array([[1, 2, 3, 0, 0],
       [0, 1, 2, 3, 0],
       [0, 0, 1, 2, 3]])

In [101]: a = [1,2,3,4,5]

In [102]: sliding_windows(a, W=3)
Out[102]: 
array([[1, 2, 3, 4, 5, 0, 0],
       [0, 1, 2, 3, 4, 5, 0],
       [0, 0, 1, 2, 3, 4, 5]])

采用相同的原理,但版本少一些混乱,我们还可以利用基于np.lib.stride_tricks.as_stridedscikit-image's view_as_windows来获取滑动窗口。 More info on use of as_strided based view_as_windows

from skimage.util.shape import view_as_windows

def sliding_windows_vw(a, W):
    a = np.asarray(a)
    p = np.zeros(W-1,dtype=a.dtype)
    b = np.concatenate((p,a,p))
    return view_as_windows(b,len(a)+W-1)[::-1]

答案 2 :(得分:0)

您还可以使用scipy的toeplitz函数,该函数与matlab的对应函数非常相似。形状也很聪明,不用担心。

import scipy.linalg as scl

# define first column and first line
column1 = [1,0,0]
line1 = [1,2,3,0,0]

scl.toeplitz(column1, line1)

如果要使用可变大小,请使用size参数(N)向列和行动态添加零。以下是我对最小为3的有限差分的实现。

col = [1,0,0] + [0] * (size -3)
lin = [1,2,1] + [0] * (size -3)
m = scl.toeplitz(col, lin)

输出:

array([[1., 2., 1., 0., 0.],
        [0., 1., 2., 1., 0.],
        [0., 0., 1., 2., 1.],
        [0., 0., 0., 1., 2.],
        [0., 0., 0., 0., 1.]])