Python 沿动态指定的轴切片numpy数组

Python 沿动态指定的轴切片numpy数组,python,numpy,Python,Numpy,我想沿着一个特定的轴动态切片一个numpy数组。鉴于此: axis = 2 start = 5 end = 10 我希望达到与此相同的结果: # m is some matrix m[:,:,5:10] 使用类似这样的方法: slc = tuple(:,) * len(m.shape) slc[axis] = slice(start,end) m[slc] 但是:值不能放在元组中,因此我不知道如何构建切片。我认为一种方法是使用切片(无): 我有一种模糊的感觉,我以前用过这个函数,但现在似乎

我想沿着一个特定的轴动态切片一个numpy数组。鉴于此:

axis = 2
start = 5
end = 10
我希望达到与此相同的结果:

# m is some matrix
m[:,:,5:10]
使用类似这样的方法:

slc = tuple(:,) * len(m.shape)
slc[axis] = slice(start,end)
m[slc]

但是
值不能放在元组中,因此我不知道如何构建切片。

我认为一种方法是使用
切片(无)


我有一种模糊的感觉,我以前用过这个函数,但现在似乎找不到它。

这对聚会来说有点晚了,但默认的Numpy方法是。但是,它总是复制数据(因为它支持奇特的索引,所以它总是假设这是可能的)。为了避免这种情况(在许多情况下,您需要的是数据视图,而不是副本),请退回到另一个答案中已经提到的
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)]

由于提及不够清楚(我也在寻找):

相当于:

a = my_array[:, :, :, 8]
b = my_array[:, :, :, 2:7]
是:


访问数组的任意轴
n
有一种优雅的方式:使用\将感兴趣的轴移动到前面

x_move=np.moveaxis(x,n,0)#向前移动第n个轴
x_移动[开始:结束]#访问第n轴
问题是,您可能必须在与
x_move[start:end]
输出一起使用的其他数组上应用
moveaxis
,以保持轴顺序一致。数组
x\u move
只是一个视图,因此您对其前轴所做的每一次更改都对应于
n
-th轴中
x
的更改(即,您可以读取/写入
x\u move


1) 与移动轴(x,n,0)相反,您也可以使用来不担心
n
0
的顺序。我更喜欢
moveaxis
而不是
swapaxes
,因为它只会改变有关
n

的顺序这对派对来说已经很晚了,但我有一个替代的切片功能,它的性能比其他答案中的略好:

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

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

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('')
结果如下:

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
  • 在评论中包括一些改进建议
  • 应用这些改进,避免了警告,而且速度更快
  • 参与会产生更糟糕的结果,虽然它不仅仅是视图,但它确实创建了一个副本
  • 涉及需要最长的时间才能完成,但令人惊讶的是,它引用了前一个数组的内存位置
  • 我的回答消除了对中间切片列表的需要。当切片轴朝向开始时,它还使用较短的切片索引。这将提供最快的结果,随着axis接近0,会有额外的改进
我还为每个版本添加了一个
step
参数,以防您需要它。

这确实太晚了!但我得到并扩展了它,使它可以处理多轴和切片参数。下面是函数的详细版本

from numpy import *

def slicer(a, axis=None, slices=None):
    if not hasattr(axis, '__iter__'):
        axis = [axis]
    if not hasattr(slices, '__iter__') or len(slices) != len(axis):
        slices = [slices]
    slices = [ sl if isinstance(sl,slice) else slice(*sl) for sl in slices ]
    mask = []
    fixed_axis = array(axis) % a.ndim
    case = dict(zip(fixed_axis, slices))
    for dim, size in enumerate(a.shape):
        mask.append( case[dim] if dim in fixed_axis else slice(None) )
    return a[tuple(mask)]
它适用于可变数量的轴,并以切片元组作为输入

>>> a = array( range(10**4) ).reshape(10,10,10,10)
>>> slicer( a, -2, (1,3) ).shape
(10, 10, 2, 10)
>>> slicer( a, axis=(-1,-2,0), slices=((3,), s_[:5], slice(3,None)) ).shape
(7, 10, 5, 3)

略为紧凑的版本

def slicer2(a, axis=None, slices=None):
    ensure_iter = lambda l: l if hasattr(l, '__iter__') else [l]
    axis = array(ensure_iter(axis)) % a.ndim
    if len(ensure_iter(slices)) != len(axis):
        slices = [slices]
    slice_selector = dict(zip(axis, [ sl if isinstance(sl,slice) else slice(*sl) for sl in ensure_iter(slices) ]))
    element = lambda dim_: slice_selector[dim_] if dim_ in slice_selector.keys() else slice(None)
    return a[( element(dim) for dim in range(a.ndim) )]

m
长什么样?@MrAlias没关系。问题是如何动态构建切片。链接问题:谢谢——这解决了问题
slice(None)
显然等同于
。这是一个很好的解决方案,尽管在
m[slc]
中使用列表进行索引现在在Numpy中被弃用,并引发
未来警告。在
FutureWarning
中,建议的修复方法是将列表转换为元组,即
m[tuple(slc)]
。当使用
netCDF4
从netCDF数据集中提取时,此答案也有效,其中
numpy.take
不可用。请使用
len(m.shape)
,使用
m.ndim
。如果您能澄清
inds
参数所需的数据类型,则会有所帮助。这应该是答案。使用np.take可创建一个新数组,从原始数组复制数据。这可能不是您想要的(对于大型阵列,额外的内存使用可能非常重要)
>>> a = array( range(10**4) ).reshape(10,10,10,10)
>>> slicer( a, -2, (1,3) ).shape
(10, 10, 2, 10)
>>> slicer( a, axis=(-1,-2,0), slices=((3,), s_[:5], slice(3,None)) ).shape
(7, 10, 5, 3)
def slicer2(a, axis=None, slices=None):
    ensure_iter = lambda l: l if hasattr(l, '__iter__') else [l]
    axis = array(ensure_iter(axis)) % a.ndim
    if len(ensure_iter(slices)) != len(axis):
        slices = [slices]
    slice_selector = dict(zip(axis, [ sl if isinstance(sl,slice) else slice(*sl) for sl in ensure_iter(slices) ]))
    element = lambda dim_: slice_selector[dim_] if dim_ in slice_selector.keys() else slice(None)
    return a[( element(dim) for dim in range(a.ndim) )]