This question already has answers here:
Explanation of how nested list comprehension works?
(11 answers)
Closed 5 years ago.
I'm having trouble understanding the syntax here.
matrix_a = [[1, 2], [3, 4], [5, 6]]
matrix_b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[a for a, b in matrix_a]
output: [1, 3, 5]
[a for b, a in matrix_a]
output: [2, 4, 6]
I understand a little about how list-comprehensions work, but I don't understand the syntax when accessing certain elements within a nested list.
I just can't wrap my head around this syntax. How is this syntax working? What does the comma represent? What does a for a mean? Can you explain whats going on under the hood? And finally how would you do this with matrix_b
If you convert it to a for loop it might be easier to see..?
res = []
for item in matrix_a:
a, b = item # a = item[0]; b = item[1]
res.append(a)
you're basically unpacking the individual items in the list and picking one of them.
I think you have mistaken in writing output here
[a for a, b in matrix_a] returns [1 2 4] which is logical , returning first element of each nested list item
see the screenshot
just understand in this way:
[a for b, a in matrix_a] #as
[a for [a, b] in matrix_a] #or
[a for (a, b) in matrix_a]
#matrix_a has elements as list of length 2 each
#so your list comprehenssion says, give me a output list -- having first element'a' from each element of matrix_a(whose each element represented as [a,b] type, with comma, becomes tuple (a,b)),
# then from [b,a] give me a, that is 2nd element
# and it will fail if you apply same on matrix_b, cause each element of matrix_b is not of type [a,b] i:e of length 2
# you will get "ValueError: too many values to unpack"
Let me know if anything is not clear. Thanks.
Related
Let's say I have:
a=[1,2,3]
b=[4,5,6]
Now I want to create a list of list from a and b, I would do it like this:
c=[a,b]=[[1,2,3],[4,5,6]]
As a.append(b) would result in: [1,2,3,b]=[1,2,3,[4,5,6]]
Now let's say there exists anew list which I want to append to c:
d=[7,8,9]
I now have to do c.append(d) to get [[1,2,3],[4,5,6],[7,8,9]]
Because
e=[c,d]=[[[1,2,3],[4,5,6]],[7,8,9]]
How can I get a list of list from individual lists without know how my lists are structured?
Try this:
a = [1,2,3]
b = [4,5,6]
c = []
c.append(a)
c.append(b)
This should work, and only takes 2 simple lines.
The two actions you are describing are distinctly different. The first is creating the outer list (a.k.a. c) and the second is appending to it.
To make the process more uniform you can just start off with an outer list and append all the child lists to it.
c = []
a=[1,2,3]
b=[4,5,6]
d=[7,8,9]
c.append(a)
c.append(b)
c.append(d)
c is now
[[[1,2,3],[4,5,6]],[7,8,9]]
A bit roundabout of a way but looks nice, using numpy
a = np.array([[1,2,3]])
b = np.array([[4,5,6]])
c = np.append(a,b,axis=0)
print(c.tolist())
gives you
[[1,2,3],[4,5,6]]
Appending another list in the same way keeps the structure of list of lists, for example
d = np.array([[7,8,9]])
e = np.append(c,d,axis=0)
print(e.tolist())
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Now this is quite roundabout. I would just keep everything in numpy arrays if possible.
EDIT: Figured out how to do this without numpy
Simply state each list as a list of lists to begin with
a = [[1,2,3]]
b = [[4,5,6]]
a.extend(b)
print(a)
[[1,2,3],[4,5,6]]
Furthermore you can do this
d = [[7,8,9]]
a.extend(d)
print(a)
[[1, 2, 3], [4, 5, 6], [4, 5, 6]]
Program description:
Program accepts a list l containing other lists. Output l where lists with length greater than 3 will be changed accordingly: the element with index 3 is going to be a sum of removed elements (from third to the end).
My solution:
l = [[1,2], [3,4,4,3,1], [4,1,4,5]]
s = 0
for i in range(len(l)-1):
if len(l[i]) > 3:
for j in range(3,len(l[i])-1):
s += l[i][j]
l[i].remove(l[i][j])
l[i].insert(len(l[i]),s)
l
Test:
Input: [[1,2], [3,4,4,3,1], [4,1,4,5]]
Expected Output: [[1, 2], [3, 4, 8], [4, 1, 9]]
Program run:
Input: [[1,2], [3,4,4,3,1], [4,1,4,5]]
Output: [[1, 2], [4, 4, 3, 1, 3], [4, 1, 4, 5]]
Question: I don't understand what can be the source of the problem in this case, why should it add some additional numbers to the end, instead of summ. I will appreciate any help.
remove is the wrong function. You should use del instead. Read the documentation to understand why.
And another bug you have is that you do not reset s. It should be set to 0 in the outer for loop.
But you're making it too complicated. I think it's better to show how you can do it really easy.
for e in l: # No need for range. Just iterate over each element
if len(e) > 3:
e[2]=sum(e[2:]) # Sum all the elements
del(e[3:]) # And remove
Or if you want it as a list comprehension that creates a new list and does not alter the old:
[e[0:2] + [sum(e[2:])] if len(e)>3 else e for e in l]
First of all, remove() is the wrong method, as it deletes by value, not index:
Python list method remove() searches for the given element in the list
and removes the first matching element.
You'd want to use del or pop().
Second of all, you're not slicing all of the elements from the end of the list, but only one value.
remove is reason why your code is not working. (as mentioned by Mat-KH in the other answer)
You can use list comprehension and lambda function to make it a two liner.
func = lambda x: x if len(x) < 3 else x[:2] + [sum(x[2:])]
l = [func(x) for x in l]
This question already has answers here:
Get unique values from a list in python [duplicate]
(30 answers)
Closed 6 years ago.
I have recently started trying to learn Python, and I try to improve the way I write code, and make it more "Pythonic".
Therefore, it would really be nice if someone could explain to me if the following can be formulated more elegantly.
Hopefully this is not a duplicate (I checked, but you never know)
I have a list of 5 elements, and I want to return specific elements.
Let's say for example that I have [1, 2, 3, 3, 4].
I already have a function double(list), that if a list element exists twice returns that element (in this case 3).
I would like to generate from this list a tuple that contains the numbers that are exist only once (1, 2, 4).
One option is the following:
Run the double(list) function and get the value of the element that is double.
Create an empty tuple
Iterate over the list items, and if the value is not equal to what the double(list) function returned, add it to the tuple
Return the tuple.
My question is: is there a more elegant/Pythonic way of doing this?
(in one line, using maybe a more complex expression?)
Thanks in advance
The general way to do this is to make a set out of the elements and then count them, or just use collections.Counter, then go through the list and include only the appropriate elements, by either creating an empty list and then adding to it with a traditional loop or by using a comprehension with a filter.
>>> import collections
>>> l = [1, 2, 3, 3, 4]
>>> c = collections.Counter(l)
>>> new_list = [item for item in l if c[item] < 2]
>>> new_list
[1, 2, 4]
Since you want a single-line solution (well except for the actual list declaration of course :) ):
your_list = [1, 2, 3, 3, 4]
uniques = [item for item in your_list if your_list.count(item) == 1]
I would use collections.Counter for that:
>>> import collections
>>> l = [1, 2, 3, 3, 4]
>>> c = collections.Counter(l)
>>> [el for el in l if c[el] == 1]
[1, 2, 4]
This question already has answers here:
Transpose list of lists
(14 answers)
Closed 9 years ago.
I have a 2d list like this:
1 2 3
4 5 6
and I want to make this:
1 4
2 5
3 6
I've tried to do a for loop and switch each value but I keep getting an index out of bound error. Here's what I have:
for i in results:
for j in range(numCenturies):
rotated[i][j] = results [j][i]
From python documentation on zip function:
This function returns a list of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables. The returned list is truncated in length to the length of the shortest argument sequence. When there are multiple arguments which are all of the same length, zip() is similar to map() with an initial argument of None. With a single sequence argument, it returns a list of 1-tuples. With no arguments, it returns an empty list.
Example:
zip([1, 2, 3], [4, 5, 6]) # returns [(1, 4), (2, 5), (3, 6)]
If you need the result to be the list of lists, not the list of tuples, you can use list comprehension:
[list(x) for x in zip([1, 2, 3], [4, 5, 6], [7, 8, 9])] # returns [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
If all your variables are stored in one 2d list, and you want it pass it into zip function, you can use the following (I'll call it the star notation, because I can't remember the proper English term for it):
results = [[1, 2, 3], [4, 5, 6]]
zip(*results) # returns [(1, 4), (2, 5), (3, 6)]
http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html
>>> from numpy import transpose
>>> transpose([[1,2,3],[4,5,6]])
array([[1, 4],
[2, 5],
[3, 6]])
zip is the right way to do this, as shown by aga.
But if you want to know why your original code wasn't working:
for i in results:
for j in range(numCenturies):
rotated[i][j] = results [j][i]
There are two clear problems here, and likely two others. (Since you didn't show us enough of the code or data to be sure, I can't guarantee the two likely ones.)
Presumably results looks something like this:
results = [[1, 2, 3], [4, 5, 6]]
When you do for i in results, that means i will be each element in results—that is, it will be [1, 2, 3], and then [4, 5, 6]. You can't use a list as an index into a list, so this is guaranteed to give you a TypeError: list indices must be integers, not list.
To fix this, you need:
for i in range(len(results)):
… or …
for i, row in enumerate(results):
Next, results[j][i] is guaranteed to raise IndexError: list index out of range, because i is each row number, but you're trying to use it as a column number. If you're iterating over the rows and columns of results, you want this:
rotated[j][i] = results[i][j]
Next, unless you pre-filled rotated with 3 lists, each of which was pre-filled with 2 objects of some kind, you're going to get an IndexError: list assignment index out of range.
To fix this, you need to pre-fill rotated, something like this:
rotated = [[None for j in range(2)] for i in range(3)]
… or …
rotated = [[None, None], [None, None], [None, None]]
Finally, I'll bet numCenturies is 3, in which case you'll get another IndexError: list index out of range as soon as j reaches 2. The simplest thing to do here is to just use the length of the row; there's no chance of an off-by-one error that way.
Putting it all together:
rotated = [[None for j in range(2)] for i in range(3)]
for i, row in enumerate(results):
for j, value in enumerate(row):
rotated[j][i] = value
But in general, Python gives you easier ways to do things than pre-creating arrays and looping over indices to fill in the values. You can use append—or, better, a list comprehension. Or, even better, find a higher-level way to write your use, like a single call to zip.
This question already has answers here:
Why is the order of multiple `for` list comprehension the way it is?
(2 answers)
Closed 5 years ago.
>>> my_list = [[[[1, 2, 3], [4, 5, 6], ]]]
>>> [a for d in my_list for c in d for b in c for a in b]
[1, 2, 3, 4, 5, 6]
is equivalent to
>>> my_list = [[[[1, 2, 3], [4, 5, 6], ]]]
>>> new_list = []
>>> for d in my_list:
... for c in d:
... for b in c:
... for a in b:
... new_list.append(a)
... print(new_list):
[1, 2, 3, 4, 5, 6]
This syntax seems backwards when read from left-to-right. According to PEP 202, "The form [... for x... for y...] nests, with the last index varying fastest, just like nested for loops." is "the Right One."
It seems that this order (of left-to-right corresponding to outer-to-inner nested for loops) was chosen because that is the order in which nested for loops are written.
However, since the expression part of the list comprehension (a in the above example), corresponds to the expression at the inner-most part of the nested loops (new_list.append(a) in the above example), it seems to me that the for _ in _ closest to this expression should be the same in both cases, i.e. it should be for a in b and on outwards:
>>> my_list = [[[[1, 2, 3], [4, 5, 6], ]]]
>>> [a for a in b for b in c for c in d for d in my_list]
NameError: name 'b' is not defined
so that the fastest-changing loop is closest to the action, so-to-speak. This also lends itself to being read from left-to-right in more logically stepwise fashion.
Is this a common sentiment among users? or does anyone have a good counter-argument as to why the current syntax implementation really is "the Right One"?
Consider:
[leaf for branch in tree for leaf in branch]
It unrolls like:
for branch in tree:
for leaf in branch:
yield leaf
If we write it the other way
[leaf for leaf in branch for branch in tree]
It might make more sense in plain English on some level, but a possible counter-argument is that here the name "branch" is used without being (yet) defined.