2015-12-21 123 views
5

切片slice(start, stop[, step])的切片索引通常可以由range(start, stop, step)(或当考虑基础维度时为range(*slice(start, stop, step).indices(length)))表示。表示连接切片的切片

比方说,我甚至有两个多维切片,第二个切片可以用作应用第一个切片结果的切片。

例子:

import numpy as np 
data = np.random.rand(*(100, 100, 100)) 
a = data[::2, 7, :] # slice 1, a.shape = (50,100) 
b = a[1, ::-1] # slice 2, b.shape = (100,) 

我想找个一般表达式计算单个片,做同样的工作。我知道底层数据结构的维度。

c = data[2, 7, ::-1] # same as b 
np.array_equal(b, c) # True 

所以,在这个例子中,从[::2, 7, :][1, ::-1]前往[2, 7, ::-1]我需要像一个函数:

def concatenate_slices(shape, outer_slice, inner_slice): 
    ... 
    return combined_slice 

其中outer_sliceinner_slice都希望能切片的元组。在示例shape=(100, 100, 100)outer_slice=(slice(None, None, 2), 7, slice(None, None, None))inner_slice=(1, slice(None, None, -1))中。

我不知道如何有效地做到这一点。

当我调用__getitem__(slice)(没有中间视图)时,我的对象会做某些事情,我只想做一次,但仍然有可能切片。

作为一个扩展(可选),我想知道如果我在切片中有椭圆会发生什么。那我该如何组合?

+2

仅供参考:'range(start,stop,step)'并不总是产生正确的索引,因为它也可能产生索引超出范围,以创建一个有效范围['slice.indices()']( https://docs.python.org/3/reference/datamodel.html?highlight=slice.indices#slice.indices)需要被使用:'range(* slice(start,stop,step).indices(length) )'。我不确定是否可以将切片组合而不考虑底层结构的形状。 – mata

+0

@mata谢谢。你是对的。在我的情况下,我也会知道底层结构的形状。我编辑了一下。 – Trilarion

+0

我想这可以通过查看前两个的限制来手动组合新切片来完成。我开始写它,但对我来说太麻烦了,应该有更好的方法(所以我放弃了)。 –

回答

1

让我们从简单情况开始:一维数组。我们需要跟踪startstop,并step值的最终片,我们可以更新,像这样:

def update_1d(a, b, length): 
    a_start, a_stop, a_step = a.indices(length) 
    a_length = len(xrange(a_start, a_stop, a_step)) 
    if a_length == 0: 
    # doesn't matter what b is if data[a] is [] 
    return a 
    b_start, b_stop, b_step = b.indices(a_length) 
    b_length = len(xrange(b_start, b_stop, b_step)) 
    if b_length == 0: 
    # result will be empty, so we can exit early 
    return slice(0, 0, 1) 
    # convert b's start into a's coordinates, without wrapping around 
    start = max(0, a_start + b_start * a_step) 
    # steps are multiplicative, which makes things easy 
    step = a_step * b_step 
    # the stop index is the hard part because it depends on the sign of both steps 
    x = a_start + b_stop * a_step 
    if step < 0: 
    # indexing backwards, so truncate if b's converted step goes below zero 
    stop = x if x >= 0 else None 
    elif a_step > 0: 
    # both steps are positive, so take the smallest stop index 
    stop = min(a_stop, x) 
    else: 
    # both steps are negative, so take the largest stop index 
    stop = max(a_stop, x) 
    return slice(start, stop, step) 

注意,这个预计ab是切片。不过,您通常可以将其他表单转换为切片对象。这甚至包括Ellipsis对象,假设你知道你有多少维度。

为了将其扩展到多维情况,我们需要做一些簿记来跟踪哪个原始维度被切分。例如,如果您有data[::2, 7, :][:, 2:-2],则必须将第二个切片的第二个维度映射到第一个切片的第三个维度。

+0

检查这种情况:'a = slice(-1,1,-2)','b = slice(0,-1,1)','length = 16'。 –

+0

谢谢。这已经是一个好的开始。 len(xrange())应该效率低一个人可以使用floor((stop-start)/ step)吗? – Trilarion

+0

'len(xrange(...))'实际上是相当快的,因为'xrange'对象基本上和你建议的一样,但是在C代码中。此外,沃伦是对的,我发布的代码不适用于所有情况。敬请期待一个更好的版本。 – perimosocordiae

1

我怀疑你只需要通过分析每个维度的繁琐工作来构建一个新的切片或一系列索引。我怀疑是否有捷径。

为了说明把你的例子:

In [77]: shape=(100,100,100) 
In [78]: outer_slice=(slice(None, None, 2), 7, slice(None, None, None)) 
In [79]: inner_slice=(1, slice(None, None, -1)) 

的目标是(对不对?):

(2, 7, slice(None,None,-1)) 

第一维度 - 使整个索引范围的阵列,并依次切片他们:

In [80]: idx=np.arange(shape[0]) 
In [81]: idx[outer_slice[0]][inner_slice[0]] 
Out[81]: 2 

我可以推断,从[:: 2]和[1]?我必须说明它从0开始,形状足够大以产生第二个值,等等。

现在为第二维。这是一个标量,所以没有对应的inner切片。

In [82]: outer_slice[1] 
Out[82]: 7 

对于第三个,我们做的一样,由第1,但考虑到内部和外部列表之间的偏差:

In [83]: idx=np.arange(shape[2]) 
In [84]: idx[outer_slice[2]][inner_slice[1]] 
Out[84]: 
array([99, 98, 97, 96, 95, 94, 93, 92, 91, ....7, 6, 5, 4, 3, 2, 1, 0]) 

或者,我可以推断出outer_slice[2]什么也不做,所以我可以直接使用inner_slice[1]

当然,将两个切片元组应用到实际数组中将会同样简单和高效。

X[outer_slice][inner_slice] 

只要outer_slice产生的图,它们组合成一个复合片不是多大的改善。

随着形状和切片元组有足够的信息来建立一个新的元组。但是看起来,所需的逻辑将会相当复杂,需要深入了解切片和大量测试。