过滤在条件满足之前把所有东西都拿出来,在条件满足之后保留所有元素
问题描述
我想知道是否有简单的方法来解决下面的问题。这里的问题是,在初始条件为真之后,我希望保留列表中出现的每个元素。这里的条件是,我要删除值大于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'
)
相关文章