【问题标题】:Cycling Slicing in PythonPython中的循环切片
【发布时间】:2018-06-06 09:06:54
【问题描述】:

我在尝试将 Cesar Cipher 应用于每行具有不同移位值的矩阵时提出了这个问题,即给定矩阵 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]]])

【问题讨论】:

  • 不应该是 [2,7,8] 用于 3D 案例中的最后一列等等吗?
  • 这可能是左移。
  • 啊,是的,你提到了右移,谢谢。

标签: python python-2.7 numpy slice


【解决方案1】:

方法 #1: 使用 modulus 实现循环模式并获取新的列索引,然后简单地使用 advanced-indexing 提取元素,为我们提供矢量化解决方案,如下所示 -

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:我们还可以利用strides 的力量来进一步加快速度。这个想法是从一开始连接切掉的部分并将其附加到最后,然后创建长度与列数相同的滑动窗口,最后索引到适当的窗口编号以获得相同的翻转效果。实现会是这样 -

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,可能有一种通用解决方案,但我会一直保持到这一点。

【讨论】:

  • 感谢您的回答。如何将这个答案应用于更多维度的数组?喜欢 3D 数组?
  • @AliShakiba 那么,S 的长度是多少?是和X的第一轴还是第二轴一样?
  • 是的。假设我们需要在任一维度上向右或向左应用移位。
  • @AliShakiba 不清楚。能否为 3D 数组添加示例案例?
  • 我已经更新了问题并添加了示例并修正了一个错字。
【解决方案2】:

您可以使用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]])

【讨论】:

    猜你喜欢
    • 2018-10-13
    • 2013-09-26
    • 2020-08-08
    • 1970-01-01
    • 2018-06-21
    • 2021-03-28
    • 1970-01-01
    • 2021-11-01
    • 1970-01-01
    相关资源
    最近更新 更多