Understanding Nested List Comprehension

Comprehension on a nested iterables?

Here is how you would do this with a nested list comprehension:

[[float(y) for y in x] for x in l]

This would give you a list of lists, similar to what you started with except with floats instead of strings. If you want one flat list then you would use [float(y) for x in l for y in x].

Explanation of how nested list comprehension works?

Ah, the incomprehensible "nested" comprehensions. Loops unroll in the same order as in the comprehension.

[leaf for branch in tree for leaf in branch]

It helps to think of it like this.

for branch in tree:
for leaf in branch:
yield leaf

The PEP202 asserts this syntax with "the last index varying fastest" is "the Right One", notably without an explanation of why.

Understanding nested list comprehension

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!

How to make a nested list comprehension?

result = [item for row in prices for item in row]
print(result)

What is the under the hood reason that we can use nested for loops in list comprehensions

Your first translation should be

li=[]
for item in iterable:
li.append( expression )

Your example [s+z for s in iterable_1 for z in iterable_2] is translated as

li=[]
for s in iterable_1:
for z in iterable_2:
li.append(s+z)

Congrats, you have discovered ... monads! which are essentially what you've described, generalized nested loops.

Nested loops just produce a plain stream of results. Nested lists, when flattened, also turn into a plain stream of elements. That's the similarity. A lazy append is pretty much like yield.

Each monad type is defined by how it implements its version of the flatMap function, which is a map followed by the flattening of the resulting nested structure. The flattening of the nested structure at each nesting level allows for an arbitrary depth of nesting to be flattened:

M [M (a)]  ==>  M (a)

M [M [M (a)]] ==> # flatten the two outer layers first:
M [M (a)] ==> M (a)
OR:
==> # flatten the two inner layers first:
M [M (a)] ==> M (a)

See the difference? There isn't any! Any type that does the above, is a "monad". Like lists.

So it is with loops as well, which can be nested to an arbitrary depth -- two, three, whatever, it doesn't matter. The whole structure is still producing its results one by one, and these are the results which the innermost loop is producing, one by one.

That is the under the hood reason why we can use nested for loops in list comprehensions. Or, saying the same thing in a fancy way, it is because list comprehensions are just like monadic chains of operations (and can be translated as such).

Nested list comprehension understanding for comparing strings in python

When you write:

l3 = [x for x in l1 for y in l2 if y in x]

It's equal to the following:

l3 = []
for x in l1: # loop through each element in the list l1
for y in l2: # loop through each element in the list l2
if y in x: # if x contains y
l3.append(x)

Nested list comprehension not the same as nested for loop

Your list comprehension doesn't do anything as it does not run without i already been defined outside the list comprehension.

To achieve what you want (and yes, this is counter intuitive ) you need to do this:

[j  for i in range(1,5) for j in range(0,i)]

This will yield:

[0, 0, 1, 0, 1, 2, 0, 1, 2, 3]

which is the same order as your nested for loops.

Nested list comprehension on python

This should do it:

value = ['a','b','c','d','e','f']
key = [2, 3, 1, 1, 3, 2]

answer = {}
for k, v in zip(key, value):
if k in answer:
answer[k].append(v)
else:
answer[k] = [v]

print(answer)
{2: ['a', 'f'], 3: ['b', 'e'], 1: ['c', 'd']}

EDIT: oops, jumped the gun. Apologies.

Here's the comprehension version, but it's not very efficient:

{
k: [v for i, v in enumerate(value) if key[i] == k]
for k in set(key)
}

EDIT 2:

Here's an one that has better complexity:

import pandas as pd
series = pd.Series(key)
{
k: [value[i] for i in indices]
for k, indices in series.groupby(series).groups.items()
}


Related Topics



Leave a reply



Submit