1D numpy 数组,用于新 2D 数组中每个连续行向右移动



我正在尝试通过删除 for 循环并仅在处理大型数据集时使用 numpy 数组来优化一些代码。

我想拿一个 1D numpy 数组,例如:

a = [1, 2, 3, 4, 5]

并生成一个 2D numpy 数组,其中每列中的值沿一个位置移动,例如在上面的情况下,我希望有一个返回的函数:

[[1 2 3 4 5]
 [0 1 2 3 4]
 [0 0 1 2 3]
 [0 0 0 1 2]
 [0 0 0 0 1]]

我找到了使用步幅函数做类似生产的事情的例子,例如:

[[1 2 3]
 [2 3 4]
 [3 4 5]]

但是,我正在尝试将我的每列都向另一个方向移动。或者,可以将问题视为将 a 的第一个元素放在第一对角线上,将第二个元素放在第二对角线上,依此类推。但是,我想再次强调我希望如何避免完全使用 for、while 或 if 循环。任何帮助将不胜感激。

这样的矩阵是 Toeplitz 矩阵的一个例子。 您可以使用scipy.linalg.toeplitz来创建它:

In [32]: from scipy.linalg import toeplitz
In [33]: a = range(1,6)
In [34]: toeplitz(a, np.zeros_like(a)).T
Out[34]: 
array([[1, 2, 3, 4, 5],
       [0, 1, 2, 3, 4],
       [0, 0, 1, 2, 3],
       [0, 0, 0, 1, 2],
       [0, 0, 0, 0, 1]])

受@EelcoHoogendoorn回答的启发,这里有一个变体,它不会像scipy.linalg.toeplitz那样使用那么多内存:

In [47]: from numpy.lib.stride_tricks import as_strided
In [48]: a
Out[48]: array([1, 2, 3, 4, 5])
In [49]: t = as_strided(np.r_[a[::-1], np.zeros_like(a)], shape=(a.size,a.size), strides=(a.itemsize, a.itemsize))[:,::-1]
In [50]: t
Out[50]: 
array([[1, 2, 3, 4, 5],
       [0, 1, 2, 3, 4],
       [0, 0, 1, 2, 3],
       [0, 0, 0, 1, 2],
       [0, 0, 0, 0, 1]])

结果应被视为"只读"数组。 否则,当您更改元素时,您会感到有些意外。 例如:

In [51]: t[0,2] = 99
In [52]: t
Out[52]: 
array([[ 1,  2, 99,  4,  5],
       [ 0,  1,  2, 99,  4],
       [ 0,  0,  1,  2, 99],
       [ 0,  0,  0,  1,  2],
       [ 0,  0,  0,  0,  1]])

这是基于索引技巧的解决方案。不像已经发布的 toeplitz 解决方案那么优雅,但如果内存消耗或性能是一个问题,它是首选。如前所述,这也使得随后以一致的方式操作矩阵的条目变得容易。

import numpy as np
a = np.arange(5)+1
def toeplitz_view(a):
    b = np.concatenate((np.zeros_like(a),a))
    i = a.itemsize
    v = np.lib.index_tricks.as_strided(b,
        shape=(len(b),)*2,
        strides=(-i, i))
    #return a view on the 'original' data as well, for manipulation
    return v[:len(a), len(a):], b[len(a):]        
v, a = toeplitz_view(a)
print v
a[0] = 10
v[2,1] = -1
print v

最新更新