Python 从对角线开始将向量写入矩阵

Python 从对角线开始将向量写入矩阵,python,numpy,Python,Numpy,我有一个向量长度n和一个mxm矩阵。通常m>>n(m比n大得多)。我需要重复地把向量写入矩阵,从对角线开始。例如: 向量v=[v_1,v_2,v_3]带有4x4零矩阵的结果为: v_1, v_2, v_3, 0 0, v_1, v_2, v_3 0, 0, v_1, v_2 0, 0, 0, v_1 因为我必须经常这样做,所以速度必须相当快。现在我正在用原始python循环遍历矩阵的每一行,并将向量写入所需的位置,但这很慢。检查。这对你有用吗 v

我有一个向量长度
n
和一个
mxm
矩阵。通常
m>>n
m
n
大得多)。我需要重复地把向量写入矩阵,从对角线开始。例如:

向量
v=[v_1,v_2,v_3]
带有
4x4
零矩阵的结果为:

v_1,  v_2,  v_3,  0
0,    v_1,  v_2,  v_3
0,    0,    v_1,  v_2
0,    0,    0,    v_1
因为我必须经常这样做,所以速度必须相当快。现在我正在用原始python循环遍历矩阵的每一行,并将向量写入所需的位置,但这很慢。

检查。这对你有用吗

v = [1,2,3]
N = 5
M = 10
arr = np.sum(np.eye(N, k=i, M=10) * j for i, j in enumerate(v))
arr
>>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.]])
编辑(感谢hpaulj的建议):如果您的矩阵非常大且有很多0,则可以使用稀疏矩阵

from scipy.sparse import diags
arr = diags(v,offsets=[0,1,2],shape=(N,M))
print(arr.A)
>>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.]])

解决这一问题的一个办法是在每一侧垫上适当数量的零,并沿其获得长度
m
的滑动窗口。我们可以利用基于的来获得滑动窗口

输出将只是将窗口视图滑动到输入的零填充版本中。因此,如果您需要输出具有自己的内存空间,请在输出中附加
.copy()

样本运行-

In [45]: a
Out[45]: array([5, 8, 6])

In [46]: extend2D(a, m=4)
Out[46]: 
array([[5, 8, 6, 0],
       [0, 5, 8, 6],
       [0, 0, 5, 8],
       [0, 0, 0, 5]])

In [47]: extend2D(a, m=5)
Out[47]: 
array([[5, 8, 6, 0, 0],
       [0, 5, 8, 6, 0],
       [0, 0, 5, 8, 6],
       [0, 0, 0, 5, 8],
       [0, 0, 0, 0, 5]])
优化-I

如果您想通过使用
np.lib.stride\u技巧坚持NumPy来使用
stried索引
弄脏您的手,那么在这个过程中,请避免在前面方法的最后一步翻页-

def extend2D_v2(a, m):
    p1 = np.zeros(m-1,dtype=a.dtype)
    p2 = np.zeros(m-len(a),dtype=a.dtype)    
    b = np.concatenate((p1,a,p2))
    s = b.strides[0]
    return np.lib.stride_tricks.as_strided(b[m-1:],shape=(m,m),strides=(-s,s))
优化II

进一步优化,我们可以初始化一个零数组,然后将输入分配给它-

def extend2D_v3(a, m):
    b = np.zeros(2*m-1,dtype=a.dtype)
    b[m-1:m-1+len(a)] = a
    s = b.strides[0]
    return np.lib.stride_tricks.as_strided(b[m-1:],shape=(m,m),strides=(-s,s))
使用
n=100
m=10000
随机数据数组的计时-

In [97]: np.random.seed(0)
    ...: a = np.random.randint(1,9,(100))

In [98]: %timeit extend2D(a, m=10000)
    ...: %timeit extend2D_v2(a, m=10000)
    ...: %timeit extend2D_v3(a, m=10000)
10000 loops, best of 3: 51.3 µs per loop
10000 loops, best of 3: 19.6 µs per loop
100000 loops, best of 3: 12.6 µs per loop

这里有一个类似的答案,但仅限于NumPy。它用零填充给定向量,然后从该缓冲区生成视图:

import numpy as np

def view_into_diagonals(v, m):
    # Add zeros before and after the vector
    v_pad = np.pad(v, [(m - 1, m - len(v))], mode='constant')
    # Current stride
    s, = v_pad.strides
    # Offset from which the first row starts
    offset = s * (m - 1)
    # Make ndarray
    view = np.ndarray(shape=(m, m),
                      dtype=v_pad.dtype,
                      buffer=v_pad.data,
                      offset=offset,
                      # Each column moves one forward, each row moves one backwards
                      strides=(-s, s))
    # Probably better not write to it
    view.flags.writeable = False
    return view

print(view_into_diagonals([1, 2, 3], 6))
# [[1 2 3 0 0 0]
#  [0 1 2 3 0 0]
#  [0 0 1 2 3 0]
#  [0 0 0 1 2 3]
#  [0 0 0 0 1 2]
#  [0 0 0 0 0 1]]

您也可以使用
np.pad
而不是串联或零+赋值。@根据我的经验,
np.pad
似乎较慢。既然你提到了
scipy.sparse
,那就试试
sparse.diags([1,2,3],offset=[0,1,2],shape=(4,4),dtype=int)。对于我的用例来说,numpy.eye工作得足够快了!谢谢发布的解决方案对你有用吗?我明天会试试,然后回来!抱歉,我被另一个项目分散了注意力。。。
import numpy as np

def view_into_diagonals(v, m):
    # Add zeros before and after the vector
    v_pad = np.pad(v, [(m - 1, m - len(v))], mode='constant')
    # Current stride
    s, = v_pad.strides
    # Offset from which the first row starts
    offset = s * (m - 1)
    # Make ndarray
    view = np.ndarray(shape=(m, m),
                      dtype=v_pad.dtype,
                      buffer=v_pad.data,
                      offset=offset,
                      # Each column moves one forward, each row moves one backwards
                      strides=(-s, s))
    # Probably better not write to it
    view.flags.writeable = False
    return view

print(view_into_diagonals([1, 2, 3], 6))
# [[1 2 3 0 0 0]
#  [0 1 2 3 0 0]
#  [0 0 1 2 3 0]
#  [0 0 0 1 2 3]
#  [0 0 0 0 1 2]
#  [0 0 0 0 0 1]]