Writing list comprehensions dependent on previous values? - python

I've seen this question before, but it only deals with recursions that are linear in nature. I'm looking for something more general.
Suppose I have the following code
n = 10
num_bits = [0]
for i in range(n):
nums_bits.append(num_bits[i>>1]+i%2)
This code will compute num_bits, a 11 element array of value with num_bits[i] representing the number of bits it takes to represent i.
Is it possible to write this as a list comprehension? Something like this doesn't work
num_bits = [0]*11
num_bits = [num_bits[i>>1]+i%2 for i in range(11)]
since the comprehension doesn't update the value of num_bits in the middle of evaluation. Is there a canonical way to do something like this, besides a for loop?
P.S. I'm aware there are other ways to solve this problem: I'm just using it as a vehicle to understand Python's features better.
Edit: To summarize, I'd like to know what the proper way of generating lists of values that are dependent on previous values is. For a simpler example, consider the Fibonacci Numbers
fibonacci = [0,1]
for i in range(10):
fibonacci.append(fibonacci[-1]+fibonacci[-2])
Is there a way to generate these numbers in a comprehension? If not, what tools are there for this other than for loops (or are for/while loops my only option)?

Given it is not a piece of code I'd recommend, for the reasons discussed in the comments above and in the other answer, this comprehension should be faster than the for loop:
fibonacci = [0,1]
deque((fibonacci.append(fibonacci[-1]+fibonacci[-2]) for _ in range(10)), maxlen=0)
as it fills the list consuming the generator and discarding the result (an empty queue, it's the fastest recommended way to consume an iterator)
It produces:
>>> fibonacci
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

No.
There is no nice way to do this with a list comprehension, and that is not what they’re for. The purpose of list comprehensions is to offer a more readable alternative to maps and filters, and this isn’t that, so it’s not possible to do this in a sensible way.

Related

Divide a Python list every n-element

Recently, I've found a code for developing a set of lists from a list, that code was written by user Mai, answering question but i have not understood it yet. Could somebody help me to understand it? And... is there a way to rewrite that code that be easier? The code is:
def even_divide(lst, num_piece=4):
return [
[lst[i] for i in range(len(lst)) if (i % num_piece) == r]
for r in range(num_piece)
]
Thanks!
It's pretty simple actually. Just follow through the values of the two loops:
Starting with the outer loop, r would be 0, then 1, then 2, etc. Let's look at the case for which r == 1. When running through the different values of i, (which would be 0, 1, 2, ... len(lst), the value of i % 4, meaning the remainder of dividing i by 4, would be 0, 1, 2, 3, 0, 1, 2, 3, .... So the i % 4 would be equal to r, for every 4 values of i!
For our chosen r == 1, that would mean we're choosing lst[1], lst[5], lst[9], ..., etc.
And for r == 2? You guessed it! You'd be picking up lst[2], lst[6], lst[10],....
So over all you'd get 4 lists, with non-overlapping elements of the original list, by just "jumping" 4 elements every time, but starting at different values.
Which naturally leads to the more simple solution:
def even_divide(lst, num_piece=4):
return [lst[r::num_piece] for r in range(num_piece)]
Could somebody help me to understand it?
Sure! It's a list comprehension. A list comprehension takes a list and does something to or with every element in that list. Let's say I want to multiply every element in my list by 2:
new_list = [element*2 for element in my_list]
What makes it a list comprehension is the bracket syntax. For those new to it, that's usually the part that takes a moment to get used to. With that said, I assume that is what is giving you difficulty in understanding the code in your question, as you have a list comprehension in a list comprehension. It might be difficult to understand now, but list comprehensions are a wonderful thing in python.
But, as this post mentions, there's a lot of discussions around list comprehension, lambda's, map, reduce, and filter. Ultimately, its up to you to decide what's best for your project. I'm not a fan of anything else but list comprehensions, so I use those religiously.
Based on the question you've linked, the list comprehension takes a 1d list of length x and turns it into a 2d list of (length x, width y). It's like numpy.reshape.
And... is there a way to rewrite that code [to] be easier?
I would not recommend it. List comprehensions are considered very pythonic and you will see them everywhere. Best to use them and get used to them.

Find lowest integer in list, which has both strings and integers

I want to find the lowest integer in a list, which contains strings and integers. Is there a quick way to find it?
I could solve the issue with regex. But that sounds too much work for this.
Create a new list, with list comprehension sounds better. But isn't there an easier way?
my_list = [2, 4, 'foo']
of course min(my_list) won't work because of the string inside.
You can use a generator expression to filter out non-ints:
min(n for n in my_list if isinstance(n, int))
# returns 2

index of a first occurrence (inequality match) in a list

A=[2,3,5,7,11,13]
print(A.index(5))
The answer is 2,
But what I need is the first one which is bigger than 4 (the answer will be the same - 2).
I can apply a while loop, but is there a more elegant or a builtin way to do it?
In my problem the list is sorted in an ascending order (no duplication),
and my target is to split it into two lists: lower or equal to 4, and bigger than 4; and given the list is sorted it would be redundant to scan it twice (or even once).
As #DanD.mentioned, you can use the bisect module for this, in you example you can use bisect_left
>>> import bisect
>>> bisect.bisect_left(A, 5)
2
This will use a binary search since your data is sorted, which will be faster than a linear search (O(logN) instead of O(N)).
If you want the index of the first value greater than 4, then you can switch to bisect_right
>>> bisect.bisect_right(A, 4)
2
You're totally correct about efficiency - if you have already sorted list, do not iterate linearly, its waste of time
There's built-in bisect module - exactly for binary search in sorted containers.
You're probably looking for bisect_right function.
Thanks everybody, the answer using your kind help is:
import bisect
A=[2,3,5,7,11,13]
N=bisect.bisect_right(A,4)
print(A[:N]) #[2,3]
print(A[N:]) #[5,7,11,13]
Use next with a default argument:
val = next((i for i, x in enumerate(A) if x > 4), len(A))
Given the above result, you can then do:
left, right = A[:val], A[val:]

Is there a one line code to find maximal value in a matrix?

To find the maximal value in a matrix of numbers, we can code 5 lines to solve the problem:
ans = matrix[0][0]
for x in range(len(matrix)):
for y in range(len(matrix[0])):
ans = max(ans, matrix[x][y])
return ans
Is there a one line solution for this problem?
The one that I came up with is pretty awkward actually:
return max(max(matrix, key=max))
or
return max(map(max, matrix))
You can use generator expression to find the maximum in your matrix. That way you can avoid building the full list of matrix elements in memory.
maximum = max(max(row) for row in matrix)
instead of list comprehension as given in a previous answer here
maximum = max([max(row) for row in matrix])
This is from PEP (the rationale section):
...many of the use cases do not need to have a full list created in
memory. Instead, they only need to iterate over the elements one at a
time.
...
Generator expressions are especially useful with functions like sum(), min(), and max() that reduce an iterable input to a single value
...
The utility of generator expressions is greatly enhanced when combined with reduction functions like sum(), min(), and max().
Also, take a look at this SO post: Generator Expressions vs. List Comprehension.
By matrix, I assume you mean a 2d-list.
max([max(i) for i in matrix])
using numpy.amax:
import numpy as np
>>> my_array
array([[1, 2, 3],
[9, 8, 6]])
>>> np.amax(my_array)
9
You can also flatten your array:
from itertools import chain
flatten = chain.from_iterable
max(flatten(matrix))

Reverse indexing a list of lists in Python

This should be trivial. Yet I don't feel 100% sure about my trick.
I have a list of lists (lol ;)) that captures edge relationships between nodes of a graph. Let's say I have a directed graph with 4 nodes labeled 0, 1, 2, 3. The edges are {(0,2),(0,3),(1,0),(1,3),(2,1)} and so the adjacency lol (call it a) is
a = [[2,3],[0,3],[1],[]]
I want to find the incidence lol now, i.e. a list of lists which indicate which nodes are incident on which nodes. For this example, the incidence lol (call it b) would be:
[[1], [2], [0], [0, 1]]
I tried the following code:
b = [[],[],[],[]]
[b[j].append(i) for i,x in enumerate(a) for j in x]
This gives me the right incidence matrix b.
The second step, although works, should ideally be b[j].append(i) for i,x in enumerate(a) for j in x, without the opening [ and closing ]. But Python interpreter cries syntax error without it. Is there a better way of phrasing it?
Your question is essentially about using list comprehensions for side effects. As, e.g. the answers to this question say, breaking it down into a for loop (or loops) is the way to go:
for i, x in enumerate(a):
for j in x:
b[j].append(i)
Also, please note that list comprehensions are used to construct lists in a very natural, easy way, like a mathematician is used to do. That is why, in Python, syntax requires square brackets (in your case).

Categories

Resources