过滤掉满足条件之前的所有内容,保留满足条件之后的所有元素

cin*_*hes 26 python list-comprehension list

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

输入:

p = [4,9,10,4,20,13,29,3,39]
Run Code Online (Sandbox Code Playgroud)

预期输出:

p = [20,13,29,3,39]
Run Code Online (Sandbox Code Playgroud)

我知道您可以通过以下方式过滤整个列表

[x for x in p if x>18] 
Run Code Online (Sandbox Code Playgroud)

但我想在找到第一个大于 18 的值后停止此操作,然后包含其余的值,无论它们是否满足条件。这似乎是一个简单的问题,但我还没有找到解决方案。

j1-*_*lee 49

您可以使用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
Run Code Online (Sandbox Code Playgroud)

在我看来,这可以说是最容易阅读的版本。dropWhile (<=18) p这也对应于其他函数式编程语言(例如Haskell 和p.dropWhile(_ <= 18)Scala)中的常见模式。


或者,使用 walrus 运算符(仅在 python 3.8+ 中可用):

exceeded = False
p = [x for x in p if (exceeded := exceeded or x > 18)]
print(p) # [20, 13, 29, 3, 39]
Run Code Online (Sandbox Code Playgroud)

但我的猜测是有些人不喜欢这种风格。在这种情况下,可以执行显式for循环(ilkkachu 的建议):

for i, x in enumerate(p):
    if x > 18:
        output = p[i:]
        break
else:
    output = [] # alternatively just put output = [] before for
Run Code Online (Sandbox Code Playgroud)

  • 这里很好地使用了“warlus”。值得一提的是它只存在于 Python 3.8+ 中 (4认同)
  • @NewbieAF,好吧,大多数情况下,几乎任何人都可以立即读取显式循环,而紧凑的单行可能需要更多解码。我并不是说另一个答案中的那个是错误的,如果我这样做了,我可能会因为它是如何Pythonic方式或其他什么而被咀嚼。也不知道性能。只是我觉得这里的显式循环可以简化,同时仍然保持可读性。 (4认同)

小智 23

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

out = next((p[i:] for i, item in enumerate(p) if item > 18), [])
Run Code Online (Sandbox Code Playgroud)

输出:

[20, 13, 29, 3, 39]
Run Code Online (Sandbox Code Playgroud)

就运行时间而言,取决于数据结构。

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

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

在此输入图像描述

但如果初始数据是 ndarray 对象,那么@richardec@0x263A(对于大型数组)提出的向量化操作会更快。特别是,无论数组大小如何,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'
)
Run Code Online (Sandbox Code Playgroud)

用于生成第二个图的代码(请注意,我必须修改,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'
)
Run Code Online (Sandbox Code Playgroud)

  • 如此美丽、优雅的仅使用内置函数,不会运行超出其需要的运行!发电机表达式摇滚! (8认同)
  • @Graipher 你确定吗?https://ideone.com/UAVscC (4认同)

小智 8

这里有很好的解决方案;只是想演示如何使用 numpy 做到这一点:

\n
>>> import numpy as np\n>>> p[(np.array(p) > 18).argmax():]\n[20, 13, 29, 3, 39]\n
Run Code Online (Sandbox Code Playgroud)\n
\n

由于这里有很多很好的答案,我决定运行一些简单的基准测试。第一个使用长度为 9 的 OP 样本数组 ( [4,9,10,4,20,13,29,3,39])。第二个使用随机生成的长度为 20,000 的数组,其中前半部分在 0 到 15 之间,后半部分在 -20 到 30 之间(以便分割不会发生在中心)。

\n

使用OP的数据(长度为9的数组):

\n
%timeit enke()\n650 ns \xc2\xb1 15.9 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n\n%timeit j1lee1()\n546 ns \xc2\xb1 4.22 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n\n%timeit j1lee2()\n551 ns \xc2\xb1 19 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n\n%timeit j2lee3()\n536 ns \xc2\xb1 12.9 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000000 loops each)\n\n%timeit richardec()\n2.08 \xc2\xb5s \xc2\xb1 16 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

使用长度为 20,000(20,000)的数组:

\n
%timeit enke()\n1.5 ms \xc2\xb1 34.5 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000 loops each)\n\n%timeit j1lee1()\n1.95 ms \xc2\xb1 43 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1000 loops each)\n\n%timeit j1lee2()\n2.1 ms \xc2\xb1 53.7 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n\n%timeit j2lee3()\n2.33 ms \xc2\xb1 96.2 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n\n%timeit richardec()\n13.3 \xc2\xb5s \xc2\xb1 461 ns per loop (mean \xc2\xb1 std. dev. of 7 runs, 100000 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n

生成第二个数组的代码:

\n
p = np.hstack([np.random.randint(0,15,10000),np.random.randint(-20,30,10000)])\n
Run Code Online (Sandbox Code Playgroud)\n

因此,对于小情况,numpy 是一个 slug,不需要。但在大型案例中,numpy 的速度几乎快了 100 倍,而且是最佳选择!:)

\n

  • 非常感谢您的回答!我确实注意到实际用例(这是一个相当大的 pandas 数据帧)的输出时间与发布的其他解决方案有很大差异。 (3认同)

U p*_*63A 7

p我注意到实际上是 Pandas DataFrame 的答案下提到的 OP 。以下是使用 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)
Run Code Online (Sandbox Code Playgroud)

输出:

    0
4  20
5  13
6  29
7   3
8  39
Run Code Online (Sandbox Code Playgroud)

注意:我对 DataFrame 的实际结构一无所知,所以我只是准确地使用了给出的结构。