range between two lists - python

Can anyone help me one this one?
I am trying to find a way to count the range between 2 list on integers; and to get each step necessary to get from one list to then next
using these 2 arrays:
a = [1,1,1]
b = [3,4,3]
I'd like to arrive to a sequence of in-between values:
[[2,2,2], [None,3,None]]
Thanks
a

This is quite simple to do with itertools.zip_longest() and a list comprehension:
>>> import itertools
>>> list(itertools.zip_longest(*[range(i+1, j) for i, j in zip(a, b)]))
[(2, 2, 2), (None, 3, None)]
Note that in 2.x itertools.zip_longest() doesn't exist - it's called itertools.izip_longest() instead.
This works by zip()ing the values together so we get the bounds, then we generate the range we need (adding one to the lower bound as you seem to not want to include it), then we separate them out into parts, using itertools.zip_longest() (which also introduces the None values).

A variation of Lattywares Answer that works in python 2.5 and below where izip_longest is not available:
map(None, *[range(x + 1, y) for x, y in zip(a, b)])

Related

Can I add an iterator to the reduce function?

Suppose I have the following list: A = [1,2,3,4], By using the reduce function, to find the product of the elements, I could do
prodA = reduce(lambda x,y: x*y, A)
However, if I have another list B=[9,8,7,6], is it still possible for me to use the reduce function to execute the following action? (from top to the bottom indicate the steps)
9
(9+1)* 2
((9+1) *2)+8
(((9+1) *2)+8)*3
((((9+1) *2)+8)*3)+7
(((((9+1) *2)+8)*3)+7)*4
((((((9+1) *2)+8)*3)+7)*4)+6
I'm not pretty sure if I could add something like an iterator for the list B to the reduce function. How I can do that? Thanks a lot!
This looks like a job for zip. Specifically, we're going to zip the two lists together, and then we'll express our reduction function as a function that takes tuples instead of simple integers.
Zipping our lists together gives us
>>> list(zip(A, B))
[(1, 9), (2, 8), (3, 7), (4, 6)]
And your function, at each step, multiplies by an element of A and then adds an element of B. So, starting with 1 (which is a sensible default since the first thing we do is multiply, so the 1 will be the identity for that first operation), multiply by the first element of the tuple and add the second.
reduce(lambda x, y: x * y[0] + y[1], zip(A, B), 1)
And, with your inputs, we get 370, which is equal to
((((9+1)*2)+8)*3+7)*4+6
You can do it using zip as the input and 1 as the initial value:
from functools import reduce
A = [1,2,3,4]
B = [9,8,7,6]
r = reduce(lambda r,ab: r*ab[0]+ab[1],zip(A,B),1)
print(r) # 370

Make simple graph with Tkinter [duplicate]

I would like to loop through a list checking each item against the one following it.
Is there a way I can loop through all but the last item using for x in y? I would prefer to do it without using indexes if I can.
Note
freespace answered my actual question, which is why I accepted the answer, but SilentGhost answered the question I should have asked.
Apologies for the confusion.
for x in y[:-1]
If y is a generator, then the above will not work.
the easiest way to compare the sequence item with the following:
for i, j in zip(a, a[1:]):
# compare i (the current) to j (the following)
If you want to get all the elements in the sequence pair wise, use this approach (the pairwise function is from the examples in the itertools module).
from itertools import tee, izip, chain
def pairwise(seq):
a,b = tee(seq)
b.next()
return izip(a,b)
for current_item, next_item in pairwise(y):
if compare(current_item, next_item):
# do what you have to do
If you need to compare the last value to some special value, chain that value to the end
for current, next_item in pairwise(chain(y, [None])):
if you meant comparing nth item with n+1 th item in the list you could also do with
>>> for i in range(len(list[:-1])):
... print list[i]>list[i+1]
note there is no hard coding going on there. This should be ok unless you feel otherwise.
To compare each item with the next one in an iterator without instantiating a list:
import itertools
it = (x for x in range(10))
data1, data2 = itertools.tee(it)
data2.next()
for a, b in itertools.izip(data1, data2):
print a, b
This answers what the OP should have asked, i.e. traverse a list comparing consecutive elements (excellent SilentGhost answer), yet generalized for any group (n-gram): 2, 3, ... n:
zip(*(l[start:] for start in range(0, n)))
Examples:
l = range(0, 4) # [0, 1, 2, 3]
list(zip(*(l[start:] for start in range(0, 2)))) # == [(0, 1), (1, 2), (2, 3)]
list(zip(*(l[start:] for start in range(0, 3)))) # == [(0, 1, 2), (1, 2, 3)]
list(zip(*(l[start:] for start in range(0, 4)))) # == [(0, 1, 2, 3)]
list(zip(*(l[start:] for start in range(0, 5)))) # == []
Explanations:
l[start:] generates a a list/generator starting from index start
*list or *generator: passes all elements to the enclosing function zip as if it was written zip(elem1, elem2, ...)
Note:
AFAIK, this code is as lazy as it can be. Not tested.

Two nested for loops

I dont know how to properly ask this question but here is what I am trying to do.
lists = []
for x in range(3):
for y in range(3):
if x!=y:
lists.append([x,y])
Is there a simple solution so it doesnt give me lists that are the same but reversed:
for example [2,0] and [0,2]?
I know I could go through the lists and remove them afterwards but is there a solution to not even make the list? (sorry my english isnt perfect)
You can use itertools.combinations
>>> from itertools import combinations
>>> list(combinations(range(3), 2))
[(0, 1), (0, 2), (1, 2)]
With the above example we take any combination of two elements from range(3) without repeating any elements.
Sure: if you add all pairs with y > x instead of all possible pairs, only one of each pair (x, y) and (y, x) will appear.
lists = []
for x in range(3):
for y in range(x + 1, 3):
lists.append([x,y])
If you don't want those "duplicates", you want a combination
a combination is a way of selecting items from a collection, such that (unlike permutations) the order of selection does not matter
>>> import itertools
>>> list(itertools.combinations(iterable=range(3), r=2))
[(0, 1), (0, 2), (1, 2)]
Above I have used combinations() from the Python module itertools.
Explanation
I've set r=2 because you want a subsequence length of 2 (the form you described as [x, y])
The iterable=range(3) parameter is just a list of elements that are going to be used to make combinations of, so range(3) would result in [0, 1, 2]
The list() applied to the end result is simply to force the output to be printed out to the console because otherwise itertools.combinations returns an iterable that you iterate through to pull the elements one by one.
Easy:
for x in range(3):
for y in range(x, 3):
lists.append([x,y])

good practice for string.partition in python

Sometime I write code like this:
a,temp,b = s.partition('-')
I just need to pick the first and 3rd elements. temp would never be used. Is there a better way to do this?
In other terms, is there a better way to pick distinct elements to make a new list?
For example, I want to make a new list using the elements 0,1,3,7 from the old list. The
code would be like this:
newlist = [oldlist[0],oldlist[1],oldlist[3],oldlist[7]]
It's pretty ugly, isn't it?
Be careful using
a, _, b = s.partition('-')
sometimes _ is use for internationalization (gettext), so you wouldn't want to accidentally overwrite it.
Usually I would do this for partition rather than creating a variable I don't need
a, b = s.partition('-')[::2]
and this in the general case
from operator import itemgetter
ig0137 = itemgetter(0, 1, 3, 7)
newlist = ig0137(oldlist)
The itemgetter is more efficient than a list comprehension if you are using it in a loop
For the first there's also this alternative:
a, b = s.partition('-')[::2]
For the latter, since there's no clear interval there is no way to do it too clean. But this might suit your needs:
newlist = [oldlist[k] for k in (0, 1, 3, 7)]
You can use Python's extended slicing feature to access a list periodically:
>>> a = range(10)
>>> # Pick every other element in a starting from a[1]
>>> b = a[1::2]
>>> print b
>>> [1, 3, 5, 7, 9]
Negative indexing works as you'd expect:
>>> c = a[-1::-2]
>>> print c
>>> [9, 7, 5, 3, 1]
For your case,
>>> a, b = s.partition('-')[::2]
the common practice in Python to pick 1st and 3rd values is:
a, _, b = s.partition('-')
And to pick specified elements in a list you can do :
newlist = [oldlist[k] for k in (0, 1, 3, 7)]
If you don't need to retain the middle field you can use split (and similarly rsplit) with the optional maxsplit parameter to limit the splits to the first (or last) match of the separator:
a, b = s.split('-', 1)
This avoids a throwaway temporary or additional slicing.
The only caveat is that with split, unlike partition, the original string is returned if the separator is not found. The attempt to unpack will fail as a result. The partition method always returns a 3-tuple.

Python list comprehensions: Is it possible to specify the inclusion of two elements at a time?

Up to this point in time, in Python, I've only ever seen list comprehensions that specify the inclusion of one element at time. For example, they're all in the following form
[f(x) for x in <iterable>]
Is there any way to specify more than one element at a time? For example,
[f(x), g(x) for x in <iterable>]
I'm asking because I want to create a list comprehension that calculates all the divisors of some number x, but I want to do this as efficiently as possible. Right now I'm using the following,
[y for y in range(1,x+1) if x%y == 0]
but I'd like to do something like this,
[y, x/y for y in range(1, sqrt(x)+1) if x%y == 0]
as this would be more efficient. Btw, I have no practical reason for doing this. It's simply a challenge problem that somebody told me and the goal is to do it with the smallest, most efficient list comprehension possible.
Thanks in advance.
Edit: Ok, it looks like I have to use tuples. I was trying to avoid that though as I'd then have to make another function to flatten the list which would make my solution longer.
Edit 2: Just in case anyone stumbles upon this question, the answer to what I wanted to do in my original question is this:
[y for x in <iterable> for y in f(x), g(x)]
It uses nested for loops in the list comprehension to get the job done.
You can flatten it in place
[y if i else x/y for y in range(1, sqrt(x)+1) for i in 0,1 if x%y == 0]
You can assign to tuples
[(y, x/y) for y in range(1, int(sqrt(x))+1) if x%y == 0]
Not really related to your basic question, but your example: I had to convert the 2nd parameter of range() to an int since sqrt() resulted in a float in my test code.
Update re Edit in post:
To flatten this list of tuples:
In [24]: s
Out[24]: [(1, 20), (2, 10), (4, 5)]
use this:
In [25]: import operator
create a tuple:
In [26]: reduce(operator.add, s, ())
Out[26]: (1, 20, 2, 10, 4, 5)
create a list:
In [27]: list(reduce(operator.add, s, ()))
Out[27]: [1, 20, 2, 10, 4, 5]
Note: In a helpful comment #jamylak points out that reduce and operator.add run order O(N^2), and that using itertools.chain would be much more efficient. This becomes more important as the size of the list grows and should be considered in that case.
Yes, you can do it. You just need to put parentheses around the element. What you get is a list of tuples.
>>> [(x, x+1) for x in range(5)]
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
Oh so close:
[(y, x/y) for y in range(1, sqrt(x)+1) if x%y == 0]
It is possible to generate a list of tuples, and these of course can hold multiple values.
Yes absolutely, just use parenthesis around the value:
[(y, x/y) for y in range(1, sqrt(x)+1) if x%y == 0]

Categories