¿Eliminar todas las apariciones de un valor de una lista?

378

En Python remove()eliminará la primera aparición de valor en una lista.

¿Cómo eliminar todas las apariciones de un valor de una lista?

Esto es lo que tengo en mente:

>>> remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
[1, 3, 4, 3]
riza
fuente

Respuestas:

506

Enfoque funcional:

Python 3.x

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter((2).__ne__, x))
[1, 3, 3, 4]

o

>>> x = [1,2,3,2,2,2,3,4]
>>> list(filter(lambda a: a != 2, x))
[1, 3, 3, 4]

Python 2.x

>>> x = [1,2,3,2,2,2,3,4]
>>> filter(lambda a: a != 2, x)
[1, 3, 3, 4]
Mark Rushakoff
fuente
120
Use la comprensión de la lista sobre el filtro + lambda; el primero es más legible además de generalmente más eficiente.
habnabit
17
s / generalmente / generalmente siendo /
habnabit
99
El código para la sugerencia de habnabit se ve así:[y for y in x if y != 2]
coredumperror
8
No llamaría a esta solución la mejor. Las comprensiones de listas son más rápidas y fáciles de entender mientras se hojea el código. Esto preferiría ser más de Perl que Python.
Peter Nimroot
3
-1 para invocar directamente __ne__. Comparar dos valores es un proceso mucho más complejo que simplemente llamar __eq__o __ne__en uno de ellos. Puede funcionar correctamente aquí porque solo está comparando números, pero en el caso general eso es incorrecto y es un error.
Aran-Fey
212

Puedes usar una lista de comprensión:

def remove_values_from_list(the_list, val):
   return [value for value in the_list if value != val]

x = [1, 2, 3, 4, 2, 2, 3]
x = remove_values_from_list(x, 2)
print x
# [1, 3, 4, 3]
mhawke
fuente
77
¿Cómo eliminaría elementos sin verificarlos?
Alexander Ljungberg
18
Esto no modifica la lista original pero devuelve una nueva lista.
John Y
66
@Selinap: No, esto es óptimo ya que escanea la lista solo una vez. En su código original, tanto el inoperador como el removemétodo escanean la lista completa (hasta encontrar una coincidencia) para que termine escaneando la lista varias veces de esa manera.
John Kugelman
44
@mhawke, @John Y: simplemente use x [:] = ... en lugar de x = y estará "en el lugar" en lugar de simplemente volver a vincular el nombre 'x' (la velocidad es esencialmente la misma y MUCHO más rápido que x .remove puede ser !!!).
Alex Martelli
10
Voto por esto porque después de 6 años de Python todavía no entiendo Lambdas :)
Benjamin
107

Puede usar la asignación de divisiones si la lista original debe modificarse, mientras sigue utilizando una comprensión eficiente de la lista (o expresión del generador).

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> x[:] = (value for value in x if value != 2)
>>> x
[1, 3, 4, 3]
A. coadyuvante
fuente
1
@Selinap: el filtro no modifica la lista, devuelve una nueva lista.
EM
las comprensiones de filtros y listas no modifican una lista. la asignación de rebanadas sí. y el ejemplo original lo hace.
A. Coady
77
Me gusta esto porque modifica la lista a la que x se refiere. Si hay otras referencias a esa lista, también se verán afectadas. Esto contrasta con las x = [ v for v in x if x != 2 ]propuestas, que crean una nueva lista y cambian x para referirse a ella, dejando intacta la lista original.
Hannes
40

Repetir la solución de la primera publicación de una manera más abstracta:

>>> x = [1, 2, 3, 4, 2, 2, 3]
>>> while 2 in x: x.remove(2)
>>> x
[1, 3, 4, 3]
canguelo
fuente
19
Sin embargo, es O (n * n).
Hannes
@Hannes, ¿no sería O (n) ya que está pasando por el ciclo solo una vez y al mismo tiempo eliminando el elemento?
penta
1
Considere x = [1] * 10000 + [2] * 1000. El cuerpo del bucle se ejecuta 1000 veces y .remove () tiene que omitir 10000 elementos cada vez que se invoca. Eso huele a O (n * n) para mí, pero no es una prueba. Creo que la prueba sería suponer que el número de 2 en la lista es proporcional a su longitud. Ese factor de proporcionalidad luego desaparece en la notación big-O. Sin embargo, el mejor caso de solo un número constante de 2 en la lista no es O (n ^ 2), solo O (2n), que es O (n).
Hannes
23

Vea la solución simple

>>> [i for i in x if i != 2]

Esto devolverá una lista con todos los elementos de xsin2

Shameem
fuente
11

Todas las respuestas anteriores (aparte de Martin Andersson) crean una nueva lista sin los elementos deseados, en lugar de eliminar los elementos de la lista original.

>>> import random, timeit
>>> a = list(range(5)) * 1000
>>> random.shuffle(a)

>>> b = a
>>> print(b is a)
True

>>> b = [x for x in b if x != 0]
>>> print(b is a)
False
>>> b.count(0)
0
>>> a.count(0)
1000

>>> b = a
>>> b = filter(lambda a: a != 2, x)
>>> print(b is a)
False

Esto puede ser importante si tiene otras referencias a la lista dando vueltas.

Para modificar la lista en su lugar, use un método como este

>>> def removeall_inplace(x, l):
...     for _ in xrange(l.count(x)):
...         l.remove(x)
...
>>> removeall_inplace(0, b)
>>> b is a
True
>>> a.count(0)
0

En cuanto a la velocidad, los resultados en mi computadora portátil son (todos en una lista de 5000 entradas con 1000 entradas eliminadas)

  • Lista de comprensión - ~ 400us
  • Filtro - ~ 900us
  • .remove () loop - 50ms

Entonces, el ciclo .remove es aproximadamente 100 veces más lento ....... Hmmm, tal vez se necesita un enfoque diferente. Lo más rápido que he encontrado es usar la comprensión de la lista, pero luego reemplazar el contenido de la lista original.

>>> def removeall_replace(x, l):
....    t = [y for y in l if y != x]
....    del l[:]
....    l.extend(t)
  • removeall_replace () - 450us
Paul S
fuente
¿Por qué no simplemente reasignar la nueva lista debajo de la dirección anterior? def remove_all(x, l): return [y for y in l if y != x]entoncesl = remove_all(3,l)
Dannid
@ Dannid Ese es el segundo método en el primer cuadro de código. Crea una nueva lista, y no está modificando la lista anterior. Cualquier otra referencia a la lista permanecerá sin filtrar.
Paul S
Ah bien. Me quedé tan atrapado en la definición de un método que pasé por alto la simple tarea que ya había hecho.
Dannid
7

Puedes hacerlo

while 2 in x:   
    x.remove(2)
Amr
fuente
3
Esa es una mala solución, ya que la lista debe atravesarse 2 * n veces para n ocurrencias de 2.
cxxl
No se recomienda agregar o eliminar de la lista que está recorriendo. Mala práctica en mi humilde opinión.
Aman Mathur
5

A costa de la legibilidad, creo que esta versión es un poco más rápida ya que no obliga a reexaminar la lista, por lo que hacer exactamente el mismo trabajo que eliminar tiene que hacer de todos modos:

x = [1, 2, 3, 4, 2, 2, 3]
def remove_values_from_list(the_list, val):
    for i in range(the_list.count(val)):
        the_list.remove(val)

remove_values_from_list(x, 2)

print(x)
Martin Andersson
fuente
Para la lista que muestra en su código, este enfoque es aproximadamente un 36% más lento que el método de comprensión de la lista (que devuelve una copia), según mi medición.
djsmith
Bien, te diste cuenta de eso. Sin embargo, debido a que creo que podría haber perdido su juicio, estaba comparando mi versión con la primera propuesta hecha por el autor de la pregunta.
Martin Andersson
4

Enfoque Numpy y tiempos contra una lista / matriz con 1.000.000 elementos:

Tiempos:

In [10]: a.shape
Out[10]: (1000000,)

In [13]: len(lst)
Out[13]: 1000000

In [18]: %timeit a[a != 2]
100 loops, best of 3: 2.94 ms per loop

In [19]: %timeit [x for x in lst if x != 2]
10 loops, best of 3: 79.7 ms per loop

Conclusión: numpy es 27 veces más rápido (en mi cuaderno) en comparación con el enfoque de comprensión de la lista

PD: si quieres convertir tu lista de Python normal lsten una matriz numpy:

arr = np.array(lst)

Preparar:

import numpy as np
a = np.random.randint(0, 1000, 10**6)

In [10]: a.shape
Out[10]: (1000000,)

In [12]: lst = a.tolist()

In [13]: len(lst)
Out[13]: 1000000

Cheque:

In [14]: a[a != 2].shape
Out[14]: (998949,)

In [15]: len([x for x in lst if x != 2])
Out[15]: 998949
MaxU
fuente
4
a = [1, 2, 2, 3, 1]
to_remove = 1
a = [i for i in a if i != to_remove]
print(a)

Quizás no sea el más pitónico pero sigue siendo el más fácil para mí jaja

Ankit Sharma
fuente
3

Para eliminar todas las ocurrencias duplicadas y dejar una en la lista:

test = [1, 1, 2, 3]

newlist = list(set(test))

print newlist

[1, 2, 3]

Aquí está la función que he usado para el Proyecto Euler:

def removeOccurrences(e):
  return list(set(e))
Jared Burrows
fuente
2
Necesitaba hacer esto en un vector con valores de 250k, y funciona de maravilla.
rschwieb
1
¡La respuesta es sí! Y entiendo completamente si tener un vector tan largo suena completamente loco para un programador competente. Me acerco a los problemas allí como matemático, sin preocuparme por optimizar las soluciones, y eso puede llevar a soluciones más largas que la media. (Aunque no tengo paciencia para las soluciones de más de 5 minutos)
Rschwieb
66
Esto eliminará cualquier pedido de la lista.
asmeurer
44
@JaredBurrows quizás porque no responde la pregunta tal como está actualmente, pero es una pregunta bastante diferente.
drevicko
66
-1, esta no es una respuesta a la pregunta del OP. Es una solución para eliminar duplicados, que es un asunto completamente diferente.
Anoyz
2

Creo que esto es probablemente más rápido que cualquier otra forma si no le importa el orden de las listas, si se preocupa por el pedido final, almacene los índices del original y recurra a eso.

category_ids.sort()
ones_last_index = category_ids.count('1')
del category_ids[0:ones_last_index]
Michael Clemmons
fuente
2
Entiendo a dónde vas, pero este código no funcionará ya que también necesitas el índice de inicio y no solo 0.
Shedokan
2
for i in range(a.count(' ')):
    a.remove(' ')

Mucho más simple, creo.

marco
fuente
2
edite su respuesta para mejorar la claridad. Deje en claro qué hace exactamente su código recomendado, por qué funciona y por qué es su recomendación. También formatee correctamente su pregunta para que el código sea claramente discernible del resto de su respuesta.
Ortund
2

Dejar

>>> x = [1, 2, 3, 4, 2, 2, 3]

La solución más simple y eficiente como ya se publicó anteriormente es

>>> x[:] = [v for v in x if v != 2]
>>> x
[1, 3, 4, 3]

Otra posibilidad que debería usar menos memoria pero ser más lenta es

>>> for i in range(len(x) - 1, -1, -1):
        if x[i] == 2:
            x.pop(i)  # takes time ~ len(x) - i
>>> x
[1, 3, 4, 3]

Resultados de tiempo para listas de longitud 1000 y 100000 con entradas coincidentes del 10%: 0,16 frente a 0,25 ms y 23 frente a 123 ms.

Tiempo con longitud 1000

Tiempo con longitud 100000

jolvi
fuente
1

Eliminar todas las apariciones de un valor de una lista de Python

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list():
    for list in lists:
      if(list!=7):
         print(list)
remove_values_from_list()

Resultado: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

Alternativamente,

lists = [6.9,7,8.9,3,5,4.9,1,2.9,7,9,12.9,10.9,11,7]
def remove_values_from_list(remove):
    for list in lists:
      if(list!=remove):
        print(list)
remove_values_from_list(7)

Resultado: 6.9 8.9 3 5 4.9 1 2.9 9 12.9 10.9 11

rafiqul786
fuente
"¡Python 'anidado para cada bucle if' dentro de una función que funciona con una precisión del 100%!"
rafiqul786
No modifica la lista, solo imprime los elementos. También nombrar una lista como listas es confuso
kon psych
0

Si no tenía incorporado filtero no quería usar espacio adicional y necesita una solución lineal ...

def remove_all(A, v):
    k = 0
    n = len(A)
    for i in range(n):
        if A[i] !=  v:
            A[k] = A[i]
            k += 1

    A = A[:k]
cifrar
fuente
0
hello =  ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
#chech every item for a match
for item in range(len(hello)-1):
     if hello[item] == ' ': 
#if there is a match, rebuild the list with the list before the item + the list after the item
         hello = hello[:item] + hello [item + 1:]
print hello

['Hola Mundo']

SaNaMeDiO
fuente
Intente elaborar su respuesta con una explicación.
Parlad
0

Acabo de hacer esto para una lista. Solo soy un principiante. Un programador un poco más avanzado seguramente puede escribir una función como esta.

for i in range(len(spam)):
    spam.remove('cat')
    if 'cat' not in spam:
         print('All instances of ' + 'cat ' + 'have been removed')
         break
Asir Ajmal
fuente
0

También podemos eliminar todo en el lugar usando delo pop:

import random

def remove_values_from_list(lst, target):
    if type(lst) != list:
        return lst

    i = 0
    while i < len(lst):
        if lst[i] == target:
            lst.pop(i)  # length decreased by 1 already
        else:
            i += 1

    return lst

remove_values_from_list(None, 2)
remove_values_from_list([], 2)
remove_values_from_list([1, 2, 3, 4, 2, 2, 3], 2)
lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)], 2)
print(len(lst))

Ahora para la eficiencia:

In [21]: %timeit -n1 -r1 x = random.randrange(0,10)
1 loop, best of 1: 43.5 us per loop

In [22]: %timeit -n1 -r1 lst = [random.randrange(0, 10) for x in range(1000000)]
g1 loop, best of 1: 660 ms per loop

In [23]: %timeit -n1 -r1 lst = remove_values_from_list([random.randrange(0, 10) for x in range(1000000)]
    ...: , random.randrange(0,10))
1 loop, best of 1: 11.5 s per loop

In [27]: %timeit -n1 -r1 x = random.randrange(0,10); lst = [a for a in [random.randrange(0, 10) for x in
    ...:  range(1000000)] if x != a]
1 loop, best of 1: 710 ms per loop

Como vemos, la versión in situ remove_values_from_list()no requiere memoria adicional, pero lleva mucho más tiempo ejecutarla:

  • 11 segundos para valores de eliminación in situ
  • 710 mili segundos para la comprensión de listas, que asigna una nueva lista en la memoria
esmoquin
fuente
0

Nadie ha publicado una respuesta óptima para la complejidad del tiempo y el espacio, así que pensé en intentarlo. Aquí hay una solución que elimina todas las apariciones de un valor específico sin crear una nueva matriz y en un tiempo de complejidad eficiente. El inconveniente es que los elementos no mantienen el orden .

Complejidad de tiempo: O (n)
Complejidad de espacio adicional: O (1)

def main():
    test_case([1, 2, 3, 4, 2, 2, 3], 2)     # [1, 3, 3, 4]
    test_case([3, 3, 3], 3)                 # []
    test_case([1, 1, 1], 3)                 # [1, 1, 1]


def test_case(test_val, remove_val):
    remove_element_in_place(test_val, remove_val)
    print(test_val)


def remove_element_in_place(my_list, remove_value):
    length_my_list = len(my_list)
    swap_idx = length_my_list - 1

    for idx in range(length_my_list - 1, -1, -1):
        if my_list[idx] == remove_value:
            my_list[idx], my_list[swap_idx] = my_list[swap_idx], my_list[idx]
            swap_idx -= 1

    for pop_idx in range(length_my_list - swap_idx - 1):
        my_list.pop() # O(1) operation


if __name__ == '__main__':
    main()
Josh
fuente
-1

Sobre la velocidad!

import time
s_time = time.time()

print 'start'
a = range(100000000)
del a[:]
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 3.25

s_time = time.time()
print 'start'
a = range(100000000)
a = []
print 'finished in %0.2f' % (time.time() - s_time)
# start
# finished in 2.11
Sergey Kozlovskiy
fuente
-3
p=[2,3,4,4,4]
p.clear()
print(p)
[]

Solo con Python 3

usuario5064329
fuente
2
Hilarantemente, esto está dentro del alcance de la pregunta formulada y es correcto.
Erich
No veo cómo es correcto. Esto eliminará todos los elementos de la lista, no todas las apariciones de un valor .
Georgy
-3

Qué hay de malo en:

Motor=['1','2','2']
For i in Motor:
       If i  != '2':
       Print(i)
Print(motor)

Usando anaconda

desarrollador de código
fuente
2
Explique sus líneas de código para que otros usuarios puedan entender su funcionalidad. ¡Gracias!
Ignacio Ara
Este código no eliminará nada de la lista.
Georgy