【问题标题】:Get list-elements that are not in index-list获取不在索引列表中的列表元素
【发布时间】:2017-08-12 10:19:35
【问题描述】:

我有一个索引列表:

idx = [1,4,5]

以及感兴趣的列表:

mylist = ['a','b','c','d','e','f']

我想从mylist 中取出索引不在idx 中的所有元素。

所以结果应该是:

['a','c','d']

我也可以将mylist 拆分为['a','c','d']['b','e','f'],因为无论如何我都会使用它们。

一个 numpy 版本是可以的,虽然我现在实际上只有两个列表。

【问题讨论】:

    标签: python list numpy


    【解决方案1】:

    通过numpy,您可以使用掩码数组。

    import numpy as np
    x=np.array(mylist)
    mask=np.full(len(mylist),True,dtype=bool)
    mask[idx]=False
    y=x[mask]
    z=x[~mask]
    print(y,z)
    

    【讨论】:

    • 非常感谢!您的解决方案甚至在下面进行了基准测试,当我的列表很大时,我现在正在使用您的答案。因为我只能选择一个答案,所以我还是把那个答案给了 MSeifert,因为他现在已经涵盖了更多的信息。
    【解决方案2】:

    如果你想同时保留两者并且你可以使用一个函数,你可以使用:

    def partition_on_index(it, indices):
        indices = set(indices)   # convert to set for fast lookups
        l1, l2 = [], []
        l_append = (l1.append, l2.append)
        for idx, element in enumerate(it):
            l_append[idx in indices](element)
        return l1, l2
    

    这里有一个技巧,那就是l_append[idx in indices]idx in indices 将返回一个布尔值,表示条件是True 还是False。由于布尔值是 Python 中整数的子类,因此可以将其解释为 0(如果是 False)或 1(如果是 True),因此是 l_append 元组的有效索引。

    l_append 元组因此可以方便地替代循环内的if ... else ... hoists 查找append 方法,从而提高函数的速度少量。所以这相当于:

    def partition_on_index_probably_slower(it, indices):
        indices = set(indices)   # convert to set for fast lookups
        l1, l2 = [], []
        for idx, element in enumerate(it):
            if idx in indices:
                l2.append(element)
            else:
                l1.append(element)
        return l1, l2
    

    但是让我们看一个例子,第一个函数是如何工作的:

    >>> idx = [1,4,5]
    >>> mylist = ['a','b','c','d','e','f']
    >>> l1, l2 = partition_on_index(mylist, idx)
    >>> l1
    ['a', 'c', 'd']
    >>> l2
    ['b', 'e', 'f']
    

    时间安排:

    我使用来自this answer 的框架来衡量性能:

    import random
    import numpy as np
    
    def func1(mylist, idx):
        idx = set(idx)
        in_idx, not_in_idx = [], []
        for i, e in enumerate(mylist):
            (not_in_idx, in_idx)[i in idx].append(e)
        return in_idx, not_in_idx
    
    def partition_on_index(it, indices):
        indices = set(indices)   # convert to set for fast lookups
        l1, l2 = [], []
        l_append = (l1.append, l2.append)
        for idx, element in enumerate(it):
            l_append[idx in indices](element)
        return l1, l2
    
    def func2(mylist, idx):
        x = np.asarray(mylist)
        mask = np.ones(len(mylist), dtype=bool)
        mask[idx] = False
        return x[mask], x[~mask]
    
    # Timing setup
    timings = {func1: [], partition_on_index: [], func2: []}
    sizes = [2**i for i in range(1, 20, 2)]
    
    # Timing
    for size in sizes:
        mylist = list(range(size))
        indices = list({random.randint(0, size-1) for _ in range(size//2)})
        for func in timings:
            res = %timeit -o func(mylist, indices)
            timings[func].append(res)
    

    所以对于小列表,函数partition_on_index 表现最好。但是,如果输入包含数千个(或更多)项,您可能会使用 Dmitri Chubarov 的 NumPy 方法获得更快的结果。然而,所有方法的性能都渐近相同,性能仅相差 2-5 倍。

    【讨论】:

    • 谢谢你,这真的很酷。现在我只需要我的列表大于 1100 个元素并相应地选择方法。
    • @Make42 是的,类似的。情节是对数对数,所以它实际上是 NumPy 开始获胜的大约 2000 个元素。
    【解决方案3】:

    我在列表理解中找到的一个解决方案是:

    idx = set(idx)
    [e for i, e in enumerate(mylist) if i not in idx]
    

    如果我想要两个列表,正如我在问题中提到的,我可以这样做:

    idx = set(idx)
    in_idx, not_in_idx = [], []
    for i, e in enumerate(mylist):
        (not_in_idx, in_idx)[i in idx].append(e)
    

    我会得到:

    >>> not_in_idx
    ['a', 'c', 'd']
    >>> in_idx
    ['b', 'e', 'f']
    

    【讨论】:

    • idx成为速度第一的集合。
    • @AlexHall:像这样?
    • 不,这是理解的每一次迭代。以前做。
    • 不要在任何地方将其转换为set(一次就足够了),您当前的代码会生成一整套不必要的副本(双关语)。此外,如果您对如何“提升”append 方法查找感兴趣,您可能需要查看other answer
    • @MSeifert:啊,谢谢,那不是故意的。我只是忘了删除它。不确定“提升方法”是什么意思。你的意思是,它在for循环之外?有什么优势?
    猜你喜欢
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-05
    • 2023-02-04
    相关资源
    最近更新 更多