Python applying dynamic list comprehension (list append) - python

vals= [1]
for j in xrange(i):
vals.append([k for k in f(vals[j])])
This loop appends values to itself over a loop. If I compress this into a list comprehension, it doesn't work because it doesn't "dynamically" extend vals using itself on each iteration -- it processes vals as it is originally framed.
Is there a way to do a one line list comprehension that dynamically appends like this? Based on my research, it looks like maybe I am looking for a reduce function? (the equivalent of a fold)

You can indeed use reduce for this, using the initial list as the third parameter.
>>> def f(lst):
... return [x+1 for x in lst] + [len(lst)]
>>> reduce(lambda lst, i: lst + [f(lst[i])], range(5), [[1]])
[[1], [2, 1], [3, 2, 2], [4, 3, 3, 3], [5, 4, 4, 4, 4], [6, 5, 5, 5, 5, 5]]
(Note that the initial list should probably be [[1]], not [1], otherwise you are passing a number to f in the first iteration, but a list in all following iterations.)
Also note that concerning performance your original loop is probably a bit faster, as the reduce basically has to create two new lists in each iteration, while you just have to append to a list. Personally, I would go with a variation of the loop, removing the (probably useless) inner list comprehension and using [-1] to make clear that you are always using the previous result.
vals = [[1]]
for _ in xrange(n):
vals.append(f(vals[-1]))

Related

Unique list of lists

I have a nested list as an example:
lst_a = [[1,2,3,5], [1,2,3,7], [1,2,3,9], [1,2,6,8]]
I'm trying to check if the first 3 indices of a nested list element are the same as other.
I.e.
if [1,2,3] exists in other lists, remove all the other nested list elements that contain that. So that the nested list is unique.
I'm not sure the most pythonic way of doing this would be.
for i in range(0, len(lst_a)):
if lst[i][:3] == lst[i-1][:3]:
lst[i].pop()
Desired output:
lst_a = [[1,2,3,9], [1,2,6,8]]
If, as you said in comments, sublists that have the same first three elements are always next to each other (but the list is not necessarily sorted) you can use itertools.groupby to group those elements and then get the next from each of the groups.
>>> from itertools import groupby
>>> lst_a = [[1,2,3,5], [1,2,3,7], [1,2,3,9], [1,2,6,8]]
>>> [next(g) for k, g in groupby(lst_a, key=lambda x: x[:3])]
[[1, 2, 3, 5], [1, 2, 6, 8]]
Or use a list comprehension with enumerate and compare the current element with the last one:
>>> [x for i, x in enumerate(lst_a) if i == 0 or lst_a[i-1][:3] != x[:3]]
[[1, 2, 3, 5], [1, 2, 6, 8]]
This does not require any imports, but IMHO when using groupby it is much clearer what the code is supposed to do. Note, however, that unlike your method, both of those will create a new filtered list, instead of updating/deleting from the original list.
I think you are missing a loop For if you want to check all possibilities. I guess it should like :
for i in range(0, len(lst_a)):
for j in range(i, len(lst_a)):
if lst[i][:3] == lst[j][:3]:
lst[i].pop()
Deleting while going throught the list is maybe not the best idea you should delete unwanted elements at the end
Going with your approach, Find the below code:
lst=[lst_a[0]]
for li in lst_a[1:]:
if li[:3]!=lst[0][:3]:
lst.append(li)
print(lst)
Hope this helps!
You can use a dictionary to filter a list:
dct = {tuple(i[:3]): i for i in lst}
# {(1, 2, 3): [1, 2, 3, 9], (1, 2, 6): [1, 2, 6, 8]}
list(dct.values())
# [[1, 2, 3, 9], [1, 2, 6, 8]]

how to split a list into a nested list in Python 3?

I want to split a list into a nest list. The list I have is this:
[1,2,1,3,2]
Now, I want the output to be like this:
[[1,2],[2,1],[1,3],[3,2]]
Is there any possible of doing the output as mentioned above?
You can use zip
lst = [1,2,1,3,2]
res = [list(pair) for pair in zip(lst, lst[1:])]
print(res) # -> [[1, 2], [2, 1], [1, 3], [3, 2]]
Note: the first instance of lst in zip does not have to be sliced since it is the smallest of the two that dictates the number of tuples that will be generated.
As #Jean-FrancoisFabre said in the comments, if the original list is big, you might want to go with a generator instead of a hard slice.
res = [list(pair) for pair in zip(lst, itertools.islice(lst, 1, None))]
The benefit of this approach (or the drawback of the previous one) is that the second list used in zip (lst[1:]) is not created in memory, but you will need to import itertools for it to work.
You're looking for bi-grams. Here is a generic function to generate n-grams from a sequence.
def ngrams(seq, n):
return [seq[i:i + n] for i in range(len(seq) - n + 1)]
a = [1,2,1,3,2]
print ngrams(a, 2)
# [[1, 2], [2, 1], [1, 3], [3, 2]]

Grouping nested list by changes in list lengths

I have a nested list in the following form
inputlist = [[1,2,3],[4,5,6],[7,8,9],[1,2,3,4],[5,6,7,8],[1,2],[3,4]]
I would like further nest it based on changing length as follows:
outputlist = [[[1,2,3],[4,5,6],[7,8,9]],[[1,2,3,4],[5,6,7,8]],[[1,2],[3,4]]]
The underlying logic is that I wish to group every change in list length into a new sublist. It is kind of difficult to explain but I hope the above two examples show what I am trying to do.
How can I achieve this simply and elegantly using python? Thanks.
>>> from itertools import groupby
>>> input_list = [[1,2,3],[4,5,6],[7,8,9],[1,2,3,4],[5,6,7,8],[1,2],[3,4]]
>>> [list(g) for k, g in groupby(input_list, key=len)]
[[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3, 4], [5, 6, 7, 8]], [[1, 2], [3, 4]]]
Here's an approach.
Get a list of the lengths involved:
#solen: set of lengths
solen = set([len(subl) for subl in inputlist]) # portable
solen = {len[subl] for subl in inputlist} # post Python 2.6
Then build the list of lists of a particular length:
#losubl: list of sublists, one for each item from solen
losubl = [[subl for subl in inputlist if len(subl) == ulen] for ulen in solen]
As jamylak points out, this solution is less efficient than the one based on itertools (more than one pass, sacrifices some order information). OTOH, it may avoid an import if you don't have other uses for itertools. If the lists you're working with are big and complicated, it's probably worth the extra import to use itertools.

Converting a list of lists into its sub-lists using a for loop and .format

I would like to apply this idea to separate a list of lists into individually named list. Say L = [[0,1,2],[3,4,5],[6,7,8]] and I want the first list ([0,1,2]) to be separate to give L0 = l[0] (first list within the list is labeled as L0). My ideas is this:
for i in range(11):
'l{}'.format(i) = l[i]
but the error message is this: can't assign to function call. I am hoping to accomplish this ultimately:
for i in range(11):
list( 'l{}'.format(i)) = l[i]
in order to convert the string into a list. Anyone know of an way to make this work or is the idea a bust?
The simplest way to do this is using tuple packing/unpacking
L = [[0,1,2],[3,4,5],[6,7,8]]
L0, L1, L2 = L
print(L0)
print(L1)
print(L2)
result:
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
You can create variables dynamically (However I don't think its a good idea). Dynamically creating variables should not be used where a dict can be used instead. If you do not know how to create dynamic variables its probably means you shouldn't. there is more than enough info in the net to look up on a quick google search to know why.
That being said here is a way to do what you want using the dict method.
L = [[0,1,2],[3,4,5],[6,7,8]]
my_dict = {}
for x in range(len(L)):
my_dict[("L%s"%(x))] = L[x]
print (my_dict)
The result would be:
{'L0': [0, 1, 2], 'L1': [3, 4, 5], 'L2': [6, 7, 8]}
Then all you need to do is interact with the dict to get anything you need from those list.
You can use exec to execute a string as if it were Python code:
for i in range(len(L)):
exec('L{} = L[{}]'.format(i, i))
will give you variables L0, L1, L2 in your case.
L = [[0,1,2],[3,4,5],[6,7,8]]
myDict = {}
for index,inner_list in enumerate(L):
key = 'L'+str(index)
myDict[key]=inner_list
print(myDict)
RESULT
{'L0': [0, 1, 2], 'L1': [3, 4, 5], 'L2': [6, 7, 8]}

Is there an equivalent to the C++ pre/postfix operators for use in Python list comprehensions?

I writing a function which populates a list of lists of two elements, where the first element is an element from a different list and the second element is a value which increments.
def list_of_pairs(seq, start):
""" Returns a list of pairs """
>>> list_of_pairs([3, 2, 1], 1)
[ [3, 1], [2, 2], [1, 3] ]
return [[i, start++] for i in seq]
Is there an equivalent to the C++ postfix operators which can be used? Thanks!
Bonus question: Is it more Pythonic to use the list constructor, than to construct the list using square brackets?
Edit: Here is my current (less beautiful) workaround -
def list_of_pairs(seq, start):
""" Returns a list of pairs """
>>> list_of_pairs([3, 2, 1], 1)
[ [3, 1], [2, 2], [1, 3] ]
return [[seq[i], start+i] for i in range(len(seq))]
You may use enumerate() to achieve this. Enumerate return the index along with value while iterating over the list of values. And as per your requirement, you need list of list as [val, index + count]. Below is the sample code to achieve that:
>>> seq = [2, 6, 9]
>>> count = 2
>>> [[val, count+i] for i, val in enumerate(seq)]
[[2, 2], [6, 3], [9, 4]]
You can achieve the same with itertools.count, calling next on the count object for each element of the sequence:
from itertools import count
c = count(start)
lst = [[x, next(c)] for x in seq]
construct the list using square brackets
That's a list comprehension. It's pretty standard and most certainly Pythonic to use list comprehensions for creating lists.
++ and -- have been deliberately excluded from Python, because using them in expressions tends to lead to confusing code and off-by-one errors.
You should use enumerate instead:
def list_of_pairs(seq, start):
return [[elem, i] for i, elem in enumerate(seq, start)]

Categories

Resources