shakaran
shakaran

Reputation: 11082

Python: Split list in subsets if the current element is minor than previous element

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

Answers (4)

Anand S Kumar
Anand S Kumar

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

Padraic Cunningham
Padraic Cunningham

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

DSM
DSM

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

Jared Mackey
Jared Mackey

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

Related Questions