提问人:Ali Shakiba 提问时间:12/25/2017 最后编辑:Ali Shakiba 更新时间:12/25/2017 访问量:1045
Python 中的循环切片
Cycling Slicing in Python
问:
我在尝试将 Cesar 密码应用于每行具有不同移位值的矩阵时提出了这个问题,即给定一个矩阵X
array([[1, 0, 8],
[5, 1, 4],
[2, 1, 1]])
当移位值为 时,输出需要S = array([0, 1, 1])
array([[1, 0, 8],
[1, 4, 5],
[1, 1, 2]])
这很容易通过以下代码实现:
Y = []
for i in range(X.shape[0]):
if (S[i] > 0):
Y.append( X[i,S[i]::].tolist() + X[i,:S[i]:].tolist() )
else:
Y.append(X[i,:].tolist())
Y = np.array(Y)
这是一个左循环移位。我想知道如何使用numpy数组以更有效的方式做到这一点?
更新:本示例将移位应用于矩阵的列。假设我们有一个 3D 数组
array([[[8, 1, 8],
[8, 6, 2],
[5, 3, 7]],
[[4, 1, 0],
[5, 9, 5],
[5, 1, 7]],
[[9, 8, 6],
[5, 1, 0],
[5, 5, 4]]])
然后,在柱子上循环右移导致S = array([0, 0, 1])
array([[[8, 1, 7],
[8, 6, 8],
[5, 3, 2]],
[[4, 1, 7],
[5, 9, 0],
[5, 1, 5]],
[[9, 8, 4],
[5, 1, 6],
[5, 5, 0]]])
答:
1赞
Moses Koledoye
12/25/2017
#1
您可以使用新行来移动每一行,并使用新行来构建输出数组:np.roll
Y = []
for i, x in zip(S, X):
Y.append(np.roll(x, -i))
print(np.array(Y))
array([[1, 0, 8],
[1, 4, 5],
[1, 1, 2]])
4赞
Divakar
12/25/2017
#2
方法#1:使用模
数来实现循环模式并得到新的列索引,然后简单地使用高级索引
来提取元素,给我们一个矢量化的解决方案,如下所示:
def cyclic_slice(X, S):
m,n = X.shape
idx = np.mod(np.arange(n) + S[:,None],n)
return X[np.arange(m)[:,None], idx]
方法#2:我们还可以利用步幅
的力量来进一步加速。这个想法是将切片部分从头开始连接起来,并在末尾附加它,然后创建长度与列数相同的滑动窗口,最后索引到适当的窗口编号以获得相同的滚动效果。实现将是这样的——
def cyclic_slice_strided(X, S):
X2 = np.column_stack((X,X[:,:-1]))
s0,s1 = X2.strides
strided = np.lib.stride_tricks.as_strided
m,n1 = X.shape
n2 = X2.shape[1]
X2_3D = strided(X2, shape=(m,n2-n1+1,n1), strides=(s0,s1,s1))
return X2_3D[np.arange(len(S)),S]
试运行 -
In [34]: X
Out[34]:
array([[1, 0, 8],
[5, 1, 4],
[2, 1, 1]])
In [35]: S
Out[35]: array([0, 1, 1])
In [36]: cyclic_slice(X, S)
Out[36]:
array([[1, 0, 8],
[1, 4, 5],
[1, 1, 2]])
运行时测试 -
In [75]: X = np.random.rand(10000,100)
...: S = np.random.randint(0,100,(10000))
# @Moses Koledoye's soln
In [76]: %%timeit
...: Y = []
...: for i, x in zip(S, X):
...: Y.append(np.roll(x, -i))
10 loops, best of 3: 108 ms per loop
In [77]: %timeit cyclic_slice(X, S)
100 loops, best of 3: 14.1 ms per loop
In [78]: %timeit cyclic_slice_strided(X, S)
100 loops, best of 3: 4.3 ms per loop
适配3D
外壳
根据情况,我们会——approach #1
3D
shift = 'left'
axis = 1 # axis along which S is to be used (axis=1 for rows)
n = X.shape[axis]
if shift == 'left':
Sa = S
else:
Sa = -S
# For rows
idx = np.mod(np.arange(n)[:,None] + Sa,n)
out = X[:,idx, np.arange(len(S))]
# For columns
idx = np.mod(Sa[:,None] + np.arange(n),n)
out = X[:,np.arange(len(S))[:,None], idx]
# For axis=0
idx = np.mod(np.arange(n)[:,None] + Sa,n)
out = X[idx, np.arange(len(S))]
可能有一种方法可以为通用解决方案提供通用解决方案,但我会保留到这一点。axis
评论
0赞
Ali Shakiba
12/25/2017
谢谢你的回答。如何将这个答案应用于更多维度的数组?喜欢 3D 数组吗?
0赞
Divakar
12/25/2017
@AliShakiba那么,长度是多少?它与 的第一个轴或第二个轴相同吗?S
X
0赞
Ali Shakiba
12/25/2017
是的。假设我们需要在任何一个维度上向右或向左应用偏移。
0赞
Divakar
12/25/2017
@AliShakiba 目前尚不清楚。你能为3D阵列添加一个示例案例吗?
0赞
Ali Shakiba
12/25/2017
我已经更新了问题并添加了示例并修复了一个错别字。
评论
[2,7,8]