在条件满足之前过滤掉所有元素,在条件满足之后保留所有元素
Filter out everything before a condition is met, keep all elements after
我想知道是否有解决以下问题的简单方法。这里的问题是我想在初始条件为真后保留此列表中出现的每个元素。这里的条件是我想在值大于 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 的值后停止此操作,然后包括其余值,无论它们是否满足条件。这似乎是一个简单的问题,但我还没有找到解决方法。
您可以使用 itertools.dropwhile
:
from itertools import dropwhile
p = [4,9,10,4,20,13,29,3,39]
p = dropwhile(lambda x: x <= 18, p)
print(*p) # 20 13 29 3 39
在我看来,这可以说是 easiest-to-read 版本。这也对应于其他函数式编程语言中的常见模式,例如Haskell中的dropWhile (<=18) p
和Scala中的p.dropWhile(_ <= 18)
。
或者,使用海象运算符(仅在 python 3.8+ 中可用):
exceeded = False
p = [x for x in p if (exceeded := exceeded or x > 18)]
print(p) # [20, 13, 29, 3, 39]
但我猜有些人不喜欢这种风格。在那种情况下,可以做一个明确的 for
循环(ilkkachu 的建议):
for i, x in enumerate(p):
if x > 18:
output = p[i:]
break
else:
output = [] # alternatively just put output = [] before for
您可以在生成器表达式和 next
:
中使用 enumerate
和列表切片
out = next((p[i:] for i, item in enumerate(p) if item > 18), [])
输出:
[20, 13, 29, 3, 39]
在运行时方面,取决于数据结构。
下图显示了此处答案在 p
的各种长度下的运行时间差异。
如果原始数据是列表,那么使用 提出的惰性迭代器显然是赢家:
但是如果初始数据是一个 ndarray 对象,那么 and (对于大数组)提出的向量化操作会更快。特别是,无论数组大小如何,numpy 都优于列表方法。但是对于非常大的数组,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'
)
这里有很棒的解决方案;只是想演示如何使用 numpy:
>>> import numpy as np
>>> p[(np.array(p) > 18).argmax():]
[20, 13, 29, 3, 39]
由于这里有很多不错的答案,我决定 运行 一些简单的基准测试。第一个使用长度为 9 的 OP 样本数组 ([4,9,10,4,20,13,29,3,39]
)。第二个使用随机生成的长度为 20,000 的数组,其中前半部分介于 0 和 15 之间,后半部分介于 -20 和 30 之间(这样分裂就不会发生在中间)。
使用 OP 的数据(长度为 9 的数组):
%timeit enke()
650 ns ± 15.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit j1lee1()
546 ns ± 4.22 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit j1lee2()
551 ns ± 19 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit j2lee3()
536 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit richardec()
2.08 µs ± 16 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
使用长度为20,000(两万)的数组:
%timeit enke()
1.5 ms ± 34.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit j1lee1()
1.95 ms ± 43 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit j1lee2()
2.1 ms ± 53.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit j2lee3()
2.33 ms ± 96.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit richardec()
13.3 µs ± 461 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
生成第二个数组的代码:
p = np.hstack([np.random.randint(0,15,10000),np.random.randint(-20,30,10000)])
所以,对于小案例,numpy 是一个 slug,不需要。但是在大的情况下,numpy 几乎快 100 倍,并且要走的路! :)
我注意到 OP 在回答下提到 p
实际上是一个 Pandas DataFrame。这是一种使用 Pandas:
过滤所有元素直到大于 18 的数字的第一个实例的方法
import pandas as pd
df = pd.DataFrame([4,9,10,4,20,13,29,3,39])
df = df[df[0].gt(18).idxmax():]
print(df)
输出:
0
4 20
5 13
6 29
7 3
8 39
注意:我对您的 DataFrame 的实际结构视而不见,所以我只是使用了给定的内容。
根据我的经验,至少对于整数列表,使用 enumerate
只是为了找到 一个 索引并丢弃所有其他索引是如此浪费以至于它更快首先只找到元素,然后使用 list.index
找到它的索引。根据一些测试,我预计它比 .
中的 explicit_loop
解决方案(使用 enumerate
)快大约 1.44 倍
def start_over_18(p):
for x in p:
if x > 18:
return p[p.index(x):]
return []
另一种解决方案,使用迭代器,因此我根本不必为索引操心。似乎是两倍 explicit_loop
解决方案:
def start_over_18(p):
it = iter(p)
for x in it:
if x > 18:
return [x, *it]
return []
我想知道是否有解决以下问题的简单方法。这里的问题是我想在初始条件为真后保留此列表中出现的每个元素。这里的条件是我想在值大于 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 的值后停止此操作,然后包括其余值,无论它们是否满足条件。这似乎是一个简单的问题,但我还没有找到解决方法。
您可以使用 itertools.dropwhile
:
from itertools import dropwhile
p = [4,9,10,4,20,13,29,3,39]
p = dropwhile(lambda x: x <= 18, p)
print(*p) # 20 13 29 3 39
在我看来,这可以说是 easiest-to-read 版本。这也对应于其他函数式编程语言中的常见模式,例如Haskell中的dropWhile (<=18) p
和Scala中的p.dropWhile(_ <= 18)
。
或者,使用海象运算符(仅在 python 3.8+ 中可用):
exceeded = False
p = [x for x in p if (exceeded := exceeded or x > 18)]
print(p) # [20, 13, 29, 3, 39]
但我猜有些人不喜欢这种风格。在那种情况下,可以做一个明确的 for
循环(ilkkachu 的建议):
for i, x in enumerate(p):
if x > 18:
output = p[i:]
break
else:
output = [] # alternatively just put output = [] before for
您可以在生成器表达式和 next
:
enumerate
和列表切片
out = next((p[i:] for i, item in enumerate(p) if item > 18), [])
输出:
[20, 13, 29, 3, 39]
在运行时方面,取决于数据结构。
下图显示了此处答案在 p
的各种长度下的运行时间差异。
如果原始数据是列表,那么使用
但是如果初始数据是一个 ndarray 对象,那么
用于生成第一个图的代码:
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'
)
这里有很棒的解决方案;只是想演示如何使用 numpy:
>>> import numpy as np
>>> p[(np.array(p) > 18).argmax():]
[20, 13, 29, 3, 39]
由于这里有很多不错的答案,我决定 运行 一些简单的基准测试。第一个使用长度为 9 的 OP 样本数组 ([4,9,10,4,20,13,29,3,39]
)。第二个使用随机生成的长度为 20,000 的数组,其中前半部分介于 0 和 15 之间,后半部分介于 -20 和 30 之间(这样分裂就不会发生在中间)。
使用 OP 的数据(长度为 9 的数组):
%timeit enke()
650 ns ± 15.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit j1lee1()
546 ns ± 4.22 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit j1lee2()
551 ns ± 19 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit j2lee3()
536 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit richardec()
2.08 µs ± 16 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
使用长度为20,000(两万)的数组:
%timeit enke()
1.5 ms ± 34.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit j1lee1()
1.95 ms ± 43 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit j1lee2()
2.1 ms ± 53.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit j2lee3()
2.33 ms ± 96.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit richardec()
13.3 µs ± 461 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
生成第二个数组的代码:
p = np.hstack([np.random.randint(0,15,10000),np.random.randint(-20,30,10000)])
所以,对于小案例,numpy 是一个 slug,不需要。但是在大的情况下,numpy 几乎快 100 倍,并且要走的路! :)
我注意到 OP 在回答下提到 p
实际上是一个 Pandas DataFrame。这是一种使用 Pandas:
import pandas as pd
df = pd.DataFrame([4,9,10,4,20,13,29,3,39])
df = df[df[0].gt(18).idxmax():]
print(df)
输出:
0
4 20
5 13
6 29
7 3
8 39
注意:我对您的 DataFrame 的实际结构视而不见,所以我只是使用了给定的内容。
根据我的经验,至少对于整数列表,使用 enumerate
只是为了找到 一个 索引并丢弃所有其他索引是如此浪费以至于它更快首先只找到元素,然后使用 list.index
找到它的索引。根据一些测试,我预计它比
explicit_loop
解决方案(使用 enumerate
)快大约 1.44 倍
def start_over_18(p):
for x in p:
if x > 18:
return p[p.index(x):]
return []
另一种解决方案,使用迭代器,因此我根本不必为索引操心。似乎是两倍 explicit_loop
解决方案:
def start_over_18(p):
it = iter(p)
for x in it:
if x > 18:
return [x, *it]
return []