Zip two int lists with all possible sign combinations - python

Say I have two lists of ints:
a = [1,2]
b = [3,4]
And I want to end up with a list of tuples like this:
[(1,3),(2,4),(-1,-3),(-2,-4),(1,-3),(2,-4),(-1,3),(-2,4)]
That is, I'm zipping the nth element of a with the nth element of b, but using all possible positive/negative sign combinations. What's the most elegant way to do this?
A not elegant way would be to create two further lists like this
a_neg = [-n for n in a]
b_neg = [-n for n in b]
then zip a with b, a_neg with b_neg, a with b_neg, and a_neg with b, and combine the results. This works fine but is unsatisfying. Is there a simpler way to do this?

I wouldn't try to be fancy with list comprehensions and just use a for loop
for idx, i in enumerate(a):
c.extend([(i, b[idx]), (-i, b[idx]), (i, -b[idx]), (-i, -b[idx])])
>>> c
[(1, 3), (-1, 3), (1, -3), (-1, -3), (2, 4), (-2, 4), (2, -4), (-2, -4)]
Or using a zip
for x,y in zip(a,b):
c.extend([(x,y), (-x, y), (x,-y), (-x, -y)])

Use this answer and just apply it across the zipped sequence, flattening the result into a single list.
from itertools import product
def sign_combinations(nums):
return product(*((x, -x) for x in nums))
a = [1,2]
b = [3,4]
print([c for t in zip(a, b) for c in sign_combinations(t)])

Related

Generating 2D grid indices using list comprehension

I would like to store the row and column indices of a 3 x 3 list in list. It should look like the following:
rc = [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)]
How can I get this list using a list comprehension in Python?
Some consider multiple for loops inside a list comprehension to be poor style. Use itertools.product() instead:
from itertools import product
list(product(range(3), repeat=2))
This outputs:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
How about:
[(x, y) for x in range(3) for y in range(3)]
If I understand correctly, and the input is A, the B can be output as follows:
A = [[1,2,3],[4,5,6],[7,8,9]]
B =[(A[i], A[j]) for i in range(3) for j in range(3)]

Remove bracket in a list of tuples in tuples

I would like to remove the bracket of a tuples who belong to tuples within a list:
Here's an example:
List_1 = [(0, (1, 1)),
(0, (1, 2)),
(0, (1, 3))]
And the expected output should look like this:
list_2 = [(0, 1, 1),
(0, 1, 2),
(0, 1, 3)]
I tried this:
for element in List_n_d1d2:
newlist = (element[0], element[1])
But ended up with the same output... Could you please help me, thank you !
Use the * spread operator to expand the tuple into separate elements.
list_2 = [(a, *b) for a, b in list_1]
A simple comprehension would be:
[(a,) + b for a,b in List_1]

How to nest tuples in Python

I am very new at Python. I am trying to do a tuple that contains tuples in the form (1,(1,(1,'a'))). I am not allowed to use any functions. I have written a list and for each element of the list I want to take tuple.
I want to take something like (2,(3,(4,'name'))) and the result I take is (2,3,4,'name').
b = [6,5,4,3,2,1]
for i in range(len(b)):
mytuple = (i,'name')
print(i)
mytuple = (b[2],)+mytuple
print(mytuple)
Iterate reverse on b:
t="name"
for i in range(-1,-len(b)-1,-1):
t=(b[i],t)
Out: (6, (5, (4, (3, (2, (1, 'name'))))))

Subtract two lists of tuples from each other

I have the these two lists and I need to subtract one from the other but the regular "-" won't work, neither will .intersection or XOR (^).
A = [(0, 1)]
B = [(0, 0), (0,1), (0, 2)]
Essentially what I want is:
B - A = [(0, 0), (0, 2)]
You can use list comprehension to solve this problem:
[item for item in B if item not in A]
More discussion can be found here
If there are no duplicate tuples in B and A, might be better to keep them as sets, and use the difference of sets:
A = [(0, 1)]
B = [(0, 0), (0,1), (0, 2)]
diff = set(B) - set(A) # or set(B).difference(A)
print(diff)
# {(0, 0), (0, 2)}
You could perform other operations like find the intersection between both sets:
>>> set(B) & set(A)
{(0, 1)}
Or even take their symmetric_difference:
>>> set(B) ^ set(A)
{(0, 0), (0, 2)}
You can do such operations by converting the lists to sets. Set difference:
r = set(B)-set(A)
Convert to list if necessary: list(r)
Working on sets is efficient compared to running "in" operations on lists: using lists vs sets for list differences

Pairwise circular Python 'for' loop

Is there a nice Pythonic way to loop over a list, retuning a pair of elements? The last element should be paired with the first.
So for instance, if I have the list [1, 2, 3], I would like to get the following pairs:
1 - 2
2 - 3
3 - 1
A Pythonic way to access a list pairwise is: zip(L, L[1:]). To connect the last item to the first one:
>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]
I would use a deque with zip to achieve this.
>>> from collections import deque
>>>
>>> l = [1,2,3]
>>> d = deque(l)
>>> d.rotate(-1)
>>> zip(l, d)
[(1, 2), (2, 3), (3, 1)]
I'd use a slight modification to the pairwise recipe from the itertools documentation:
def pairwise_circle(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ... (s<last>,s0)"
a, b = itertools.tee(iterable)
first_value = next(b, None)
return itertools.zip_longest(a, b,fillvalue=first_value)
This will simply keep a reference to the first value and when the second iterator is exhausted, zip_longest will fill the last place with the first value.
(Also note that it works with iterators like generators as well as iterables like lists/tuples.)
Note that #Barry's solution is very similar to this but a bit easier to understand in my opinion and easier to extend beyond one element.
I would pair itertools.cycle with zip:
import itertools
def circular_pairwise(l):
second = itertools.cycle(l)
next(second)
return zip(l, second)
cycle returns an iterable that yields the values of its argument in order, looping from the last value to the first.
We skip the first value, so it starts at position 1 (rather than 0).
Next, we zip it with the original, unmutated list. zip is good, because it stops when any of its argument iterables are exhausted.
Doing it this way avoids the creation of any intermediate lists: cycle holds a reference to the original, but doesn't copy it. zip operates in the same way.
It's important to note that this will break if the input is an iterator, such as a file, (or a map or zip in python-3), as advancing in one place (through next(second)) will automatically advance the iterator in all the others. This is easily solved using itertools.tee, which produces two independently operating iterators over the original iterable:
def circular_pairwise(it):
first, snd = itertools.tee(it)
second = itertools.cycle(snd)
next(second)
return zip(first, second)
tee can use large amounts of additional storage, for example, if one of the returned iterators is used up before the other is touched, but as we only ever have one step difference, the additional storage is minimal.
There are more efficient ways (that don't built temporary lists), but I think this is the most concise:
> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]
Pairwise circular Python 'for' loop
If you like the accepted answer,
zip(L, L[1:] + L[:1])
you can go much more memory light with semantically the same code using itertools:
from itertools import islice, chain #, izip as zip # uncomment if Python 2
And this barely materializes anything in memory beyond the original list (assuming the list is relatively large):
zip(l, chain(islice(l, 1, None), islice(l, None, 1)))
To use, just consume (for example, with a list):
>>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
[(1, 2), (2, 3), (3, 1)]
This can be made extensible to any width:
def cyclical_window(l, width=2):
return zip(*[chain(islice(l, i, None), islice(l, None, i)) for i in range(width)])
and usage:
>>> l = [1, 2, 3, 4, 5]
>>> cyclical_window(l)
<itertools.izip object at 0x112E7D28>
>>> list(cyclical_window(l))
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
>>> list(cyclical_window(l, 4))
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 1), (4, 5, 1, 2), (5, 1, 2, 3)]
Unlimited generation with itertools.tee with cycle
You can also use tee to avoid making a redundant cycle object:
from itertools import cycle, tee
ic1, ic2 = tee(cycle(l))
next(ic2) # must still queue up the next item
and now:
>>> [(next(ic1), next(ic2)) for _ in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
This is incredibly efficient, an expected usage of iter with next, and elegant usage of cycle, tee, and zip.
Don't pass cycle directly to list unless you have saved your work and have time for your computer to creep to a halt as you max out its memory - if you're lucky, after a while your OS will kill the process before it crashes your computer.
Pure Python Builtin Functions
Finally, no standard lib imports, but this only works for up to the length of original list (IndexError otherwise.)
>>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
[(1, 2), (2, 3), (3, 1)]
You can continue this with modulo:
>>> len_l = len(l)
>>> [(l[i % len_l], l[(i + 1) % len_l]) for i in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
I would use a list comprehension, and take advantage of the fact that l[-1] is the last element.
>>> l = [1,2,3]
>>> [(l[i-1],l[i]) for i in range(len(l))]
[(3, 1), (1, 2), (2, 3)]
You don't need a temporary list that way.
Amazing how many different ways there are to solve this problem.
Here's one more. You can use the pairwise recipe but instead of zipping with b, chain it with the first element that you already popped off. Don't need to cycle when we just need a single extra value:
from itertools import chain, izip, tee
def pairwise_circle(iterable):
a, b = tee(iterable)
first = next(b, None)
return izip(a, chain(b, (first,)))
I like a solution that does not modify the original list and does not copy the list to temporary storage:
def circular(a_list):
for index in range(len(a_list) - 1):
yield a_list[index], a_list[index + 1]
yield a_list[-1], a_list[0]
for x in circular([1, 2, 3]):
print x
Output:
(1, 2)
(2, 3)
(3, 1)
I can imagine this being used on some very large in-memory data.
This one will work even if the list l has consumed most of the system's memory. (If something guarantees this case to be impossible, then zip as posted by chepner is fine)
l.append( l[0] )
for i in range( len(l)-1):
pair = l[i],l[i+1]
# stuff involving pair
del l[-1]
or more generalizably (works for any offset n i.e. l[ (i+n)%len(l) ] )
for i in range( len(l)):
pair = l[i], l[ (i+1)%len(l) ]
# stuff
provided you are on a system with decently fast modulo division (i.e. not some pea-brained embedded system).
There seems to be a often-held belief that indexing a list with an integer subscript is un-pythonic and best avoided. Why?
This is my solution, and it looks Pythonic enough to me:
l = [1,2,3]
for n,v in enumerate(l):
try:
print(v,l[n+1])
except IndexError:
print(v,l[0])
prints:
1 2
2 3
3 1
The generator function version:
def f(iterable):
for n,v in enumerate(iterable):
try:
yield(v,iterable[n+1])
except IndexError:
yield(v,iterable[0])
>>> list(f([1,2,3]))
[(1, 2), (2, 3), (3, 1)]
How about this?
li = li+[li[0]]
pairwise = [(li[i],li[i+1]) for i in range(len(li)-1)]
from itertools import izip, chain, islice
itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))
(As above with #j-f-sebastian's "zip" answer, but using itertools.)
NB: EDITED given helpful nudge from #200_success. previously was:
itr = izip(l, chain(l[1:], l[:1]))
If you don't want to consume too much memory, you can try my solution:
[(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]
It's a little slower, but consume less memory.
Starting in Python 3.10, the new pairwise function provides a way to create sliding pairs of consecutive elements:
from itertools import pairwise
# l = [1, 2, 3]
list(pairwise(l + l[:1]))
# [(1, 2), (2, 3), (3, 1)]
or simply pairwise(l + l[:1]) if you don't need the result as a list.
Note that we pairwise on the list appended with its head (l + l[:1]) so that rolling pairs are circular (i.e. so that we also include the (3, 1) pair):
list(pairwise(l)) # [(1, 2), (2, 3)]
l + l[:1] # [1, 2, 3, 1]
Just another try
>>> L = [1,2,3]
>>> zip(L,L[1:]) + [(L[-1],L[0])]
[(1, 2), (2, 3), (3, 1)]
L = [1, 2, 3]
a = zip(L, L[1:]+L[:1])
for i in a:
b = list(i)
print b
this seems like combinations would do the job.
from itertools import combinations
x=combinations([1,2,3],2)
this would yield a generator. this can then be iterated over as such
for i in x:
print i
the results would look something like
(1, 2)
(1, 3)
(2, 3)

Categories

Resources