过滤在条件满足之前把所有东西都拿出来,在条件满足之后保留所有元素

2022-03-12 00:00:00 python list list-comprehension

问题描述

我想知道是否有简单的方法来解决下面的问题。这里的问题是,在初始条件为真之后,我希望保留列表中出现的每个元素。这里的条件是,我要删除值大于18的条件之前的所有内容,但保留之后的所有内容。示例

输入:

p = [4,9,10,4,20,13,29,3,39]

预期输出:

p = [20,13,29,3,39]

我知道你可以通过

在整个名单上发过滤
[x for x in p if x>18] 

但是,一旦找到大于18的第一个值,我就想停止该操作,然后包括这些值的睡觉,而不管它们是否满足条件。这似乎是一个简单的问题,但我还没有找到解决它的办法。


解决方案

您可以在生成器表达式中使用enumerate和列表切片,next

out = next((p[i:] for i, item in enumerate(p) if item > 18), [])

输出:

[20, 13, 29, 3, 39]

运行时取决于数据结构。

下面的图表显示了不同长度p的此处答案之间的运行时间差异。

如果原始数据是列表,那么使用@Kelly Bundy提出的惰性迭代器是明显的赢家:

但是如果初始数据是ndarray对象,那么@richardec和@0x263A(对于大型数组)提出的矢量化操作会更快。特别地,无论数组大小如何,Numpy都优于List方法。但是对于非常大的阵列, pandas 开始表现得比Numpy更好(我不知道为什么,如果有人能解释的话,我(我相信其他人)会很感激的)。

用于生成第一个绘图的代码:

import perfplot
import numpy as np
import pandas as pd
import random
from itertools import dropwhile

def it_dropwhile(p):
    return list(dropwhile(lambda x: x <= 18, p))

def walrus(p):
    exceeded = False
    return [x for x in p if (exceeded := exceeded or x > 18)]

def explicit_loop(p):
    for i, x in enumerate(p):
        if x > 18:
            output = p[i:]
            break
    else:
        output = []
    return output

def genexpr_next(p):
    return next((p[i:] for i, item in enumerate(p) if item > 18), [])

def np_argmax(p):
    return p[(np.array(p) > 18).argmax():]

def pd_idxmax(p):
    s = pd.Series(p)
    return s[s.gt(18).idxmax():]

def list_index(p):
    for x in p:
        if x > 18:
            return p[p.index(x):]
    return []

def lazy_iter(p):
    it = iter(p)
    for x in it:
        if x > 18:
            return [x, *it]
    return []

perfplot.show(
    setup=lambda n: random.choices(range(0, 15), k=10*n) + random.choices(range(-20,30), k=10*n),
    kernels=[it_dropwhile, walrus, explicit_loop, genexpr_next, np_argmax, pd_idxmax, list_index, lazy_iter],
    labels=['it_dropwhile','walrus','explicit_loop','genexpr_next','np_argmax','pd_idxmax', 'list_index', 'lazy_iter'],
    n_range=[2 ** k for k in range(18)],
    equality_check=np.allclose,
    xlabel='~n/20'
)

用于生成第二个图的代码(请注意,我必须修改list_index,因为numpy没有index方法):

def list_index(p):
    for x in p:
        if x > 18:
            return p[np.where(p==x)[0][0]:]
    return []

perfplot.show(
    setup=lambda n: np.hstack([np.random.randint(0,15,10*n), np.random.randint(-20,30,10*n)]),
    kernels=[it_dropwhile, walrus, explicit_loop, genexpr_next, np_argmax, pd_idxmax, list_index, lazy_iter],
    labels=['it_dropwhile','walrus','explicit_loop','genexpr_next','np_argmax','pd_idxmax', 'list_index', 'lazy_iter'],
    n_range=[2 ** k for k in range(18)],
    equality_check=np.allclose,
    xlabel='~n/20'
)

相关文章