沿动态指定的轴切割numpy数组

Sea*_*sey 21 python numpy

我想动态切片沿特定轴的numpy数组.鉴于这种:

axis = 2
start = 5
end = 10
Run Code Online (Sandbox Code Playgroud)

我希望得到与此相同的结果:

# m is some matrix
m[:,:,5:10]
Run Code Online (Sandbox Code Playgroud)

使用这样的东西:

slc = tuple(:,) * len(m.shape)
slc[axis] = slice(start,end)
m[slc]
Run Code Online (Sandbox Code Playgroud)

但是这些:值不能放在元组中,所以我无法弄清楚如何构建切片.

DSM*_*DSM 19

我认为一种方法是使用slice(None):

>>> m = np.arange(2*3*5).reshape((2,3,5))
>>> axis, start, end = 2, 1, 3
>>> target = m[:, :, 1:3]
>>> target
array([[[ 1,  2],
        [ 6,  7],
        [11, 12]],

       [[16, 17],
        [21, 22],
        [26, 27]]])
>>> slc = [slice(None)] * len(m.shape)
>>> slc[axis] = slice(start, end)
>>> np.allclose(m[slc], target)
True
Run Code Online (Sandbox Code Playgroud)

我有一种模糊的感觉,我之前使用过这个功能,但我现在似乎无法找到它.

  • 很好的解决方案,尽管现在 Numpy 中已弃用“m[slc]”中的列表索引并抛出“FutureWarning”。“FutureWarning”中建议的修复是将列表转换为元组,即“m[tuple(slc)]”。 (3认同)
  • 使用“m.ndim”代替“len(m.shape)”。 (2认同)

Śmi*_*gło 15

因为它没有被清楚地提及(我也在寻找它):

相当于:

a = my_array[:, :, :, 8]
b = my_array[:, :, :, 2:7]
Run Code Online (Sandbox Code Playgroud)

是:

a = my_array.take(indices=8, axis=3)
b = my_array.take(indices=range(2, 7), axis=3)
Run Code Online (Sandbox Code Playgroud)

  • 使用 np.take 创建一个新数组,该数组从原始数组复制数据。这可能不是您想要的(额外的内存使用对于大型数组来说可能很重要) (8认同)
  • 这应该是答案。 (3认同)

Eel*_*aak 9

派对有点晚了,但默认的Numpy方式是这样做的numpy.take.但是,那个总是复制数据(因为它支持花式索引,它总是假设这是可能的).为了避免这种情况(在许多情况下,您需要查看数据而不是副本),请回退到slice(None)另一个答案中已经提到的选项,可能将其包装在一个很好的函数中:

def simple_slice(arr, inds, axis):
    # this does the same as np.take() except only supports simple slicing, not
    # advanced indexing, and thus is much faster
    sl = [slice(None)] * arr.ndim
    sl[axis] = inds
    return arr[tuple(sl)]
Run Code Online (Sandbox Code Playgroud)

  • 如果您能澄清您期望的“inds”参数是什么数据类型,将会很有帮助。 (2认同)

Lel*_*rth 7

这是非常迟到了,但我有一个备用切割功能执行比那些从其他的答案略胜一筹:

def array_slice(a, axis, start, end, step=1):
    return a[(slice(None),) * (axis % a.ndim) + (slice(start, end, step),)]
Run Code Online (Sandbox Code Playgroud)

这是测试每个答案的代码。每个版本都标有发布答案的用户的姓名:

import numpy as np
from timeit import timeit

def answer_dms(a, axis, start, end, step=1):
    slc = [slice(None)] * len(a.shape)
    slc[axis] = slice(start, end, step)
    return a[slc]

def answer_smiglo(a, axis, start, end, step=1):
    return a.take(indices=range(start, end, step), axis=axis)

def answer_eelkespaak(a, axis, start, end, step=1):
    sl = [slice(None)] * m.ndim
    sl[axis] = slice(start, end, step)
    return a[tuple(sl)]

def answer_clemisch(a, axis, start, end, step=1):
    a = np.moveaxis(a, axis, 0)
    a = a[start:end:step]
    return np.moveaxis(a, 0, axis)

def answer_leland(a, axis, start, end, step=1):
    return a[(slice(None),) * (axis % a.ndim) + (slice(start, end, step),)]

if __name__ == '__main__':
    m = np.arange(2*3*5).reshape((2,3,5))
    axis, start, end = 2, 1, 3
    target = m[:, :, 1:3]
    for answer in (answer_dms, answer_smiglo, answer_eelkespaak,
                   answer_clemisch, answer_leland):
        print(answer.__name__)
        m_copy = m.copy()
        m_slice = answer(m_copy, axis, start, end)
        c = np.allclose(target, m_slice)
        print('correct: %s' %c)
        t = timeit('answer(m, axis, start, end)',
                   setup='from __main__ import answer, m, axis, start, end')
        print('time:    %s' %t)
        try:
            m_slice[0,0,0] = 42
        except:
            print('method:  view_only')
        finally:
            if np.allclose(m, m_copy):
                print('method:  copy')
            else:
                print('method:  in_place')
        print('')
Run Code Online (Sandbox Code Playgroud)

结果如下:

answer_dms

Warning (from warnings module):
  File "C:\Users\leland.hepworth\test_dynamic_slicing.py", line 7
    return a[slc]
FutureWarning: Using a non-tuple sequence for multidimensional indexing is 
deprecated; use `arr[tuple(seq)]` instead of `arr[seq]`. In the future this will be 
interpreted as an array index, `arr[np.array(seq)]`, which will result either in an 
error or a different result.
correct: True
time:    2.2048302
method:  in_place

answer_smiglo
correct: True
time:    5.9013344
method:  copy

answer_eelkespaak
correct: True
time:    1.1219435999999998
method:  in_place

answer_clemisch
correct: True
time:    13.707583699999999
method:  in_place

answer_leland
correct: True
time:    0.9781496999999995
method:  in_place
Run Code Online (Sandbox Code Playgroud)
  • DSM 的回答在评论中包含了一些改进建议。
  • EelkeSpaak 的回答应用了这些改进,从而避免了警告并且速度更快。
  • ?mig?o 的答案涉及np.take给出更糟糕的结果,虽然它不是仅限查看,但它确实创建了一个副本。
  • clemisch 的答案涉及np.moveaxis需要最长的时间才能完成,但令人惊讶的是它引用了前一个数组的内存位置。
  • 我的回答消除了对中间切片列表的需要。当切片轴朝向开头时,它还使用较短的切片索引。这给出了最快的结果,随着轴更接近 0,还有额外的改进。

我还step为每个版本添加了一个参数,以防您需要。


cle*_*sch 5

有一种访问任意n数组轴的优雅方法x:使用numpy.moveaxis¹ 将感兴趣的轴移到前面。

x_move = np.moveaxis(x, n, 0)  # move n-th axis to front
x_move[start:end]              # access n-th axis
Run Code Online (Sandbox Code Playgroud)

问题是您可能必须应用moveaxis到您使用的其他数组上,x_move[start:end]以保持轴顺序一致。该数组x_move只是一个视图,因此您对其前轴所做的每一次更改都对应x于第n-th 轴的更改(即您可以读/写x_move)。


1) 您也可以使用swapaxesto 不用担心nand的顺序0,与moveaxis(x, n, 0). 我更喜欢moveaxisswapaxes因为它只会改变有关n.