This question already has answers here:
Python reverse-stride slicing
(8 answers)
Closed 4 years ago.
Let's say I have:
>>> a = [1, 2, 3, 4]
And I want to get a reversed slice. Let's say I want the 1st and 0th elements given start_idx = 1 and stop_idx = 0:
[2, 1]
Using the slice notation:
a[x:y:z]
What values do I use for x, y, and z using start_idx and stop_idx?
I've tried:
>>> a[start_idx:stop_idx:-1]
[2]
>>> a[start_idx:stop_idx-1:-1]
[]
Differentiation:
This question is about a slice with a negative step where the both start and end indexed elements should be included (like a closed interval in math), and the slice end index is dynamically calculated.
Understanding Python's slice notation
is a generic generic question on notation: what x, y, and z mean in a[x:y:z]. It doesn't mention the reversal case.
This question differs from the other flagged duplicates as it deals with the general case where the reversed slice begin and end indices are either calculated or given by variables rather than hard coded.
You can omit the second index when slicing if you want your reversed interval to end at index 0.
a = [1, 2, 3, 4]
a[1::-1] # [2, 1]
In general whenever your final index is zero, you want to replace it by None, otherwise you want to decrement it.
Due to indexing arithmetic, we must treat those cases separately to be consistent with the usual slicing behavior. This can be done neatly with a ternary expression.
def reversed_interval(lst, i=None, j=None):
return lst[j:i - 1 if i else None:-1]
reversed_interval([1, 2, 3, 4], 0, 1) # [2, 1]
Here are two generic solutions:
Take the forward slice then reverse it:
>>> a[stop_idx:start_idx+1][::-1]
[2, 1]
Based on this answer, use a negative step and stop 1 element before the first element (plus the stop offset):
>>> a[start_idx:stop_idx-len(a)-1:-1]
[2, 1]
Comparing execution times, the first version is faster:
>>> timeit.timeit('foo[stop_idx:start_idx+1][::-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
1.7157553750148509
>>> timeit.timeit('foo[start_idx:stop_idx-len(foo)-1:-1]', setup='foo="012345"; stop_idx=0; start_idx=3', number=10_000_000)
1.9317215870250948
Related
This question already has answers here:
Fastest way to left-cycle a numpy array (like pop, push for a queue)
(2 answers)
Closed 7 months ago.
I have an numpy array with (1,4) shape of zeros. I want to add numbers in the end of the array and the array element shift to the left.
I expect the below results:
at the beginning:
zeros=[0,0,0]
first iteration (add 1):
[0,0,1]
second iteration (add 2):
[0,1,2]
third iteration (add 3):
[1,2,3]
forth iteration (add 4):
[2,3,4]
First of all, we define a function to shift each element to the left:
def shift_all_to_the_left(array):
for index, element in enumerate(array):
if index == len(array)-1:
continue
array[index] = array[index+1]
return array
After that, we apply the function to our list and we change the last element with the desired one as many times as the number of elements that we want to add:
zeros=[0,0,0]
number_to_add= [1,2,3,4]
for number in number_to_add:
zeros = shift_all_to_the_left(zeros)
zeros[-1] = number
print(zeros)
Output
[0, 0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
I'm slicing lists in python and can't explain some results.
Both of the following seem natural to me:
>>>[0,1,2,3,4,5][1:4:1]
[1, 2, 3]
>>>[0,1,2,3,4,5]
[::-1] == [5,4,3,2,1,0]
However,
>>>[0,1,2,3,4,5][1:4:-1]
[]
thought I expected it to be [3,2,1]. Why does it produce [ ]? Why does it not reverse the list? What happens first inside python, the step or the slicing?
I also found that
>>>[0,1,2,3,4,5][-3:-6:-1]
[3,2,1]
The third number in the slice is the step count. So, in [0,1,2,3,4,5][1:4:-1], the slicing starts at 1 and goes DOWN by 1 until it is less than 4, which is immediately is. Try doing this:
>>> [0,1,2,3,4,5][4:1:-1]
[4, 3, 2]
If you are slicing this then slicing will look like this [start:end:step]. For this one:
>>> [0,1,2,3,4,5][1:4:1]
[1, 2, 3]
It is staring from index 1 to index 3 because it exlcudes the index 4 with 1 step at a time.
You are getting an empty list in the second one because you are stepping -1 from 1st index. So this will be the solution.
>>> [0,1,2,3,4,5][4:1:-1]
[4, 3, 2]
This works because you are taking an index from 4 to one with -1 step forward.
Consider the following simple python code
>>> L = range(3)
>>> L
[0, 1, 2]
We can take slices of this array as follows:
>>> L[1:3]
[1, 2]
Is there any way to wrap around the above array by shifting to the left
[1, 2, 0]
by simply using slice operations?
Rotate left n elements (or right for negative n):
L = L[n:] + L[:n]
Note that collections.deque has support for rotations. It might be better to use that instead of lists.
Left:
L[:1], L[1:] = L[-1:], L[:-1]
Right:
L[-1:], L[:-1] = L[:1], L[1:]
To my mind, there's no way, unless you agree to cut and concatenate lists as shown above.
To make the wrapping you describe you need to alter both starting and finishing index.
A positive starting index cuts away some of initial items.
A negative starting index gives you some of the tail items, cutting initial items again.
A positive finishing index cuts away some of the tail items.
A negative finishing index gives you some of the initial items, cutting tail items again.
No combination of these can provide the wrapping point where tail items are followed by initial items. So the entire thing can't be created.
Numerous workarounds exist. See answers above, see also itertools.islice and .chain for a no-copy sequential approach if sequential access is what you need (e.g. in a loop).
If you are not overly attached to the exact slicing syntax, you can write a function that produces the desired output including the wrapping behavior.
E.g., like this:
def wrapping_slice(lst, *args):
return [lst[i%len(lst)] for i in range(*args)]
Example output:
>>> L = range(3)
>>> wrapping_slice(L, 1, 4)
[1, 2, 0]
>>> wrapping_slice(L, -1, 4)
[2, 0, 1, 2, 0]
>>> wrapping_slice(L, -1, 4, 2)
[2, 1, 0]
Caveat: You can't use this on the left-hand side of a slice assignment.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm learning Python, coming from a C#/Java background, and was playing around with list behavior. I've read some of the documentation, but I don't understand how, or why, slices with indices larger than length-1 make is possible to append items.
ls = ["a", "b", "c", "d"]
n = len(ls) # n = 4
letter = ls[4] # index out of range (list indexes are 0 - 3)
# the following all do the same thing
ls += ["echo"]
ls.append("foxtrot")
ls[len(ls):] = ["golf"] # equivalent to ls[len(ls): len(ls)]
print(ls)
Although it seems odd to me, I understand how slices can modify the list they operate on. What I don't get is why list[len(list)] results in the expected out of bounds error, but list[len(list):] doesn't. I understand that slices are fundamentally different than indexes, just not what happens internally when a slice begins with an index outside of the list values.
Why can I return a slice from a list that starts with a non-existent element (len(list))? And why does this allow me to expand the list?
Also, of the first three above methods for appending an item, which one is preferred from a convention or performance perspective? Are there performance disadvantages to any of them?
While a.append(x) and a[len(a):] = [x] do the same thing, the first is clearer and should almost always be used. Slice replacement may have a place, but you shouldn't look for reasons to do it. If the situation warrants it, it should be obvious when to do it.
Start with the fact that indexing returns an element of the list, and slicing returns a "sublist" of a list. While an element at a particular either exists or does not exist, it is mathematically convenient to think of every list have the empty list as a "sublist" between any two adjacent positions. Consider
>>> a = [1, 2, 3]
>>> a[2:2]
[]
Now consider the following sequence. At each step, we increment the starting index of the slice
>>> a[0:] # A copy of the list
[1, 2, 3]
>>> a[1:]
[2, 3]
>>> a[2:]
[3]
Each time we do so, we remove one more element from the beginning of the list for the resulting slice. So what should happen when the starting index finally equals the final index (which is implicitly the length of the list)?
>>> a[3:]
[]
You get the empty list, which is consistent with the following identities
a + [] = a
a[:k] + a[k:] = a
(The first is a special case of the second where k >= len(a).
Now, once we see why it makes sense for the slice to exist, what does it mean to replace that slice with another list? In other words, what list do we get if we replace the empty list at the end of [1, 2, 3] with the list [4]? You get [1, 2, 3, 4], which is consistent with
[1, 2, 3] + [4] = [1, 2, 3, 4]
Consider another sequence, this time involving slice replacements.
>>> a = [1,2,3]; a[0:] = ['a']; a
['a']
>>> a = [1,2,3]; a[1:] = ['a']; a
[1, 'a']
>>> a = [1,2,3]; a[2:] = ['a']; a
[1, 2, 'a']
>>> a = [1,2,3]; a[3:] = ['a']; a
[1, 2, 3, 'a']
Another way of looking at slice replacement is to treat it as appending a list to a sublist of the original. When that sublist is the list itself, then it is equal to appending to the original.
Why can I return a slice from a list that starts with a non-existent element?
I can't tell for sure, but my guess would be that it's because a reasonable value can be returned (there are no elements this refers to, so return an empty list).
And why does this allow me to expand the list?
Your slice selects the last 0 elements of the list. You then replace those elements with the elements of another list.
Also, of the first three above methods for appending an item, which one is preferred
Use .append() if you're adding a single element, and .extend() if you have elements inside another list.
I'm not sure about performance, but my guess would be that those are pretty well optimized.
This line of code
print [0, 1, 2, 3, 4][0:1:1]
returns [0].
However, the following line of code:
print [0, 1, 2, 3, 4][0:0:1]
returns [].
Why is this? Based on this Explain Python's slice notation, my understanding is that the format should be:
a[start:end:step] # start through not past end, by step
So shouldn't [0, 1, 2, 3, 4][0:0:1] start and end at the 0th value, thus returning [0]?
The "end" index of a slice is always excluded from the result; i.e., listy[start:end] returns all listy[i] where start <= i < end (note use of < instead of <=). As there is no number i such that 0 <= i < 0, listy[0:0:anything] will always be an empty list (or error).
The end index in Python's slice notation is exclusive. A slice of [n:m] will return every element whose index is >= n and < m.
To simplify things a bit, try it without the step (which isn't necessary when the step value is 1):
>>> a = [0, 1, 2, 3, 4]
>>> a[0:1]
[0]
>>> a[0:0]
[]
As a general rule, the number of elements in a slice is equal to the slice's start index minus the slice's end index. I.e., the slice [n:m] will return m-n elements. This agrees with the one element (1-0) returned by [0:1] and zero elements (0-0) returned by [0:0].
(Note that this is not true if either of the slice indices is outside of the size of the array.)
For a nice visualization of how slice indices work, search for "One way to remember how slices work" at http://docs.python.org/2/tutorial/introduction.html
Note that is [0:0:1] not [0:1:1]
So:
start = 0
end = 0
step = 1
The slice [start:end:step] means it will return values that are between start and end - 1 with a certain step, so for your example:
...[0:0:1]
Values between 0 and -1, so it doesn't return anything.