This question already has an answer here:
flat list as a result of list comprehension [duplicate]
(1 answer)
Closed 12 months ago.
I have a list w where w[i] represents the number of times i should be present in the final result.
For example, if:
w = [1, 3, 4]
then the resulting list should be:
[0, 1, 1, 1, 2, 2, 2, 2]
Notice there is one 0, three 1's, and four 2's.
I have tried to accomplish this with the list comprehension:
w_list = [[i]*w[i] for i in range(len(w))]
but of course this doesn't quite work, giving me this:
[[0], [1, 1, 1], [2, 2, 2, 2]]
How can I write a list comprehension to get me the desired result, like in the example?
You could use a double loop to flatten w_list:
w_list = [x for num, i in enumerate(w) for x in [num] * i]
or
w_list = [x for i in range(len(w)) for x in [i]*w[i]]
Output:
[0, 1, 1, 1, 2, 2, 2, 2]
Here's an alternative solution you might like.
w = [1, 3, 4]
print([i for i in range(len(w)) for _ in range(w[i])])
results in [0, 1, 1, 1, 2, 2, 2, 2] as desired.
Alternatively, [i for i,n in enumerate(w) for _ in range(n)] accomplishes the same thing.
#Write a function named add_one_to_all that takes in a list of numbers and adds one to each of the original numbers
assert add_one_to_all([0, 0, 0]) == [1, 1, 1]
assert add_one_to_all([1, 2, 3]) == [2, 3, 4]
assert add_one_to_all([6, 7, 8]) == [7, 8, 9]
I've tried this
def my_list ([x,y,z]):
new_list = [x+1 for x in my_list]
def add_one_to_all(my_list):
return [x+1 for x in my_list]
def add_one_to_all(integer):
return integer+1
int_list = [1, 2, 3, 4, 5]
# Adding one on each integer
# using list comprehension.
output_list = [add_one_to_all(i) for i in int_list]
print(output_list)
The output will be [2,3,4,5,6]
You can use map
l = [1, 2, 3]
print(list(map(lambda x:x+1, l)))
result
[2, 3, 4]
I was curios about the question: Eliminate consecutive duplicates of list elements, and how it should be implemented in Python.
What I came up with is this:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
i = 0
while i < len(list)-1:
if list[i] == list[i+1]:
del list[i]
else:
i = i+1
Output:
[1, 2, 3, 4, 5, 1, 2]
Which I guess is ok.
So I got curious, and wanted to see if I could delete the elements that had consecutive duplicates and get this output:
[2, 3, 5, 1, 2]
For that I did this:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
i = 0
dupe = False
while i < len(list)-1:
if list[i] == list[i+1]:
del list[i]
dupe = True
elif dupe:
del list[i]
dupe = False
else:
i += 1
But it seems sort of clumsy and not pythonic, do you have any smarter / more elegant / more efficient way to implement this?
>>> L = [1,1,1,1,1,1,2,3,4,4,5,1,2]
>>> from itertools import groupby
>>> [key for key, _group in groupby(L)]
[1, 2, 3, 4, 5, 1, 2]
For the second part
>>> [k for k, g in groupby(L) if len(list(g)) < 2]
[2, 3, 5, 1, 2]
If you don't want to create the temporary list just to take the length, you can use sum over a generator expression
>>> [k for k, g in groupby(L) if sum(1 for i in g) < 2]
[2, 3, 5, 1, 2]
Oneliner in pure Python
[v for i, v in enumerate(your_list) if i == 0 or v != your_list[i-1]]
If you use Python 3.8+, you can use assignment expression :=:
list1 = [1, 2, 3, 3, 4, 3, 5, 5]
prev = object()
list1 = [prev:=v for v in list1 if prev!=v]
print(list1)
Prints:
[1, 2, 3, 4, 3, 5]
A "lazy" approach would be to use itertools.groupby.
import itertools
list1 = [1, 2, 3, 3, 4, 3, 5, 5]
list1 = [g for g, _ in itertools.groupby(list1)]
print(list1)
outputs
[1, 2, 3, 4, 3, 5]
You can do this by using zip_longest() + list comprehension.
from itertools import zip_longest
list1 = [1, 2, 3, 3, 4, 3, 5, 5].
# using zip_longest()+ list comprehension
res = [i for i, j in zip_longest(list1, list1[1:])
if i != j]
print ("List after removing consecutive duplicates : " + str(res))
Here is a solution without dependence on outside packages:
list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
L = list + [999] # append a unique dummy element to properly handle -1 index
[l for i, l in enumerate(L) if l != L[i - 1]][:-1] # drop the dummy element
Then I noted that Ulf Aslak's similar solution is cleaner :)
To Eliminate consecutive duplicates of list elements; as an alternative, you may use itertools.zip_longest() with list comprehension as:
>>> from itertools import zip_longest
>>> my_list = [1,1,1,1,1,1,2,3,4,4,5,1,2]
>>> [i for i, j in zip_longest(my_list, my_list[1:]) if i!=j]
[1, 2, 3, 4, 5, 1, 2]
Plenty of better/more pythonic answers above, however one could also accomplish this task using list.pop():
my_list = [1, 2, 3, 3, 4, 3, 5, 5]
for x in my_list[:-1]:
next_index = my_list.index(x) + 1
if my_list[next_index] == x:
my_list.pop(next_index)
outputs
[1, 2, 3, 4, 3, 5]
Another possible one-liner, using functools.reduce (excluding the import) - with the downside that string and list require slightly different implementations:
>>> from functools import reduce
>>> reduce(lambda a, b: a if a[-1:] == [b] else a + [b], [1,1,2,3,4,4,5,1,2], [])
[1, 2, 3, 4, 5, 1, 2]
>>> reduce(lambda a, b: a if a[-1:] == b else a+b, 'aa bbb cc')
'a b c'
I have the following list:
x = np.array([1, 1, 2, 2, 2])
with np.unique values of [1, 2]
How do I generate the following list:
[1, 2, 1, 2, 3]
i.e. a running index from 1 for each of the unique elements in the list x.
you can use pandas.cumcount() after grouping by the value itself, it does exactly that:
Number each item in each group from 0 to the length of that group - 1.
try this:
import numpy as np
import pandas as pd
x = np.array([1, 1, 2, 2, 2])
places = list(pd.Series(x).groupby(by=x).cumcount().values + 1)
print(places)
Output:
[1, 2, 1, 2, 3]
Just use return_counts=True of np.unique with listcomp and np.hstack. It is still faster pandas solution
c = np.unique(x, return_counts=True)[1]
np.hstack([np.arange(item)+1 for item in c])
Out[869]: array([1, 2, 1, 2, 3], dtype=int64)
I'm not sure, if this is any faster or slower solution, but if you need just a list result with no pandas, you could try this
arr = np.array([1, 1, 2, 2, 2])
from collections import Counter
ranges = [range(1,v+1) for k,v in Counter(arr).items()]
result = []
for l in ranges:
result.extend(list(l))
print(result)
[1, 2, 1, 2, 3]
(or make your own counter with dict instead of Counter())
As per the title, I have a nested lists like so (the nested list is a fixed length):
# ID, Name, Value
list1 = [[ 1, "foo", 10],
[ 2, "bar", None],
[ 3, "fizz", 57],
[ 4, "buzz", None]]
I'd like to return a list (the number of items equal to the length of a sub-list from list1), where the sub-lists are the indices of rows without None as their Xth item, i.e.:
[[non-None ID indices], [non-None Name indices], [non-None Value indices]]
Using list1 as an example, the result should be:
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 2]]
My current implementation is:
indices = [[] for _ in range(len(list1[0]))]
for i, row in enumerate(list1):
for j in range(len(row)):
if not isinstance(row[j], types.NoneType):
indices[j].append(i)
...which works, but can be slow (the lengths of the lists are in the hundreds of thousands).
Is there a better/more efficient way to do this?
EDIT:
I've refactored the above for loops into nested list comprehensions (similar to SilentGhost's answer). The following line gives the same result as the my original implementation, but runs approximately 10x faster.
[[i for i in range(len(list1)) if list1[i][j] is not None] for j in range(len(log[0]))]
>>> [[i for i, j in enumerate(c) if j is not None] for c in zip(*list1)]
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 2]]
in python-2.x you could use itertools.izip instead of zip to avoid generating intermediate list.
[[i for i in range(len(list1)) if list1[i] is not None] for _ in range(len(log[0]))]
The above seems to be about 10x faster than my original post.
import numpy as np
map(lambda a: np.not_equal(a, None).nonzero()[0], np.transpose(list1))
# -> [array([0, 1, 2, 3]), array([0, 1, 2, 3]), array([0, 2])]