从列表中删除多个元素


160

是否可以同时从列表中删除多个元素?如果我想删除索引0和2的元素,然后尝试类似del somelist[0],然后尝试del somelist[2],则第二条语句实际上将删除somelist[3]

我想我总是可以先删除编号较高的元素,但我希望有更好的方法。

Answers:


110

可能不是此问题的最佳解决方案:

indices = 0, 2
somelist = [i for j, i in enumerate(somelist) if j not in indices]

2
几乎,仅当您删除整个列表时。它是len(indices)* len(somelist)。它还会创建可能不希望使用的副本
Richard Levasseur

如果要检查列表中的值,则为。“ in”运算符适用于列表的值,而适用于字典键。如果我错了,请点我给PEP /参考
理查德·勒瓦瑟

5
我选择元组作为索引的原因仅仅是为了简化记录。对于set()给出O(n)来说将是一项完美的工作
SilentGhost 2009年

18
这根本不是从某列表中删除项目,而是创建一个全新的列表。如果有什么东西保留着对原始列表的引用,它将仍然包含所有项目。
汤姆·未来

2
@SilentGhost无需进行枚举。怎么样:somelist = [ lst[i] for i in xrange(len(lst)) if i not in set(indices) ]
ToolmakerSteve

183

由于某种原因,我不喜欢这里的任何答案。是的,它们可以工作,但是严格来说,大多数不是删除列表中的元素,对吗?(但是要进行复制,然后用编辑后的副本替换原始副本)。

为什么不先删除较高的索引呢?

是否有一个原因?我会做:

for i in sorted(indices, reverse=True):
    del somelist[i]

如果您真的不想向后删除项目,那么我想您应该减少大于上一个删除索引的索引值(因为您使用的是不同的列表,所以不能真正使用相同的索引)或使用列表的副本(不会被“删除”,而是将原件替换为已编辑的副本)。

我是否在这里缺少任何东西,有什么理由不以相反的顺序删除?


1
我不知道为什么未选择此答案!谢谢你
swathis

4
有两个原因。(a)对于列表,平均时间(假设使用随机索引)的时间复杂度将高于“制作副本”方法(使用一组索引),因为某些元素需要多次向前移动。(b)至少对我来说,很难阅读,因为有一个排序函数与任何实际的程序逻辑都不对应,仅出于技术原因而存在。即使到现在我已经完全理解了逻辑,我仍然觉得很难阅读。
不朽之夜

1
@ImperishableNight您能否详细说明(a)?我不明白“某些要素需要转移”。对于(b),如果您需要阅读清楚,则可以只定义一个函数。
tglaria

109

如果要删除多个不相邻的项目,那么您描述的是最好的方法(是的,请确保从最高索引开始)。

如果您的项目相邻,则可以使用切片分配语法:

a[2:10] = []

95
您也可以说del a[2:10]具有相同的效果。
某事

8
@sth有趣的是,del的速度比分配速度快。
thefourtheye

24

您可以使用numpy.delete以下方法:

import numpy as np
a = ['a', 'l', 3.14, 42, 'u']
I = [0, 2]
np.delete(a, I).tolist()
# Returns: ['l', '42', 'u']

如果您不介意以最后一个numpy数组结尾,则可以省略.tolist()。您还将看到一些相当大的速度改进,从而使它成为更具可扩展性的解决方案。我尚未对其进行基准测试,但是numpy操作是用C或Fortran编写的已编译代码。


1
当元件不是连续的1通用的解决方案
noɥʇʎԀʎzɐɹƆ

1
问题在这里,如何删除['a',42]。
evanhutomo

与其他解决方案相比,此解决方案在速度方面拥有巨大的加分。我能说的是,对于一个非常大的数据集,要花好几秒钟才能获得好的结果,这需要我花费几分钟。
莱格尔

18

作为Greg答案的一种专业,您甚至可以使用扩展切片语法。例如。如果要删除项目0和2:

>>> a= [0, 1, 2, 3, 4]
>>> del a[0:3:2]
>>> a
[1, 3, 4]

当然,这并不涉及任何选择,但可以删除两个项目。


16

作为功​​能:

def multi_delete(list_, *args):
    indexes = sorted(list(args), reverse=True)
    for index in indexes:
        del list_[index]
    return list_

n log(n)时间运行,这应该使其成为最快的正确解决方案。


1
带有args.sort()。reverse()的版本肯定更好。它也适用于dict,而不是抛出或更糟的是无声地损坏。

未为元组定义sort(),您必须首先转换为list。sort()返回None,因此您不能在其上使用reverse()。
SilentGhost

@ R. Pate:因此,我删除了第一个版本。谢谢。// @ SilentGhost:已修复。
Nikhil Chelliah,2009年

@Nikhil:不,您没有;)args = list(args)args.sort()args.reverse()但更好的选择是:args = sorted(args,reverse = True)
SilentGhost

2
n log n?真?我认为del list[index]不是O(1)。
user202729

12

因此,您本质上想一次删除多个元素吗?在这种情况下,下一个要删除的元素的位置将被偏移,但是之前删除了许多元素。

我们的目标是删除所有预计算为索引1、4和7的元音。请注意,to_delete索引重要的是升序排列,否则它将不起作用。

to_delete = [1, 4, 7]
target = list("hello world")
for offset, index in enumerate(to_delete):
  index -= offset
  del target[index]

如果您想以任何顺序删除元素,将更加复杂。IMO,排序to_delete可能比弄清楚何时应该从中减去应该不容易index


8

我是Python的初学者,至少我现在的编程很粗糙,但是我的解决方案是结合使用我在早期教程中学到的基本命令:

some_list = [1,2,3,4,5,6,7,8,10]
rem = [0,5,7]

for i in rem:
    some_list[i] = '!' # mark for deletion

for i in range(0, some_list.count('!')):
    some_list.remove('!') # remove
print some_list

显然,由于必须选择“删除标记”字符,因此有其局限性。

至于列表大小可扩展的性能,我确定我的解决方案不是最佳的。但是,它很简单,我希望能吸引其他初学者,并且可以在some_list格式众所周知的简单情况下使用,例如始终为数字...


2
而不是使用'!' 作为您的特殊字符,请使用“无”。这可以确保每个角色都有效,并释放您的可能性
portforwardpodcast 2015年

5

这是一种替代方法,它不使用enumerate()创建元组(如SilentGhost的原始答案)。

这对我来说似乎更具可读性。(如果我习惯于使用枚举,也许会有所不同。)CAVEAT:我尚未测试两种方法的性能。

# Returns a new list. "lst" is not modified.
def delete_by_indices(lst, indices):
    indices_as_set = set(indices)
    return [ lst[i] for i in xrange(len(lst)) if i not in indices_as_set ]

注意:Python 2.7语法。对于Python 3,xrange=> range

用法:

lst = [ 11*x for x in xrange(10) ]
somelist = delete_by_indices( lst, [0, 4, 5])

清单:

[11, 22, 33, 66, 77, 88, 99]

-奖金-

从列表中删除多个值。也就是说,我们具有要删除的值:

# Returns a new list. "lst" is not modified.
def delete__by_values(lst, values):
    values_as_set = set(values)
    return [ x for x in lst if x not in values_as_set ]

用法:

somelist = delete__by_values( lst, [0, 44, 55] )

清单:

[11, 22, 33, 66, 77, 88, 99]

这是与以前相同的答案,但是这次我们提供了要删除的VALUES [0, 44, 55]


我认为@SilentGhost的代码很难读,因为用于枚举结果的非描述性变量名。同样,原谅将使它更容易阅读。因此,这里是怎么会有我的字他的解决方案(添加,性能“设置”): [ value for (i, value) in enumerate(lst) if i not in set(indices) ]。但是,我将在这里保留我的答案,因为我还将显示如何按值删除。这是一个简单的案例,但可能会对某人有所帮助。
ToolmakerSteve

@ Veedrac-谢谢;我已经重新编写以首先构建集合。您认为-现在比SilentGhost更快的解决方案?(我不认为这是重要的,足以实际上一次,只是问你的意见。)同样,我会重新写SilentGhost的版本indices_as_set = set(indices)[ value for (i, value) in enumerate(lst) if i not in indices_as_set ]以加快速度。
制造商史蒂夫(Steve)2014年

双重下划线存在样式上的原因delete__by_values()吗?
汤姆(Tom)

5

使用列表索引值的另一种列表理解方法:

stuff = ['a', 'b', 'c', 'd', 'e', 'f', 'woof']
index = [0, 3, 6]
new = [i for i in stuff if stuff.index(i) not in index]

返回:

['b', 'c', 'e', 'f']

好的答案,但是将索引列表命名index为误导性的,因为在列表迭代器中使用了该方法index()
Joe

4

这是另一种删除适当元素的方法。同样,如果您的清单很长,则速度会更快。

>>> a = range(10)
>>> remove = [0,4,5]
>>> from collections import deque
>>> deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)

>>> timeit.timeit('[i for j, i in enumerate(a) if j not in remove]', setup='import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1)
0.1704120635986328

>>> timeit.timeit('deque((list.pop(a, i) for i in sorted(remove, reverse=True)), maxlen=0)', setup='from collections import deque;import random;remove=[random.randrange(100000) for i in range(100)]; a = range(100000)', number=1)
0.004853963851928711

+1:有趣的使用双端队列在表达式中执行for动作,而不需要“ for ..:”块。但是,对于这种简单的情况,我发现Nikhil的for块更具可读性。
ToolmakerSteve 2013年

4

已经提到了这一点,但是以某种方式没有人设法正确地做到这一点。

O(n)解决办法是:

indices = {0, 2}
somelist = [i for j, i in enumerate(somelist) if j not in indices]

这确实接近SilentGhost的版本,但增加了两个花括号。


O(n)如果您要计算log(len(indices))每次迭代的查找次数,则不是这样。
疯狂物理学家

@MadPhysicist j not in indicesO(1)
Veedrac

我不确定你怎么得到那个号码。由于索引是一个集合,因此j not in indices仍需要查找,即O(log(len(indices)))。虽然我同意2元素集中的查找符合条件O(1),但一般情况下是O(log(N))。任一种方法O(N log(N))仍然有效O(N^2)
疯狂物理学家,2015年


两个牙套到底是做什么的?
核03020704

4
l = ['a','b','a','c','a','d']
to_remove = [1, 3]
[l[i] for i in range(0, len(l)) if i not in to_remove])

它基本上与票数最高的答案相同,只是编写方式不同。注意,使用l.index()不是一个好主意,因为它不能处理列表中的重复元素。


2

Remove方法将导致列表元素发生大量移位。我认为最好复制:

...
new_list = []
for el in obj.my_list:
   if condition_is_true(el):
      new_list.append(el)
del obj.my_list
obj.my_list = new_list
...

2

从技术上讲,答案是否定的,不可能在同一时间删除两个对象。但是,可以在一行漂亮的python中删除两个对象。

del (foo['bar'],foo['baz'])

将删除后foo['bar']foo['baz']


这是从dict对象而不是列表中删除的,但是我仍然对其+1,因为它真是太漂亮了!
乌尔夫·阿斯拉克

它也适用于具有适当语法的列表。但是,声称不能同时删除两个对象为假;参见@bobince的回答
Pedro Gimeno

2

我们可以通过在索引列表降序排序后使用for循环遍历索引来实现此目的

mylist=[66.25, 333, 1, 4, 6, 7, 8, 56, 8769, 65]
indexes = 4,6
indexes = sorted(indexes, reverse=True)
for i in index:
    mylist.pop(i)
print mylist

2

对于listA的索引0和2:

for x in (2,0): listA.pop(x)

对于一些要从listA中删除的随机索引:

indices=(5,3,2,7,0) 
for x in sorted(indices)[::-1]: listA.pop(x)

2

我想找到一种比较不同解决方案的方法,这些解决方案可以轻松旋转旋钮。

首先,我生成了数据:

import random

N = 16 * 1024
x = range(N)
random.shuffle(x)
y = random.sample(range(N), N / 10)

然后我定义了我的功能:

def list_set(value_list, index_list):
    index_list = set(index_list)
    result = [value for index, value in enumerate(value_list) if index not in index_list]
    return result

def list_del(value_list, index_list):
    for index in sorted(index_list, reverse=True):
        del(value_list[index])

def list_pop(value_list, index_list):
    for index in sorted(index_list, reverse=True):
        value_list.pop(index)

然后我用来timeit比较解决方案:

import timeit
from collections import OrderedDict

M = 1000
setup = 'from __main__ import x, y, list_set, list_del, list_pop'
statement_dict = OrderedDict([
    ('overhead',  'a = x[:]'),
    ('set', 'a = x[:]; list_set(a, y)'),
    ('del', 'a = x[:]; list_del(a, y)'),
    ('pop', 'a = x[:]; list_pop(a, y)'),
])

overhead = None
result_dict = OrderedDict()
for name, statement in statement_dict.iteritems():
    result = timeit.timeit(statement, number=M, setup=setup)
    if overhead is None:
        overhead = result
    else:
        result = result - overhead
        result_dict[name] = result

for name, result in result_dict.iteritems():
    print "%s = %7.3f" % (name, result)

输出量

set =   1.711
del =   3.450
pop =   3.618

因此,索引为a的生成器set就是赢家。然后del快一点pop


感谢您的比较,这使我进行了自己的测试(实际上只是借用了您的代码),并且要删除的项目数量很少,创建SET的开销使其成为最差的解决方案(将10、100、500用于长度为y,您会看到)。在大多数情况下,这取决于应用程序。
tglaria

2

您可以使用以下逻辑:

my_list = ['word','yes','no','nice']

c=[b for i,b in enumerate(my_list) if not i in (0,2,3)]

print c

2

从最高索引中删除的想法的另一种实现。

for i in range(len(yourlist)-1, -1, -1):
    del yourlist(i)

1

实际上,我可以想到两种方法:

  1. 像这样对列表进行切片(这将删除第1,第3和第8个元素)

    somelist = somelist [1:2] + somelist [3:7] + somelist [8:]

  2. 做到这一点,但一次:

    somelist.pop(2)somelist.pop(0)


1

您可以对字典而不是列表进行这种操作。在列表中,元素是按顺序排列的。在字典中,它们仅取决于索引。

简单的代码只是为了解释这样做

>>> lst = ['a','b','c']
>>> dct = {0: 'a', 1: 'b', 2:'c'}
>>> lst[0]
'a'
>>> dct[0]
'a'
>>> del lst[0]
>>> del dct[0]
>>> lst[0]
'b'
>>> dct[0]
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    dct[0]
KeyError: 0
>>> dct[1]
'b'
>>> lst[1]
'c'

一种“转换”字典中的列表的方法是:

>>> dct = {}
>>> for i in xrange(0,len(lst)): dct[i] = lst[i]

逆是:

lst = [dct[i] for i in sorted(dct.keys())] 

无论如何,我认为最好从您所说的更高的索引中删除。


Python是否保证[dct中i的dct [i]]始终使用i的递增值?如果是这样,list(dct.values())肯定会更好。

我没有考虑这个。你是对的。当我阅读[here] [1]时,不能保证将按顺序(至少是预期的顺序)来拣货。我编辑了。[1]:docs.python.org/library/stdtypes.html#dict.items
Andrea Ambu,2009年

2
这个答案以根本错误的方式谈论字典。字典具有键(不是索引)。是的,键/值对彼此独立。不,您删除条目的顺序无关紧要。仅转换为字典以从列表中删除某些元素将是过大的。
ToolmakerSteve 2013年

1

概括来自@sth的评论。在实现abc.MutableSequence的任何类中list,尤其是通过__delitem__magic方法,都可以删除项目。此方法的工作方式类似于__getitem__,意味着它可以接受整数或切片。这是一个例子:

class MyList(list):
    def __delitem__(self, item):
        if isinstance(item, slice):
            for i in range(*item.indices(len(self))):
                self[i] = 'null'
        else:
            self[item] = 'null'


l = MyList(range(10))
print(l)
del l[5:8]
print(l)

这将输出

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 'null', 'null', 'null', 8, 9]

1

仅出于这个原因导入它可能会过大,但是如果您碰巧正在使用它pandas,则解决方案非常简单明了:

import pandas as pd
stuff = pd.Series(['a','b','a','c','a','d'])
less_stuff = stuff[stuff != 'a']  # define any condition here
# results ['b','c','d']

1
some_list.remove(some_list[max(i, j)])

避免了排序成本,并且不必显式复制列表。


0

其中之一怎么样(我是Python的新手,但看起来还不错):

ocean_basin = ['a', 'Atlantic', 'Pacific', 'Indian', 'a', 'a', 'a']
for i in range(1, (ocean_basin.count('a') + 1)):
    ocean_basin.remove('a')
print(ocean_basin)

[“大西洋”,“太平洋”,“印度”]

ob = ['a', 'b', 4, 5,'Atlantic', 'Pacific', 'Indian', 'a', 'a', 4, 'a']
remove = ('a', 'b', 4, 5)
ob = [i for i in ob if i not in (remove)]
print(ob)

[“大西洋”,“太平洋”,“印度”]


0

到目前为止提供的答案都不进行删除到位的O(N)在列表的长度为指标,删除任意数量的,所以这里是我的版本:

def multi_delete(the_list, indices):
    assert type(indices) in {set, frozenset}, "indices must be a set or frozenset"
    offset = 0
    for i in range(len(the_list)):
        if i in indices:
            offset += 1
        elif offset:
            the_list[i - offset] = the_list[i]
    if offset:
        del the_list[-offset:]

# Example:
a = [0, 1, 2, 3, 4, 5, 6, 7]
multi_delete(a, {1, 2, 4, 6, 7})
print(a)  # prints [0, 3, 5]

0

您也可以使用remove。

delete_from_somelist = []
for i in [int(0), int(2)]:
     delete_from_somelist.append(somelist[i])
for j in delete_from_somelist:
     newlist = somelist.remove(j)

0

我将所有内容放到一个list_diff函数中,该函数仅将两个列表作为输入并返回它们的差,同时保留第一个列表的原始顺序。

def list_diff(list_a, list_b, verbose=False):

    # returns a difference of list_a and list_b,
    # preserving the original order, unlike set-based solutions

    # get indices of elements to be excluded from list_a
    excl_ind = [i for i, x in enumerate(list_a) if x in list_b]
    if verbose:
        print(excl_ind)

    # filter out the excluded indices, producing a new list 
    new_list = [i for i in list_a if list_a.index(i) not in excl_ind]
    if verbose:
        print(new_list)

    return(new_list)

用法示例:

my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'woof']
# index = [0, 3, 6]

# define excluded names list
excl_names_list = ['woof', 'c']

list_diff(my_list, excl_names_list)
>> ['a', 'b', 'd', 'e', 'f']
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.