2

Suppose I have a CLI and would like to construct a slice from the CLI, where the CLI user just inputs a range string.

r = '1:4:2'
axis = '2'

And I have a way of slicing this as follows:

arr = np.zeros((10,10,10))

sliced_arr = eval("arr["+','.join([':',':',r])+"]")

Question: is there a better way to do this?

3
  • Yes. Not using eval is just about always better. Commented Dec 5, 2019 at 17:05
  • is axis ever (meant to be) used? Commented Dec 5, 2019 at 17:09
  • @norok2 yeah, in my full code it is used. I didn't bother because this is the minimal representation. It just is there to imply that it is part of a bigger function. Commented Dec 5, 2019 at 23:08

3 Answers 3

2

Here's one using r and axis as input arguments -

indexer = [slice(None)]*arr.ndim
indexer[int(axis)] = slice(*map(int,r.split(':')))
out = arr[tuple(indexer)]

Generalizing to handle to any generic indexing string notation in r, it would be -

ax = int(axis)
indexer = [slice(None)]*arr.ndim
o = np.array([0,arr.shape[ax],1])
v = np.array(r.split(':'))
v = np.r_[v,['']*(len(o)-len(v))]
indexer[ax] = slice(*np.where(v=='',o,v).astype(int))
out = arr[tuple(indexer)]
Sign up to request clarification or add additional context in comments.

2 Comments

one reason I went with eval is in case someone did something clever like "::-1": the eval will always work exactly like brackets. I did look at slice, but it seemed like it would not handle this case well. Is that wrong?
@donlan Edited to handle generic string notations in r.
1

You could build a slice object from the string, and then use numpy.s_:

import numpy as np

r = '1:4:2'
arr = np.zeros((10,10,10))

s = slice(*map(int, r.split(":")))
print(s)

sliced_arr = arr[np.s_[:, :, s]]

Output

slice(1, 4, 2)

3 Comments

I think OP wants to use that slice only for the one axis
Perhaps, but the string passed to eval() is, indeed, 'arr[:,:,1:4:2]' which would not be strictly equivalent to this solution.
@norok2 You are right, modified the answer to match the output of the eval()
0

You can use something like this, which also supports negative indexs:

import numpy as np


def str2slicing(
        slice_str: str,
        index: int,
        n_dim: int):
    assert(-n_dim <= index < n_dim)
    index %= n_dim
    return tuple(
        slice(*map(int, slice_str.split(':'))) if i == index else slice(None)
        for i in range(n_dim))


arr = np.random.randint(1, 100, (10, 10, 10))
sliced_arr = arr[str2slicing('1:4:2', int('2'), arr.ndim)]
print(sliced_arr.shape)
# (10, 10, 2)

(Note that this is pretty much an extension / variation of the other answers: the juice is pretty much this slice(*map(int, slice_str.split(':')))).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.