我想沿特定轴动态切片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]
但是:
值不能放在元组中,所以我无法弄清楚如何构建切片。
答案 0 :(得分:16)
我认为一种方法是使用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
我有一种模糊的感觉,我以前曾使用过这个功能,但我现在似乎无法找到它..
答案 1 :(得分:10)
因为它没有被清楚地提及(我也在寻找它):
相当于:
a = my_array[:, :, :, 8]
b = my_array[:, :, :, 2:7]
是:
a = my_array.take(indices=8, axis=3)
b = my_array.take(indices=range(2, 7), axis=3)
答案 2 :(得分:7)
派对有点晚了,但默认的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[sl]
答案 3 :(得分:1)
这晚了很晚,但是我有一个替代的切片功能,它的性能比其他答案要好:
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
np.take
给出的结果更糟,尽管它不是仅用于查看,但它确实会创建副本。
涉及clemisch's answer的np.moveaxis
花费了最长的时间,但令人惊讶的是,它引用了先前阵列的存储位置。我还向每个版本添加了一个step
参数,以防您需要。
答案 4 :(得分:0)
有一种优雅的方法可以访问数组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
要注意的是,您可能必须将moveaxis
应用于与x_move[start:end]
的输出一起使用的其他数组,以保持轴顺序的一致性。数组x_move
只是一个视图,因此您对其前轴所做的每次更改都对应于第x
轴上n
的更改(即,您可以对{ {1}}。
1)与x_move
相反,您也可以使用交换轴来担心n
和0
的顺序。与moveaxis(x, n, 0)
相比,我更喜欢moveaxis
,因为它只会更改与swapaxes
答案 5 :(得分:0)
这真的很晚了!但是我得到了Leland's answer并对其进行了扩展,因此它可以与多个轴和切片参数一起使用。这是该功能的详细版本
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) )]