Python:如果当前元素比前一个元素小,则在子集中拆分列表

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

我有以下 python 列表:

my_list = [1, 2, 3, 1, 2, 1, 2, 3]

我正在寻找一种仅当当前元素小于前一个元素时将列表拆分为多个子列表的有效方法。

在这个例子中,我应该得到:

result = [[1,2,3],[1,2],[1,2,3]]

我应该得到三个子集,因为 1<3 和 1<2。

我只得到了这个解决方案的第一个元素:

[[x] for index, x in enumerate(my_list) if index < 1 or x < my_list[index - 1]]

结果:

[[1], [1], [1]]

再次尝试,结果相同:

[ [my_list[x]] for x in range(len(my_list)) if my_list[x] < my_list[x-1]]

我认为您不能使用简单的列表推导来做到这一点。一种简单的方法是正常的 for 循环方法 -

new_list = []
prev = float('inf')
for x in my_list:
    if x < prev:
        temp = []
        new_list.append(temp)
    temp.append(x)
    prev = x

演示 -

>>> 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]]

此处给出的不同方法的时序比较 -

代码-

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)]

结果-

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

这是一个可行的解决方案。如果愿意,您可以将其转换为列表推导式,但这会很丑陋。

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

这是一种理解式的方法。但是,虽然您可以将其塞进一个长表达式中,但您到底为什么要这么做?

>>> 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]]

这很简单,只需找到新组开始的点,然后使用这些点切入 my_list

你也可以使用 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])]