Reputation: 11082
I have the following python list:
my_list = [1, 2, 3, 1, 2, 1, 2, 3]
I am looking for a efficient way to split a list in several sublist only if the current element is minor than the previous element.
In the example, I should get:
result = [[1,2,3],[1,2],[1,2,3]]
I should get three subsets since 1<3 and 1<2.
I am only getting the first elements with this solution:
[[x] for index, x in enumerate(my_list) if index < 1 or x < my_list[index - 1]]
Result:
[[1], [1], [1]]
Another try with the same result:
[ [my_list[x]] for x in range(len(my_list)) if my_list[x] < my_list[x-1]]
Upvotes: 3
Views: 484
Reputation: 90889
I don't think you can use a simple list comprehension to do this. A simple way would be the normal for loop method -
new_list = []
prev = float('inf')
for x in my_list:
if x < prev:
temp = []
new_list.append(temp)
temp.append(x)
prev = x
Demo -
>>> my_list = [1, 2, 3, 1, 2, 1, 2, 3]
>>> new_list = []
>>> prev = float('inf')
>>> for x in my_list:
... if x < prev:
... temp = []
... new_list.append(temp)
... temp.append(x)
... prev = x
...
>>> new_list
[[1, 2, 3], [1, 2], [1, 2, 3]]
Timing comparisons of different methods given here -
Code -
from itertools import groupby
def func1(my_list):
new_list = []
prev = float('inf')
for x in my_list:
if x < prev:
temp = []
new_list.append(temp)
temp.append(x)
prev = x
return new_list
def func2(my_list):
brks = [i for i in range(1,len(my_list)) if my_list[i] < my_list[i-1]]
return [my_list[x:y] for x,y in zip([0]+brks,brks+[None])]
def func3(my_list):
return [list(next(g)) + [x[1] for x in g] for k, g in
groupby(zip(my_list, my_list[1:]), lambda x: x[1] >= x[0]) if k]
def func4(my_list):
results = []
for i, x in enumerate(my_list):
if i == 0:
results.append([x])
continue
if x < my_list[i - 1]:
results.append([x])
else:
results[-1].append(x)
return results
import random
my_list = [random.randint(1,10) for _ in range(1000)]
Result -
In [20]: %timeit func1(my_list) #Simple for-loop
1000 loops, best of 3: 236 µs per loop
In [21]: %timeit func2(my_list) #List comprehension using breaks.
1000 loops, best of 3: 293 µs per loop
In [22]: %timeit func3(my_list) #@Ashwini's One-liner
1000 loops, best of 3: 689 µs per loop
In [23]: %timeit func4(my_list) #@electrometro's approach.
1000 loops, best of 3: 407 µs per loop
In [31]: %timeit func1(my_list)
1000 loops, best of 3: 223 µs per loop
In [32]: %timeit func2(my_list)
1000 loops, best of 3: 293 µs per loop
In [33]: %timeit func3(my_list)
1000 loops, best of 3: 703 µs per loop
In [34]: %timeit func4(my_list)
1000 loops, best of 3: 415 µs per loop
Upvotes: 2
Reputation: 180391
You could also use numpy:
import numpy as np
my_list = np.array([1, 2, 3, 1, 2, 1, 2, 3])
print(np.split(my_list, np.where(np.diff(my_list) < 0)[0] + 1))
[array([1, 2, 3]), array([1, 2]), array([1, 2, 3])]
Upvotes: 0
Reputation: 353009
Here's a comprehension-ish approach. But while you could cram this into one long expression, why on Earth would you want to?
>>> my_list = [1, 2, 3, 1, 2, 1, 2, 3]
>>> brks = [i for i in range(1,len(my_list)) if my_list[i] < my_list[i-1]]
>>> [my_list[x:y] for x,y in zip([0]+brks,brks+[None])]
[[1, 2, 3], [1, 2], [1, 2, 3]]
This works simply by finding the points where a new group begins and using those to slice into my_list
.
Upvotes: 3
Reputation: 4158
Here is a working solution. You can convert it to a list comprehension if you would like but it would be a rather ugly one.
my_list = [1, 2, 3, 1, 2, 1, 2, 3]
results = []
for i, x in enumerate(my_list):
if i == 0:
results.append([x])
continue
if x < my_list[i - 1]:
results.append([x])
else:
results[-1].append(x)
print results
Upvotes: 1