Reverse Indexing in Python? - python

I know that a[end:start:-1] slices a list in a reverse order.
For example
a = range(20)
print a[15:10:-1] # prints [15, ..., 11]
print a[15:0:-1] # prints [15, ..., 1]
but you cannot get to the first element (0 in the example). It seems that -1 is a special value.
print a[15:-1:-1] # prints []
Any ideas?

You can assign your variable to None:
>>> a = range(20)
>>> a[15:None:-1]
[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>>

Omit the end index:
print a[15::-1]

In Python2.x, the simplest solution in terms of number of characters should probably be :
>>> a=range(20)
>>> a[::-1]
[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Though i want to point out that if using xrange(), indexing won't work because xrange() gives you an xrange object instead of a list.
>>> a=xrange(20)
>>> a[::-1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sequence index must be integer, not 'slice'
After in Python3.x, range() does what xrange() does in Python2.x but also has an improvement accepting indexing change upon the object.
>>> a = range(20)
>>> a[::-1]
range(19, -1, -1)
>>> b=a[::-1]
>>> for i in b:
... print (i)
...
19
18
17
16
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0
>>>
the difference between range() and xrange() learned from source: http://pythoncentral.io/how-to-use-pythons-xrange-and-range/
by author: Joey Payne

>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> print a[:6:-1]
[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7]
>>> a[7] == a[:6:-1][-1]
True
>>> a[1] == a[:0:-1][-1]
True
So as you can see when subsitute a value in start label :end: it will give you from start to end exclusively a[end].
As you can see in here as well:
>>> a[0:2:]
[0, 1]
-1 is the last value in a:
>>> a[len(a)-1] == a[-1]
True

EDIT: begin and end are variables
I never realized this, but a (slightly hacky) solution would be:
>>> a = range(5)
>>> s = 0
>>> e = 3
>>> b = a[s:e]
>>> b.reverse()
>>> print b
[2, 1, 0]

If you use negative indexes you can avoid extra assignments, using only your start and end variables:
a = range(20)
start = 20
for end in range(21):
a[start:-(len(a)+1-end):-1]

Related

Trying to make a for loop that will sum consecutive numbers every four numbers in my dataset Python

Let's say I have a dataset
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
I am trying to write a for loop that will sum the third and fourth numbers in the dataset, then subtract the sum of the first and second numbers in the dataset, then move to the next set of four (so sum the fourth and fifth, then subtract the sum of the second and third from that) down the line until it gets to the last number with three other numbers ahead of it. Problem is, I have no idea how to do either of those things. Here's what I have:
for n in a:
runsum = ((a[3] + a[2]) - (a[0] + a[1])) ... ((a[15] + a[14]) - (a[13] + a[12]))
print(runsum)
Obviously, "..." isn't how I let the for loop know it should move down the dataset, but I'm not sure how so I'm using it as a placeholder. Any advice would be much appreciated.
I think this is what you want:
[(w+z)-(x+y) for x,y,z,w in (a[i:i+4] for i in range(0,len(a),4))]
Which evaluates to [4, 4, 4, 4]
Loop through every 4th position:
for i in range(0,len(a),4):
print((a[i+3]+a[i+2])-(a[i]+a[i+1]))
The straightforward answer to this question would be to use for loop and sum all required indices:
s = 0
for i in range(0, len(a) - 3):
s += a[i+2] + a[i+3] - a[i] - a[i+1]
Or, as a for comprehension:
sum(a[i+2] + a[i+3] - a[i] - a[i+1] for i in range(0, len(a) - 3))
However, if you consider elements that are being added and removed to the sum, you'll notice that most of the elements do not participate in the result, as they are being added and removed the same number of times.
For example, for indices in the range [0..15] here are indices that are added:
[2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15]
And these indices are subtracted:
[0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13]
You can see that there is overlap in the middle, and some difference at the start and at the end.
You can exploit that and only sum this difference:
s = -sum(a[:3]) - a[1] + sum(a[-3:]) + a[-2]
This adds last three elements (element before the last twice), and subtracts first three elements (second element twice). This yields the same result as the for loop.
Try this:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
fragments = [a[i:i + 4] for i in range(0, len(a), 4)]
result = sum([(f[2] + f[1]) - (f[0] + f[1]) for f in fragments])
print(result)
# 8
a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
sum = 0
for i in range(1,int(len(a)/4)+1 ):
sum = sum + ((a[i*4 -1] + a[i*4 -2]) - (a[i*4 -4] + a[i*4 -3]))
You can use the sum function and striding subscripts stepping by 4:
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
r = sum(a[2::4])+sum(a[3::4]) - sum(a[::4]) - sum(a[1::4])
print(r) #16
Each subscript pick up the following indexes:
Indexes: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
a[2::4) 3 7 11 15 sum=36
a[3::4] 4 8 12 16 sum=40 = 76
a[::4] 1 5 9 13 sum=28
a[1::4] 2 6 10 14 sum=32 - 60
----
16
alternatively you can subtract 2x the negative part from the whole sum
r = sum(a) - 2*( sum(a[::4]) + sum(a[1::4]) )
Another way to do it would be to add from a comprehension where you take the negative for the 1st/2nd of each group of 4
r = sum((x,-x)[i%4<2] for i,x in enumerate(a))
With numpy:
import numpy as np
# some sample data
np.random.seed(0)
a = np.random.randint(0, 10, 16)
print(a)
It gives:
[5 0 3 3 7 9 3 5 2 4 7 6 8 8 1 6]
Then compute differences between sums of consecutive numbers:
out = a[2:-1] + a[3:] - a[:-3] - a[1:-2]
print(out)
The result:
[ 1 7 10 2 -8 -5 -2 4 7 3 3 -5 -9]
Alternatively, this gives the same result:
out = np.convolve([1, 1, -1, -1], a, 'valid')

How to populate a 2d array?

I have the following code that (nearly) populates a list of lists (I will call it a 2d array) in Python.
Instead of going up from 0-6 and repeating this 3 times, I want it to populate the array with numbers 0 - 20. Please see code below:
#matrix=[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21]
#print(matrix[1][2])
rows=3
columns=7
for i in range(rows):
for j in range(columns):
matrix=[j]
i=i+1
print(matrix,end="")
The erroneous output is:
[0][1][2][3][4][5][6][0][1][2][3][4][5][6][0][1][2][3][4][5][6]
I want the output to be:
[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21]
There are fancier ways, but this is the most straightforward:
>>> rows = 3
>>> columns = 7
>>> n = 1
>>> matrix = []
>>> for _ in range(rows):
... sub = []
... for _ in range(columns):
... sub.append(n)
... n += 1
... matrix.append(sub)
...
>>> matrix
[[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21]]
And for good measure, a fancy way:
>>> import itertools
>>> counter = itertools.count(1)
>>> rows = 3
>>> columns = 7
>>> matrix = [[n for n, _ in zip(counter, range(columns))] for _ in range(rows)]
>>> matrix
[[1, 2, 3, 4, 5, 6, 7], [9, 10, 11, 12, 13, 14, 15], [17, 18, 19, 20, 21, 22, 23]]
>>>
Use list comprehension. You want 3 rows so the base of the list comprehension is: [for y in range(rows)]. You want to have incrementing numbers starting with a number divisible by columns but starting from 1 so: range(columns*y+1,...) and you want to have columns range (7) so range(columns*y+1,columns+(columns*y+1)) and then turn that into a list.
rows=3
columns=7
matrix=[list(range(columns*y+1,columns+(columns*y+1))) for y in range(rows)]
print(matrix)
#outputs: [[1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20, 21]]

Python: simple way to increment by alternating values?

I am building a list of integers that should increment by 2 alternating values.
For example, starting at 0 and alternating between 4 and 2 up to 20 would make:
[0,4,6,10,12,16,18]
range and xrange only accept a single integer for the increment value. What's the simplest way to do this?
I might use a simple itertools.cycle to cycle through the steps:
from itertools import cycle
def fancy_range(start, stop, steps=(1,)):
steps = cycle(steps)
val = start
while val < stop:
yield val
val += next(steps)
You'd call it like so:
>>> list(fancy_range(0, 20, (4, 2)))
[0, 4, 6, 10, 12, 16, 18]
The advantage here is that is scales to an arbitrary number of steps quite nicely (though I can't really think of a good use for that at the moment -- But perhaps you can).
You can use a list comprehension and the modulus operator to do clever things like that. For example:
>>> [3*i + i%2 for i in range(10)]
[0, 4, 6, 10, 12, 16, 18, 22, 24, 28]
l = []
a = 0
for i in xrnage (N) :
a += 2
if i&1 == 0 :
a+=2
l.append (a)
Looks simple enough to me.
This could be solution that is flexible and work for any range.
def custom_range(first, second, range_limit):
start , end = range_limit
step = first + second
a = range(start, end, step)
b = range(first, end, step)
from itertools import izip_longest
print [j for i in izip_longest(a,b) for j in i if j!=None]
custom_range(4,2,(0,19))
custom_range(6,5,(0,34))
Output:
[0, 4, 6, 10, 12, 16, 18]
[0, 6, 11, 17, 22, 28, 33]
1 Generate a range of numbers from 0 to n with step size 4
2 generate another range of numbers from 0 to n with step size 6
3 Combine both the list and sorted. Remove duplicates
>>> a = range(0,20,4)
>>> a
[0, 4, 8, 12, 16]
>>> b = range(0,20,6)
>>> c = sorted(a + b)
>>> b
[0, 6, 12, 18]
>>> c
[0, 0, 4, 6, 8, 12, 12, 16, 18]
>>> c = list(set(c))
>>> c
[0, 4, 6, 8, 12, 16, 18]

Given a bunch of ranges of numbers, get all the numbers within those ranges?

In Python, you can get the numbers in a range by calling range(x,y). But given two ranges, say 5-15, and 10-20 how can you get all the numbers 5-20 without duplicates? The ranges may also be disjoint.
I could concat all the results and then uniquify the list, but is that the fastest solution?
>>> a = range(5, 15)
>>> b = range(10, 20)
>>> print sorted(set(a + b))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Or if you want a more general expansion of the lists to their elements for inclusion in the set:
>>> list_of_lists = [a, b]
>>> print sorted(set(elem for l in list_of_lists for elem in l))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
And I found a way to compose it all in one line:
>>> list_of_lists = [a, b]
>>> print set.union(*map(set, list_of_lists))
Is sorting necessary? It's just expository, but then I don't see that sets necessarily output in sorted order:
>>> x = set(range(3))
>>> x
set([0, 1, 2])
>>> x.add(-1)
>>> x
set([0, 1, 2, -1])
Or you can join overlapping ranges:
>>> def join_overlapping_ranges(ranges):
... list_of_ranges = []
... # ranges are sorted on first element
... for r in sorted(ranges):
... # ranges are stored as [start, end]
... if list_of_ranges and list_of_ranges[-1][1] >= r[0]:
... list_of_ranges[-1][1] = r[1]
... else:
... list_of_ranges.append(r)
... return list_of_ranges
...
>>> ranges = [[3,4], [5,7], [1,2], [4,6], [5,5]]
>>> print sorted(ranges)
[[1, 2], [3, 4], [4, 6], [5, 5], [5, 7]]
>>> print join_overlapping_ranges(ranges)
[[1, 2], [3, 7]]
Sort the ranges (x, y) by increasing x values. Now, for each range, if it overlaps the previous range, set your current "big range"'s y value to the current range's y value. If it doesn't, start a new "big range": this one won't overlap any of the previous ones. If the current range is completely included in the current big one, ignore it.
For the quantity you need I would just keep it simple
>>> a = range(5, 15)
>>> b = range(10, 20)
>>> from itertools import chain
>>> sorted(set(chain.from_iterable(list_of_lists)))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

Unable to reverse lists in Python, getting "Nonetype" as list [duplicate]

This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Closed 6 months ago.
I have a .py file that takes a list, finds the lowest number, puts it into a new array, removes the lowest number from the first array, and repeats until the original array returns contains no more items:
def qSort(lsort):
listlength = len(lsort)
sortedlist = list()
if listlength == 0:
return lsort
else:
while listlength > 0:
lmin = min(lsort)
sortedlist.append(lmin)
lsort.remove(lmin)
listlength = len(lsort)
return sortedlist
Now another .py file imports the qSort and runs it on some list, saving it to a variable. Then I try to use the .reverse() command on the list and I end up getting it as a NoneType. I try to use reversed(), but all it does is say "<listreverseiterator object at 0xSomeRandomHex>":
from qSort import qSort #refer to my first Pastebin
qSort = qSort([5,42,66,1,24,5234,62])
print qSort #this prints the sorted list
print type(qSort) #this prints <type 'list'>
print qSort.reverse() #this prints None
print reversed(qSort) #this prints "<listreverseiterator object at 0xSomeRandomHex>"
Can anyone explain why I can't seem to reverse the list, no matter what I do?
As jcomeau mentions, the .reverse() function changes the list in place. It does not return the list, but rather leaves qSort altered.
If you want to 'return' the reversed list, so it can be used like you attempt in your example, you can do a slice with a direction of -1
So replace print qSort.reverse() with print qSort[::-1]
You should know slices, its useful stuff. I didn't really see a place in the tutorial where it was all described at once, (http://docs.python.org/tutorial/introduction.html#lists doesn't really cover everything) so hopefully here are some illustrative examples.
Syntax is: a[firstIndexInclusive:endIndexExclusive:Step]
>>> a = range(20)
>>> a
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> a[7:] #seventh term and forward
[7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> a[:11] #everything before the 11th term
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> a[::2] # even indexed terms. 0th, 2nd, etc
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> a[4:17]
[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
>>> a[4:17:2]
[4, 6, 8, 10, 12, 14, 16]
>>> a[::-1]
[19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> a[19:4:-5]
[19, 14, 9]
>>> a[1:4] = [100, 200, 300] #you can assign to slices too
>>> a
[0, 100, 200, 300, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
list.reverse() reverses in-place and returns nothing (None). so you don't say:
mylist = mylist.reverse()
you say:
mylist.reverse()
or alternatively:
mylist = list(reversed(mylist))
The reverse() list method sorts the list in place and returns None to remind you of that (according to note 7 in the documentation). The built-in reversed() function returns an iterator object, which can be turned into a list object by passing it to the list() constructor function like this: list(reversed(qSort)). You can accomplish the same thing by creating a slice with a step size of negative one so it goes backwards, i.e qSort[::-1].
BTW, list's also have a sort() method (but be careful, it also returns None ;-).
l5= [60,70,77]
myl2 = list(reversed(l5))
print(myl2)
or
mylist2 =[50,60,80,90]
mylist2.reverse()
print(mylist2)

Categories

Resources