Python中移动矩阵行的最快方法

Python中移动矩阵行的最快方法,python,numpy,matrix,shift,Python,Numpy,Matrix,Shift,我有一个4x4矩阵,如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 我想根据行索引的数量向左移动每一行(左循环移动)。即第0行保持原样,第1行向左移动1,第2行向左移动2,等等 所以我们得到这个: 1 2 3 4 6 7 8 5 11 12 9 10 16 13 14 15 我在Python中想出的最快的方法是: 将numpy导入为np def换档旋钮(x): x[1:][np.append(x[i][i:],x[i][:

我有一个4x4矩阵,如下所示:

1  2  3  4
5  6  7  8
9  10 11 12
13 14 15 16
我想根据行索引的数量向左移动每一行(左循环移动)。即第0行保持原样,第1行向左移动1,第2行向左移动2,等等

所以我们得到这个:

1  2  3  4
6  7  8  5
11 12 9  10
16 13 14 15
我在Python中想出的最快的方法是:

将numpy导入为np
def换档旋钮(x):
x[1:][np.append(x[i][i:],x[i][:i])表示范围(1,4)内的i
返回x
我需要像这样在数千个4x4矩阵上运行这个函数,所以速度很重要(在Python中尽可能)。我不关心使用其他模块,比如numpy,我只关心速度

任何帮助都将不胜感激

谢谢大家!

首先改进,摆脱列表理解 我假设您的输入将始终是4x4 Ndaray。如果没有,您需要适当地修改函数(即添加
np.asarray
,检查维度等)。删除列表理解已经提供了很好的加速:

import numpy as np

a = np.arange(16).reshape(4, 4)

def ShiftRows(x):
    x[1:] = [np.append(x[i][i:], x[i][:i]) for i in range(1, 4)]
    return x

def shift(x):
    for i in range(1, 4):
        x[i] = np.append(x[i, i:], x[i, :i])
    return x

%timeit ShiftRows(a)
# 38.6 µs ± 1.08 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit shift(a)
# 31.9 µs ± 583 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
请记住,这两种变体都会在适当的位置修改阵列。如果这不是您想要的,请在两个函数的开头添加一个
x=x.copy()

从我的测试来看,
numpy.roll
比这两个版本都慢得多

第二个改进,使用 现在,真正的加速比来自于我们使用:

这比现在快了15倍。使用
parallel
模式不会提高性能,可能是因为阵列的大小很小


测试:展开循环 应Patrick Artner的要求,我展开了循环(很可能是4x4):

展开似乎会产生相同的结果



编辑:修复了一个大问题,现在的加速比要小得多。

只使用列表列表的基本解决方案(适用于那些没有考虑numpy的人):

得到

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[1, 2, 3, 4], [6, 7, 8, 5], [11, 12, 9, 10], [16, 13, 14, 15]]
[[1, 2, 3, 4], [6, 7, 8, 5], [11, 12, 9, 10], [16, 13, 14, 15]]

4.8055571                 # timing with for loop
4.098531100000001         # timing with unrolled loop
因此,手动展开循环似乎更快。您可能还需要使用numpy来了解这一点。

这一点很有效:

import numpy as np


def stepped_roll(arr):
    return np.array([np.roll(row, -n) for n, row in enumerate(arr)])


print(stepped_roll(np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])))
我倾向于使用
np.roll
,因为numpy例程往往比Python更快
np.apply_沿_轴
在这里不起作用,因为你需要每行的索引


但是,在您的情况下,操作非常简单,数据集非常小,像@JanChristofterasa的答案中建议的
shift()
函数会快得多。

如果您不介意硬编码数组大小,在我的测试中,硬编码重排模式的速度大约是6倍:

def rot(a):
    return a.take((0,1,2,3,5,6,7,4,10,11,8,9,15,12,13,14)).reshape(4, 4)

您的4x4自动取款机是什么格式的?简单的python列表?磁盘上的文件?名单?简单列表?它是一个numpy 2d arrayalways 4x4?或者总是方形?总是4x4,而不仅仅是普通python的方形手动展开循环更有效-也许也可以检查一下这种方法?@PatrickArtner我将在一分钟内展示结果。这些都是很好的解决方案。请注意,numba/jit在很大程度上取决于您的硬件。numba solutionsEDIT的改进不到10倍:我在代码中发现了一个错误,并添加了正确的数字。展开的与未展开的具有相同的性能。
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]
[[1, 2, 3, 4], [6, 7, 8, 5], [11, 12, 9, 10], [16, 13, 14, 15]]
[[1, 2, 3, 4], [6, 7, 8, 5], [11, 12, 9, 10], [16, 13, 14, 15]]

4.8055571                 # timing with for loop
4.098531100000001         # timing with unrolled loop
import numpy as np


def stepped_roll(arr):
    return np.array([np.roll(row, -n) for n, row in enumerate(arr)])


print(stepped_roll(np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]])))
def rot(a):
    return a.take((0,1,2,3,5,6,7,4,10,11,8,9,15,12,13,14)).reshape(4, 4)