2

I'd like to slice a numpy array to get all but the first item, unless there's only one element, in which case, I only want to select that element (i.e. don't slice).

Is there a way to do this without using an if-statement?

x = np.array([1,2,3,4,5])
y = np.array([1])
print(x[1:]) # works
print(y[1 or None:]) # doesn't work 

I tried the above, but it didn't work.

vsocrates
  • 172
  • 13

5 Answers5

2

A way to write that without a conditional is to use negative indexing with -len(arr) + 1:

>>> x = np.array([1,2,3,4,5])
>>> y = np.array([1])

>>> x[-len(x)+1:]
array([2, 3, 4, 5])

>>> y[-len(y)+1:]
array([1])

If the array has N elements where N > 1, slice becomes -N+1:. Since -N+1 < 0, it is effectively (N + (-N + 1)): === 1:, i.e, first one onwards.

Elsewhen N == 1, slice is 0:, i.e., take the first element onwards which is the only element.

Because of how slicing works, an empty array (i.e., N = 0 case) will result in an empty array too.

Mustafa Aydın
  • 17,645
  • 4
  • 15
  • 38
  • @vsocrates OK, maybe this works, but for the sake of anyone reading your code in the future, including yourself in a year or two, please don't do this! Think about legibility. An if statement here would make the code's intent clear. The more concise code just makes things cryptic. – joanis Sep 23 '21 at 20:08
  • I would frankly use the more verbose `y[0] if len(y) == 1 else y[1:]` – joanis Sep 23 '21 at 20:13
1

You can just write an if / else:

x[1 if len(x) > 1 else 0:]
array([2, 3, 4, 5])

y[1 if len(y) > 1 else 0:]
array([1])

Or:

y[int(len(y) > 1):]
array([1])

x[int(len(x) > 1):]
array([2, 3, 4, 5])
Psidom
  • 209,562
  • 33
  • 339
  • 356
  • 1
    Looks like I'll have to write some sort of conditional. Just wondering if there was a clever numpy way to do it. I do like the 3rd option, even though it's not quite as clear. Thanks! – vsocrates Sep 23 '21 at 19:56
0

Just move None out of the brackets

x = [1,2,3,4,5]
y = [1]
print(x[1:]) 
print(y[1:] or None)
MarkBeras
  • 459
  • 1
  • 3
  • 13
  • This doesn't work, but sorry, my mistake. I want to keep the single element array then. Updated the question. – vsocrates Sep 23 '21 at 19:54
  • you could just write `or y` instead. However, if you do this with numpy arrays, Numpy will emit a warning. – Joooeey Sep 23 '21 at 20:06
  • then just use y instead of None: ```x = [1,2,3,4,5] y = [1] print(x[1:]) print(y[1:] or y) print(x[1:] or x)``` – MarkBeras Sep 23 '21 at 20:20
0

Check the array size is greater than 1 if the case you can delete the first element from array and it will give new array without it.

print(np.delete(x, 0))

Now you can get a new array which will contain only remaining items other than first.

0

Use a ternary expression to make the logic clear but still be able to use it as a function argument or inside other expressions.

The ternary expression x if len(x) == 1 else x[1:] works and is very clear. And you can use it as a parameter in a function call, or in a larger expression.

E.g.:

>>> x = np.array([1,2,3,4,5])
>>> y = np.array([1])
>>> print(x if len(x) == 1 else x[1:])
[2 3 4 5]
>>> print(y if len(y) == 1 else y[1:])
[1]

Musings on other solutions

I'm not sure if you're looking for the most concise code possible to do this, or just for the ability to have the logic inside a single expression.

I don't recommend fancy slicing solutions with negative indexing, for the sake of legibility of your code. Think about future readers of your code, even yourself in a year or two.

Using this is a larger expression

In the comments, you mention you need a solution that can be incorporated into something like a comprehension. The ternary expression can be used as is within a comprehension. For example, this code works:

l = [np.array(range(i)) for i in range(5)]
l2 = [
    x if len(x) == 1 else x[1:]
    for x in l
]

I've added spacing to make the code easier to read, but it would also work as a one liner:

l2 = [x if len(x) == 1 else x[1:] for x in l]

EDIT note

Earlier, I thought you wanted the first element extracted from the list in the single-element case, i.e., x[0], but I believe you actually want that single-element list unsliced, i.e., x, so I've updated my answer accordingly.

joanis
  • 10,635
  • 14
  • 30
  • 40
  • The issue is the larger expression I'm using is a list comprehension. I don't think this solution works in that case. However, I do agree that readability is important, so I might use @Psidom's solution instead. – vsocrates Sep 24 '21 at 02:42
  • Well, I'm pretty sure you can use a ternary expression in a comprehension, I've seen it in my code base before, in stuff written by colleagues. But of course, the best solution is certainly the one that works in your actual code. – joanis Sep 24 '21 at 04:49