Let's see if I can explain what I am looking for. I want unwrap a list according to those elements which are a list in a list (tweaky, I know!).
I want this:
a = [[1],[2],[5],[4,6],[3]]
to be transform to this:
b = [[[1],[2],[5],[4],[3]],[[1],[2],[5],[6],[3]]]
And also applied to:
a = [[1],[2,3],[5],[4,6],[3]]
for achieving:
b = [[[1],[2],[5],[4],[3]],[[1],[2],[5],[6],[3]],[[1],[3],[5],[4],[3]],[[1],[3],[5],[6],[3]]]
I hope I have expressed propertly. I have been looking for some buil-in function that performs this operation but I have find nothing.
Thanks you in advance
You're looking for itertools.product:
>>> import itertools
>>> a = [[1],[2,3],[5],[4,6],[3]]
>>> for tup in itertools.product(*a):
... print tup
...
(1, 2, 5, 4, 3)
(1, 2, 5, 6, 3)
(1, 3, 5, 4, 3)
(1, 3, 5, 6, 3)
You can convert the tuples to lists and wrap the elements in 1-element lists if you really want to.
Related
I am starting on my Python journey and am doing some exercises to get the hang of it all. One question is really giving me troubles as I do not understand how to complete it.
Given a list with an even number of integers, join adjacent elements using '-' and print each pair.
So it will be that this is given:
a = [1, 2, 3, 4, 5, 6, 7, 8]
and the output needs to be:
1-2
3-4
5-6
7-8
Now I have gotten as far as this, but have no idea what to do next:
a = [1, 2, 3, 4, 5, 6, 7, 8]
a1 = a[::2]
a2 = a[1::2]
duos = zip(a1, a2)
print(list(duos))
And this only gives me this as result:
[(1, 2), (3, 4), (5, 6), (7, 8)]
I feel like I am close and just missing one tiny step.
Build a lazy iterator:
>>> a = [1, 2, 3, 4, 5, 6, 7, 8]
>>> it = iter(a)
>>> print([f"{x}-{y}" for x,y in zip(it,it)])
['1-2', '3-4', '5-6', '7-8']
Yep, very close indeed.
You can use a generator expression to form the pair strings without the intermediate variables, then "\n".join to make a single string out of the formatted pairs.
>>> numbers = [1, 2, 3, 4, 5, 6, 7, 8]
>>> print("\n".join(f"{a}-{b}" for (a, b) in zip(numbers[::2], numbers[1::2])))
1-2
3-4
5-6
7-8
The more procedural version (that's functionally equivalent, but doesn't form a list, but just prints each pair) would be
for (a, b) in zip(numbers[::2], numbers[1::2]):
print(f"{a}-{b}")
Completing your work:
for x, y in duos:
print(f'{x}-{y}')
(Note you need to do this instead of your print(list(duos)), otherwise that consumes the zip iterator and there's nothing left.)
You're indeed very close. Now just print each pair in duos on a separate line with a dash as separator:
for a,b in duos: print(a,b,sep="-")
Or you could do it all in one line using a combination of map, zip and join:
print(*map("-".join,zip(*[map(str,a)]*2)),sep="\n")
A somewhat fun[*] alternative, easily adapts to similar cases just by altering the string of ends:
from itertools import cycle
a = [1, 2, 3, 4, 5, 6, 7, 8]
for x, end in zip(a, cycle('-\n')):
print(x, end=end)
For example with cycle('+-*\n'), it would instead print this:
1+2-3*4
5+6-7*8
[*] Everything itertools is fun for me :-)
This question already has answers here:
Zip lists in Python
(10 answers)
Closed 2 years ago.
My input is
tbl_ports = [[1,2,3,4], [5,6,7,8], [9,10,11,12]]
And my expected output is
[[1,5,9], [2,6,10], [3,7,11], [4,8,12]]
My limit was to do the following to create the output reorder_list
reorder_list = []
for i in range(len(tbl_ports)):
for col in tbl_ports:
reorder_list.append(col[i])
reorder_list=[1, 5, 9, 2, 6, 10, 3, 7, 11]
How can I contain them in a list of 3 elements?
To fix the code that you already have, you need to create a new list every time a row is complete, such as:
reorder_list = []
for i in range(len(tbl_ports)):
reorder_list.append([])
for col in tbl_ports:
reorder_list[-1].append(col[i])
Which would yield the following result:
[[1, 5, 9], [2, 6, 10], [3, 7, 11]]
You can also use a more pythonic method of solving the problem,
list(zip(*tbl_port))
Which would yield a list of tuples:
[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
If you want a list of lists, then you can simply just use list comprehension:
[list(e) for e in zip(*tbl_port)]
Edit:
for an explanation of why zip(*list) works, you need to know what zip does.
zip is a function in python that takes in multiple lists, and outputs a generator of lists for each element in every corresponding list. So zip([1, 2, 3], [4, 5, 6]) would yield [(1, 4), (2, 5), (3, 6)].
the * basically expands the input into multiple positional arguments, where function(*[1, 2, 3, 4]) is equivalent to function(1, 2, 3, 4)
So the code passed in the input array as a list of arguments to the zip function, which then outputs the result in the order that you want.
The only remaining problem is that zip generates a generator instead of an actual list.
To solve that problem, simply call the list function on a generator to convert it into a list, or pass it in a list comprehension to yield the desired result.
This is exactly what the zip() function is for.
list(zip([1,2,3,4],'abcd'))
You can use the unpack syntax " * " to make python unpack your lists to the zip function.
tbl_ports = [[1,2,3,4], [5,6,7,8], [9,10,11,12]]
reorder_list = list(zip(*tbl_ports))
I have a question that I haven't quite found a good solution to. I'm looking for a better way to append function output to two or more lists, without using temp variables. Example below:
def f():
return 5,6
a,b = [], []
for i in range(10):
tmp_a, tmp_b = f()
a.append(tmp_a)
b.append(temp_b)
I've tried playing around with something like zip(*f()), but haven't quite found a solution that way.
Any way to remove those temp vars would be super helpful though, thanks!
Edit for additional info:
In this situation, the number of outputs from the function will always equal the number of lists that are being appended to. The main reason I'm looking to get rid of temps is for the case where there are maybe 8-10 function outputs, and having that many temp variables would get messy (though I don't really even like having two).
def f():
return 5,6
a,b = zip(*[f() for i in range(10)])
# this will create two tuples of elements 5 and 6 you can change
# them to list by type casting it like list(a), list(b)
First solution: we make a list of all results, then transpose it
def f(i):
return i, 2*i
# First make a list of all your results
l = [f(i) for i in range(5)]
# [(0, 0), (1, 2), (2, 4), (3, 6), (4, 8)]
# then transpose it using zip
a, b = zip(*l)
print(list(a))
print(list(b))
# [0, 1, 2, 3, 4]
# [0, 2, 4, 6, 8]
Or, all in one line:
a, b = zip(*[f(i) for i in range(5)])
A different solution, building the lists at each iteration, so that you can use them while they're being built:
def f(i):
return 2*i, i**2, i**3
doubles = []
squares = []
cubes = []
results = [doubles, squares, cubes]
for i in range(1, 4):
list(map(lambda res, val: res.append(val), results, f(i)))
print(results)
# [[2], [1], [1]]
# [[2, 4], [1, 4], [1, 8]]
# [[2, 4, 6], [1, 4, 9], [1, 8, 27]]
print(cubes)
# [1, 8, 27]
Note about list(map(...)): in Python3, map returns a generator, so we must use it if we want the lambda to be executed.list does it.
For your specific case, the zip answers are great.
Using itertools.cycle and itertools.chain is a different approach from the existing answers that might come in handy if you have a lot of pre-existing lists that you want to append to in a round-robin fashion. It also works when your function returns more values than you have lists.
>>> from itertools import cycle, chain
>>> a, b = [], [] # new, empty lists only for demo purposes
>>> for l, v in zip(cycle([a, b]), (chain(*(f() for i in range(10))))):
... l.append(v)
...
>>> a
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
>>> b
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6]
I'd do
tmp = f()
a.append(tmp[0])
b.append(tmp[1])
Not sure how pythonic it is for you though.
I want to ask if something like this is possible in python:
a,b = [i,i+1 for i in range(5)]
I know this isn't possible because I have got an error, but I think you understand what I am trying to achieve. Let me clear it up, I can do :
a,b = 3+2,3
Edit ---> Or even better:
a,b = [0,1,2,3,4],[1,2,3,4,5]
I wan't a similar thing in my first code example. I am trying to assign variables 'a' and 'b' as list, with list comprehension, but using tuple as assignment, the point is I don't want to use this:
a = [i for in range(5)]
b = [i+1 for in range(5)]
I am aware that I can use this: t = [(i,i+1) for i in range(5)], but that's not the point.
By the way this is only a simple example => "i,i+1"
Edit ---> I would like to clarify my question. How to assign several variables (type list) in one line, using list comprehension?
When you run this:
a,b = [(i,i+1) for i in range(5)] # wrapped i, i+1 in parentheses (syntax error)
It makes a list of five two-item tuples, like this:
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
But you're trying to assign those five tuples to only two objects (a and b)
Using argument unpacking (*) in zip, you can "unzip" the output to the first and second elements of each tuple:
a,b = zip(*[(i,i+1) for i in range(5)])
Which is this:
[(0, 1, 2, 3, 4), (1, 2, 3, 4, 5)]
And can be assigned to a and b as you've written
Don't try to be clever. This is perfectly acceptable code:
>>> a = range(5)
>>> b = range(1,6)
>>> a, b
([0, 1, 2, 3, 4], [1, 2, 3, 4, 5])
I have a list of tuples:
l=[(1,2,3),(4,5,6)]
The list can be of arbitrary length, as can the tuples. I'd like to convert this into a list or tuple of the elements, in the order they appear:
f=[1,2,3,4,5,6] # or (1,2,3,4,5,6)
If I know the at development time how many tuples I'll get back, I could just add them:
m = l[0] + l[1] # (1,2,3,4,5,6)
But since I don't know until runtime how many tuples I'll have, I can't do that. I feel like there's a way to use map to do this, but I can't figure it out. I can iterate over the tuples and add them to an accumulator, but that would create lots of intermediate tuples that would never be used. I could also iterate over the tuples, then the elements of the tuples, and append them to a list. This seems very inefficient. Maybe there's an even easier way that I'm totally glossing over. Any thoughts?
Chain them (only creates a generator instead of reserving extra memory):
>>> from itertools import chain
>>> l = [(1,2,3),(4,5,6)]
>>> list(chain.from_iterable(l))
[1, 2, 3, 4, 5, 6]
l = [(1, 2), (3, 4), (5, 6)]
print sum(l, ()) # (1, 2, 3, 4, 5, 6)
reduce(tuple.__add__, [(1,2,3),(4,5,6)])
tuple(i for x in l for i in x) # (1, 2, 3, 4, 5, 6)
Use the pythonic generator style for all of the following:
b=[(1,2,3),(4,5,6)]
list = [ x for x in i for i in b ] #produces a list
gen = ( x for x in i for i in b ) #produces a generator
tup = tuple( x for x in i for i in b ) #produces a tuple
print list
>> [1, 2, 3, 4, 5, 6]
>>> from itertools import chain
>>> l = [(1,2,3),(4,5,6)]
>>> list(chain(*l))
[1, 2, 3, 4, 5, 6]
You can combine the values in a list using the .extend() function like this:
l = [(1,2,3), (4,5,6)]
m = []
for t in l:
m.extend(t)
or a shorter version using reduce:
l = [(1,2,3), (4,5,6)]
m = reduce(lambda x,y: x+list(y), l, [])