【问题标题】:How to remove multiple indexes from a list at the same time? [duplicate]如何同时从列表中删除多个索引? [复制]
【发布时间】:2012-07-03 10:20:55
【问题描述】:

假设我在这里有这个清单:

list = [a, b, c, d, e, f, g]

如何同时删除说索引2, 3, 45

pop 不接受多个值。我该怎么做?

【问题讨论】:

    标签: python list


    【解决方案1】:

    对于不同方式的性能没有太多提示,所以我进行了测试,以所有 3 种通常不同的方法从 50000 中删除 5000 项,对我来说,numpy 是赢家(如果你有适合 numpy 的元素) :

    • 枚举列表理解为 7.5 秒 [在另一台 PC 上为 4.5 秒]
    • 0.08 秒以相反的顺序删除项目 [0.017 (!) 秒]
    • numpy.delete 为 0.009 秒 [0.006 秒]

    这是我计时的代码(如果可以直接处理 numpy 数组,则可以删除从/到列表的第三个函数转换):

    import time
    import numpy as np
    import random
    
    def del_list_indexes(l, id_to_del):
        somelist = [i for j, i in enumerate(l) if j not in id_to_del]
        return somelist
    
    def del_list_inplace(l, id_to_del):
        for i in sorted(id_to_del, reverse=True):
            del(l[i])
    
    def del_list_numpy(l, id_to_del):
        arr = np.array(l, dtype='int32')
        return list(np.delete(arr, id_to_del))
    
    l = range(50000)
    random.shuffle(l)
    remove_id = random.sample(range(len(l)), 5000) # 10% ==> 5000
    
    # ...
    

    【讨论】:

    • del_list_indexes 如果先将id_to_del 转换为集合,应该会快得多。
    • @C.Yduqoli 如果将set 用于enumerate,我得到的时间是:enumerate = 0.0041、del = 0.035、numpy = 0.0079,已枚举为预期的最快。
    【解决方案2】:

    另一个选项(就地,任何索引组合):

    _marker = object()
    
    for i in indices:
        my_list[i] = _marker  # marked for deletion
    
    obj[:] = [v for v in my_list if v is not _marker]
    

    【讨论】:

      【解决方案3】:
      lst = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
      lst = lst[0:2] + lst[6:]
      

      这是一个单步操作。它不使用循环,因此执行速度很快。它使用列表切片。

      【讨论】:

      • 您应该包括对您的代码的解释。一般来说,描述某些命令或代码解决问题的方式和原因会更有用,因为它有助于 OP 和其他人解决类似问题。
      • 请查看版本并给予指导。
      • 想知道为什么 1460 天和 23410 次浏览,没有想出这个简单的解决方案。这有点令人沮丧。
      • 我已编辑您的答案以改进语法并使用 lst 作为变量名,因为 list 是 Python 数据类型。没有人建议将此作为解决方案的原因是,这会创建一个新的列表对象(基于原始列表对象),而不是实际修改原始列表对象。由于最终结果是相似的,我会赞成你的答案。欢迎来到Stack Exchange
      • [6:] 是做什么的?
      【解决方案4】:

      老问题,但我有答案。

      首先,像这样细读列表中的元素:

      for x in range(len(yourlist)):
          print '%s: %s' % (x, yourlist[x])
      

      然后,使用您要弹出的元素的索引列表调用此函数。它足够健壮,列表的顺序无关紧要。

      def multipop(yourlist, itemstopop):
          result = []
          itemstopop.sort()
          itemstopop = itemstopop[::-1]
          for x in itemstopop:
              result.append(yourlist.pop(x))
          return result
      

      作为奖励,结果应该只包含您想要删除的元素。

      在 [73] 中:mylist = ['a','b','c','d','charles']

      在 [76] 中:对于范围内的 x(len(mylist)):

            mylist[x])
      

      ....:

      0: 一个

      1: b

      2:c

      3: d

      4: 查尔斯

      ...

      在 [77] 中:multipop(mylist, [0, 2, 4])

      输出[77]: ['charles', 'c', 'a']

      ...

      在 [78] 中:我的列表

      输出[78]: ['b', 'd']

      【讨论】:

        【解决方案5】:

        如果你可以使用numpy,那么你可以删除多个索引:

        >>> import numpy as np
        >>> a = np.arange(10)
        >>> np.delete(a,(1,3,5))
        array([0, 2, 4, 6, 7, 8, 9])
        

        如果您使用np.r_,您可以将切片与单个索引结合起来:

        >>> np.delete(a,(np.r_[0:5,7,9]))
        array([5, 6, 8])
        

        但是,删除的不是in place,所以你要给它赋值。

        【讨论】:

          【解决方案6】:

          如果它们是连续的,你可以这样做

          x[2:6] = []
          

          如果要删除不连续的索引,那就有点棘手了。

          x = [v for i,v in enumerate(x) if i not in frozenset((2,3,4,5))] 
          

          【讨论】:

          • 请注意,您必须使用枚举来执行此操作的原因是因为您无法删除正在迭代的内容,那会很糟糕。
          • @Trick 从技术上讲,只有在您使用真正的迭代器时才如此。如果你使用一个简单的 int 来保持你在列表中的位置,没有任何问题,除了如果你在低于当前位置的位置删除一些东西,索引会跳过。
          • 是的,这可能会越界,通常不是一件好事。
          【解决方案7】:
          remove_indices = [1,2,3]
          somelist = [i for j, i in enumerate(somelist) if j not in remove_indices]
          

          例子:

          In [9]: remove_indices = [1,2,3]
          
          In [10]: somelist = range(10)
          
          In [11]: somelist = [i for j, i in enumerate(somelist) if j not in remove_indices]
          
          In [12]: somelist
          Out[12]: [0, 4, 5, 6, 7, 8, 9]
          

          【讨论】:

          • 正如@C.Yduqoli 在下面指出的那样,这是最有效的解决方案(我们不需要不断重建列表),但您必须先将remove_indices 转换为set
          • 最优雅的解决方案
          【解决方案8】:

          您需要循环执行此操作,没有内置操作可以一次删除多个索引。

          您的示例实际上是一个连续的索引序列,因此您可以这样做:

          del my_list[2:6]
          

          删除从 2 开始并在 6 之前结束的切片。

          从您的问题中不清楚您通常是否需要删除任意索引集合,或者它是否始终是一个连续的序列。

          如果您有任意索引集合,则:

          indexes = [2, 3, 5]
          for index in sorted(indexes, reverse=True):
              del my_list[index]
          

          注意,你需要以相反的顺序删除它们,这样你就不会丢掉后面的索引。

          【讨论】:

          • +1 如果我们使用my_list.pop(index) 而不是del my_list[index],这会有什么不同?有关系吗?在效率还是语义方面?
          • 请原谅我的评论,我正在学习 Python,但是 Python 在循环删除时重新索引数组?为什么如果按正常顺序循环会引发未定义的偏移异常?
          • @kitensei 它确实会重新索引,但是因为首先以相反的顺序对索引进行排序,所以我们从列表的右端删除,所以我们在删除元素之前永远不会重新索引它.
          • 向后循环索引。天才!我一直在这里梦想着一些可笑的递减指数的情况,而这要优雅得多!
          • @Levon 我认为主要区别在于您删除了 pop returns the value。由于我们不需要要删除的值,因此在这种情况下我们可能应该使用del,尽管两者都可以。
          猜你喜欢
          • 2017-03-05
          • 2016-12-03
          • 2020-02-20
          • 1970-01-01
          • 1970-01-01
          • 2020-02-21
          • 2023-02-23
          • 1970-01-01
          • 2022-06-16
          相关资源
          最近更新 更多