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))
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:
Concatenating two range function results
(8 answers)
How can I generate a list of consecutive numbers? [duplicate]
(8 answers)
Closed 3 years ago.
in python is there a way to create of list that will skip numbers and will continue after skipping? something like the following code:
x = [1...3, 6...10]
print(x)
# [1,2,3,6,7,8,9,10]
Well its easy to write a for loop and then skip each defined index/value, or i can just use range, what I am looking for is a shorter more readable line. If not I can understand.
Simplest way to do this is to call range() and unpack result inside list assignment.
x = [*range(1, 4), *range(6, 11)]
Alternatively you can use itertools.chain:
>>> import itertools
>>> list(itertools.chain(range(1, 5), range(20, 25)))
[1, 2, 3, 4, 20, 21, 22, 23, 24]
If numpy is an option, you can use np.r_ to concatenate slice objects:
import numpy as np
np.r_[1:4, 6:11]
# array([ 1, 2, 3, 6, 7, 8, 9, 10])
You can turn it into a recursive function:
def recursive_ranges(ranges):
if len(ranges) == 1:
return list(range(*ranges[0]))
else:
return list(range(*ranges[0])) + recursive_ranges(ranges[1:])
You can then call this, specifying ranges as a list of lists:
ranges = [[1, 4], [6, 11]]
recursive_ranges(ranges)
# [1, 2, 3, 6, 7, 8, 9, 10]
Note the *ranges[0] is used to unpack the elements in ranges[0] into individual arguments. Essentially the recursive function keeps grabbing the first element of ranges, each element of which is a two-element array, and passing those numbers into the range() method as two different values instead of one array. That's what the * does, it unpacks the array. First call, you unpack [1, 4] into range(1, 4) and then append the next call of the recursive function to it.
Basically this unpacks into the following:
list(range(1, 4)) + list(range(6, 11))
but you get to use a much more compact syntax, just passing a list of lists.
This question already has answers here:
How can I use list comprehensions to process a nested list?
(13 answers)
Closed 27 days ago.
I want to understand nested list comprehension.
Below, I listed a list comprehension expression and their for loop equivalent.
I wonder if my understanding is correct on those.
For example,
[(min([row[i] for row in rows]),max([row[i] for row in rows]))
for i in range(len(rows[0]))]
is equivalent to
result=[]
for i in range(len(rows[0])):
innerResult=[]
for row in rows:
innerResult.append(row[i])
innerResult2=[]
for row in rows:
innerResult2.append(row[i])
tuple=(min(innerResult), max(innerResult2))
result.append(tuple)
If I may generalize, I guess
[exp2([exp1 for x in xSet]) for y in ySet]
form can be translated to the following. (I hope I'm correct on this)
result=[]
for y in ySet:
innerResult =[]
for x in xSet:
innerResult.append(exp1)
exp2Result = exp2(innerResult)
result.append(exp2Result)
For simpler case,
[exp1 for x in xSet for y in ySet]
is equal to
result=[]
for x in xSet:
for y in ySet:
result.append(exp1)
whereas,
[[exp1 for x in xSet] for y in ySet]
is equal to
result=[]
for y in ySet:
innerResult=[]
for x in xSet:
innerResult.append(exp1)
result.append(innerResult)
I asked a similar question on Equivalent for loop expression for complex list comprehension
The answers given there reconstruct the form after understanding what it does internally.
I'd like to know how it works systematically so I can apply the concept to other slightly varying examples.
Indeed, you are correct. This is described in detail in the Expressions section in the Python Language Reference.
Note especially the order of nesting of several fors in a single list comprehension, which is always left-to-right:
>>> matrix = [[1, 2], [3, 4]]
>>> [item for item in row for row in matrix] # oops!
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
[item for item in row for row in matrix]
NameError: name 'row' is not defined
>>> [item for row in matrix for item in row] # nesting is in left-to-right order
[1, 2, 3, 4]
The short answer is: yes, you are correct in your understanding.
There's only a catch: the way you normally use nested list comprehension in python code is to operate on multidimensional arrays.
A typical example is when you operate on matrices:
>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> [[el - 1 for el in row] for row in matrix]
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
As you can see the "nesting" works by operating on each dimension of the matrix.
In the examples you provided, it seems that ySet [unfortunate name btw, as sets are one of the types provided with python] is just a generic counter, which makes a bit harder to follow what is going on under the hood.
As for your first example:
>>> rows = ([1, 2, 3], [10, 20, 30])
>>> [(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
[(1, 10), (2, 20), (3, 30)]
You might wish to look into the zip built-in function:
>>> zip(rows[0], rows[1])
[(1, 10), (2, 20), (3, 30)]
or for maximum brevity and elegance:
>>> zip(*rows)
[(1, 10), (2, 20), (3, 30)]
HTH!
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.
This question already has answers here:
Expanding tuples into arguments
(5 answers)
Closed 6 months ago.
I want to zip the following list of lists:
>>> zip([[1,2], [3,4], [5,6]])
[[1,3,5], [2,4,6]]
This could be achieved with the current zip implementation only if the list is split into individual components:
>>> zip([1,2], [3,4], [5,6])
(1, 3, 5), (2, 4, 6)]
Can't figure out how to split the list and pass the individual elements to zip. A functional solution is preferred.
Try this:
>>> zip(*[[1,2], [3,4], [5,6]])
[(1, 3, 5), (2, 4, 6)]
See Unpacking Argument Lists:
The reverse situation occurs when the arguments are already in a list or tuple but need to be unpacked for a function call requiring separate positional arguments. For instance, the built-in range() function expects separate start and stop arguments. If they are not available separately, write the function call with the *-operator to unpack the arguments out of a list or tuple:
>>> range(3, 6) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # call with arguments unpacked from a list
[3, 4, 5]