从Python列表中删除项目

时间:2013-08-01 22:47:20

标签: python list numbers

如何从列表中删除相同的项目? 说

A = [1,2,3,8,7,8,8,7,6]

我想要删除所有8个 我该怎么做?如果数字不合适?

3 个答案:

答案 0 :(得分:4)

执行此操作的简便方法是构建一个新列表,其中包含所有不是8的项目。例如:

B=[item for item in A if item != 8]

如果您确实想要就地执行此操作(例如,因为某些其他对象具有与A相同的列表的引用,并且您希望其他对象看到更改),则可以。但它更棘手。例如,如果您按索引删除,则必须在某个时间点向后移动(因为当您删除第一个8时,所有以后 8都有新的索引,所以你总是要先删除最后一个):

indices = [index for index, item in enumerate(A) if item==8]
for index in reversed(indices):
    del A[index]

或者你可以继续尝试remove直到它失败,但这既丑陋又缓慢:

while True:
    try:
        A.remove(8)
    except ValueError:
        break

事实上,你通常最好仍然创建一个新列表,然后只需将A变更为该新列表的副本:

A[:]=[item for item in A if item != 8]

对于性能测试,我针对所有建议的答案运行this code。测试的算法是:

  • revind:我在这个答案中的第一个就地算法。
  • whiledel:Chris Barker的第一个算法。
  • slide:Chris Barker的第二个算法(固定版本)。
  • genexpr:我的最后一个算法,但使用的是genexpr而不是listcomp。
  • listcomp:我的最后一个算法。
  • filterer:我的最后一个算法,但使用过滤器(请注意,这意味着它在2.x中构建一个列表,但在3.x中构建一个类似于genexpr的迭代器)。

请注意,如果你实际上不需要改变A - 通常你不需要,你可以重新绑定名称 - 复制然后变异算法变得更简单,更快。但我没有在这里测试过。

我也没有完全测试“while True:删除直到错误”算法,或Chris Barker在评论中建议的变化,因为它们显然会慢得多,不值得测试。然而,一个非常快速的测试表明,变化速度大约是预期的2倍,并且几乎在任何测试用例中都比其他任何东西慢几个数量级。

无论如何,测试是从长度为100K或1M的随机列表中删除0(以1/10的次数为1/10),值从0到16,256或65536(不同的值越少,值越高)要删除的命中百分比)。

如果你正在使用CPython,那么listcomp版本总是最快的,尤其是当N很大时。在你使用PyPy时,就地算法可以在N很大且M很小时击败它,但在这种情况下,它们 all 非常快。花哨的幻灯片算法消除了其他就地算法的二次行为,但它也减慢了简单情况下的速度,所以实际上没有哪个地方是明显的赢家。 (这也是迄今为止最复杂的 - 也就是为什么它是唯一一个在第一次尝试时不正确的原因。)如果你绝对相信你只会删除少量副本,并且你正在使用PyPy,考虑一下whiledel解决方案;在任何其他用例中,或者当您不确定用例时,我会使用listcomp。

          64-bit python CPython 3.3.0
          16 values    256 values   65536 values
          100K  1000K  100K  1000K  100K  1000K
revind    0.188 17.3   0.085 1.23   0.074 0.080
whiledel  0.324 19.3   0.206 1.36   0.199 0.203
slide     0.091  0.54  0.097 0.54   0.095 0.538
genepxr   0.094  0.11  0.100 0.11   0.099 0.108
listcomp  0.070  0.08  0.073 0.08   0.071 0.079
filterer  0.081  0.09  0.080 0.09   0.835 0.088

          64-bit python CPython 2.7.2
          16 values    256 values   65536 values
          100K  1000K  100K  1000K  100K  1000K
revind    0.198 17.1   0.089 1.23   0.088 0.955
whiledel  0.345 19.8   0.233 1.36   0.234 0.243
slide     0.095  0.54  0.099 0.55   0.095 0.551
genepxr   0.092  0.11  0.097 0.11   0.107 0.116
listcomp  0.091  0.09  0.099 0.08   0.105 0.114
filterer  0.122  0.23  0.132 0.09   0.135 0.150

          64-bit python PyPy 1.9.0 (Python 2.7.2)
          16 values    256 values   65536 values
          100K  1000K  100K  1000K  100K  1000K
revind    0.266 28.5   0.027 1.97   0.018 0.013
whiledel  0.281 30.2   0.023 1.94   0.034 0.009
slide     0.022  0.39  0.015 0.022  0.006 0.018
genepxr   0.089  0.13  0.087 0.154  0.089 0.147
listcomp  0.052  0.08  0.057 0.073  0.052 0.073
filterer  0.054  0.07  0.053 0.078  0.048 0.074

预测幻灯片的性能有点难度。在大N /小M的情况下,你会期望它会随着时间的推移而消失,但它实际上更慢。但是如果你考虑一下:该算法有效地用N个简单副本替换M个线性移动。虽然前者是O(NM)而后者是O(N),但是在C中循环的乘数(或者更好,在memmove内)比Python中的循环要小得多,你不能忽视除非N / M很大(此时所有的解决方案都是如此之快以至于不重要)。因此,做M Python循环和NM C循环可以很容易地完成N Python循环。

答案 1 :(得分:2)

这些答案中的大多数建议复制数据。如果您的列表足够大,这可能是不希望的。您可以轻松使用

while 8 in A: A.remove(8)

执行此操作而不复制任何数据。但是,这会以二次方运行,如果列表很大,这也是不可取的。要在线性时间内完成而不复制任何数据,请使用:

def remove_all_from_list(L, n):
    i = 0
    while i < len(L):
        if L[i] == n:
            del L[i] # Do not increment i here, because L[i] will change
        else:
            i += 1

>>> A = [1, 2, 3, 8, 7, 8, 8, 7, 6]
>>> remove_all_from_list(A, 8)
>>> A
[1, 2, 3, 7, 7, 6]

编辑:@abarnert提醒我del L [i]是O(N)所以这实际上是二次的。这是O(N)就地解决方案的另一次尝试......

def remove_all_from_list(L, n):
    # indices takes *worse-case* O(N) space, but typical-case much less
    indices = [i for i, x in enumerate(L) if x==n]
    indices_seen = 0
    num_indices = len(indices)
    for i in xrange(len(L)):
        while (indices_seen < num_indices and 
            i + indices_seen == indices[indices_seen]):
            indices_seen += 1
        if i+indices_seen >= len(L):
            break
        L[i] = L[i+indices_seen]            
    L[-indices_seen:] = []

这将在最后进行所有的洗牌,因此每个元素最多移动一次。我意识到这将花费与abarnert的复制方法一样多的时间。我只是想办法减少内存使用量,以防你有一个非常大的列表。


最终编辑:速度测试(不像@ abarnert那样全面)

import random
L = range(30)*10000
random.shuffle(L)
from copy import copy

for fn in [remove_1, remove_2, remove_3, remove_4, remove_5, remove_6]:
    print fn.__name__
    %timeit fn(copy(L), 8)

remove_1 # listcomp
10 loops, best of 3: 39.1 ms per loop
remove_2 # revind
1 loops, best of 3: 1.7 s per loop
remove_3 # try: remove; except: break
1 loops, best of 3: 65.7 s per loop
remove_4 # while n in L: L.remove(n)
1 loops, best of 3: 129 s per loop
remove_5 # whiledel
1 loops, best of 3: 1.87 s per loop
remove_6 # slide
1 loops, best of 3: 227 ms per loop

答案 2 :(得分:0)

In [13]: A= [1, 2, 3, 8, 7, 8, 8, 7, 6]

In [14]: [i for i in A if i!=8]
Out[14]: [1, 2, 3, 7, 7, 6]

In [15]: filter(lambda i: i!=8, A)
Out[15]: [1, 2, 3, 7, 7, 6]