Concatenated Slice

Slice indexes slice(start, stop[, step]) can often be represented by range(start, stop, step) (or range(*slice(start, stop, step).indices(length)) when taking basic measurements into account).

Say I even have two multi-dimensional slices, and the second slice can be used as a slice as a result of applying the first slice.

Example:

 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,) 

I would like to find a general expression for computing a single fragment that does the same job. I know the dimensions of the underlying data structure.

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

So, when getting from [::2, 7, :] and [1, ::-1] to [2, 7, ::-1] in this example, I will need a function such as:

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

where outer_slice and inner_slice will both be a tuple of slices. In the example, shape=(100, 100, 100) and outer_slice=(slice(None, None, 2), 7, slice(None, None, None)) and inner_slice=(1, slice(None, None, -1))

I am not sure how to do this effectively.

My objects do something when __getitem__(slice) is called (there are no intermediate views), and I want to do this only once, but still have the ability to have sliced ​​slices.

As an extension (optional), I would like to know what happens if I have ellipses in slices. How can I make a combination?

+5
source share
2 answers

Let's start with a simple case: 1-d arrays. We need to track the start , stop and step values ​​for the final fragment, which we can update like this:

 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 start into a 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 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) 

Note that this expects a and b be slicers. However, you can convert other shapes to slice objects. It even includes Ellipsis objects, assuming you know how many sizes you have.

To extend this to a multidimensional case, we will need to do some accounting reporting to keep track of which initial measurement is cut. For example, if you have data[::2, 7, :][:, 2:-2] , you will need to display the second dimension of the second slice in the third dimension of the first fragment.

+1
source

I suspect you just need to bore the analysis of each dimension to create either a new slice or an array of indices. I doubt there is a short cut.

To illustrate your example:

 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)) 

Target (right?):

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

First dimension - create an array of the entire range of indices and slice them sequentially:

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

Can this be inferred from [:: 2] and [1]? I should think that it starts at 0, the form is large enough to get the second value, etc.

Now for the second dimension. This is a scalar, so there is no corresponding inner segment.

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

For the third, we will do the same as with the 1st, but taking into account the offset between the external and internal lists:

 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]) 

Or I could conclude that outer_slice[2] does nothing, so I can use inner_slice[1] directly.

Of course, it would be just as simple and effective to apply two slice sets to a real array.

 X[outer_slice][inner_slice] 

While outer_slice creates a view, combining them into a composite slice is not a big improvement.

With the shape and fragments of the tuples, there is enough information to create a new tuple. But, apparently, the required logic will be fully involved and requires a deep knowledge of cutting and a large number of tests.

+1
source

Source: https://habr.com/ru/post/1238836/


All Articles