从向量创建矩阵,其中每行是向量的移位版本

Cle*_*leb 7 python performance numpy vectorization

我有一个像这样的numpy数组

import numpy as np

ar = np.array([1, 2, 3, 4])
Run Code Online (Sandbox Code Playgroud)

我想创建一个如下所示的数组:

array([[4, 1, 2, 3],
       [3, 4, 1, 2],
       [2, 3, 4, 1],
       [1, 2, 3, 4]])
Run Code Online (Sandbox Code Playgroud)

因此,每行对应于ar行指数+ 1的移位.

简单的实现可能如下所示:

ar_roll = np.tile(ar, ar.shape[0]).reshape(ar.shape[0], ar.shape[0])

for indi, ri in enumerate(ar_roll):
    ar_roll[indi, :] = np.roll(ri, indi + 1)
Run Code Online (Sandbox Code Playgroud)

这给了我想要的输出.

我的问题是,是否有一种更聪明的方法来避免循环.

Div*_*kar 5

这是一种使用NumPy strides基本填充和剩余元素的方法,然后strides帮助我们非常有效地创建移位版本 -

def strided_method(ar):
    a = np.concatenate(( ar, ar[:-1] ))
    L = len(ar)
    n = a.strides[0]
    return np.lib.stride_tricks.as_strided(a[L-1:], (L,L), (-n,n))
Run Code Online (Sandbox Code Playgroud)

样品运行 -

In [42]: ar = np.array([1, 2, 3, 4])

In [43]: strided_method(ar)
Out[43]: 
array([[4, 1, 2, 3],
       [3, 4, 1, 2],
       [2, 3, 4, 1],
       [1, 2, 3, 4]])

In [44]: ar = np.array([4,9,3,6,1,2])

In [45]: strided_method(ar)
Out[45]: 
array([[2, 4, 9, 3, 6, 1],
       [1, 2, 4, 9, 3, 6],
       [6, 1, 2, 4, 9, 3],
       [3, 6, 1, 2, 4, 9],
       [9, 3, 6, 1, 2, 4],
       [4, 9, 3, 6, 1, 2]])
Run Code Online (Sandbox Code Playgroud)

运行时测试 -

In [5]: a = np.random.randint(0,9,(1000))

# @Eric's soln
In [6]: %timeit roll_matrix(a)
100 loops, best of 3: 3.39 ms per loop

# @Warren Weckesser's soln
In [8]: %timeit circulant(a[::-1])
100 loops, best of 3: 2.03 ms per loop

# Strides method
In [18]: %timeit strided_method(a)
100000 loops, best of 3: 6.7 µs per loop
Run Code Online (Sandbox Code Playgroud)

制作副本(如果你想进行更改而不只是用作只读数组)不会对我们的strides方法造成太大的伤害-

In [19]: %timeit strided_method(a).copy()
1000 loops, best of 3: 381 µs per loop
Run Code Online (Sandbox Code Playgroud)


War*_*ser 5

现有的两个答案都很好;如果您已经在使用 scipy,则可能只会对这个答案感兴趣。

您描述的矩阵称为循环矩阵。如果您不介意对 scipy 的依赖,您可以使用以下方法scipy.linalg.circulant创建一个:

In [136]: from scipy.linalg import circulant

In [137]: ar = np.array([1, 2, 3, 4])

In [138]: circulant(ar[::-1])
Out[138]: 
array([[4, 1, 2, 3],
       [3, 4, 1, 2],
       [2, 3, 4, 1],
       [1, 2, 3, 4]])
Run Code Online (Sandbox Code Playgroud)