Eliminar todos los elementos que aparecen en una lista de otra

Digamos que tengo dos listas, l1 y l2 . Quiero realizar l1 - l2 , que devuelve todos los elementos de l1 no en l2 .

Puedo pensar en un enfoque de bucle ingenuo para hacer esto, pero eso va a ser realmente ineficiente. ¿Qué es una forma pythonica y eficiente de hacer esto?

Como ejemplo, si tengo l1 = [1,2,6,8] and l2 = [2,3,5,8] , l1 - l2 debería devolver [1,6]

Python tiene una función de lenguaje llamada Comprensiones de lista que se adapta perfectamente para hacer este tipo de cosas extremadamente fácil. La siguiente statement hace exactamente lo que quiere y almacena el resultado en l3 :

 l3 = [x for x in l1 if x not in l2] 

l3 contendrá [1, 6] .

¡Espero que esto ayude!

Una forma es usar conjuntos:

 >>> set([1,2,6,8]) - set([2,3,5,8]) set([1, 6]) 

Al ampliar la respuesta de Donut y las otras respuestas aquí, puede obtener resultados aún mejores si usa un generador de comprensión en lugar de una lista de comprensión, y si usa una estructura de datos set (ya que el operador in está en O (n) en una lista pero O ( 1) en un set).

Así que aquí hay una función que funcionaría para usted:

 def filter_list(full_list, excludes): s = set(excludes) return (x for x in full_list if x not in s) 

El resultado será un iterable que buscará perezosamente la lista filtrada. Si necesita un objeto de lista real (por ejemplo, si necesita hacer un len() en el resultado), puede crear fácilmente una lista así:

 filtered_list = list(filter_list(full_list, excludes)) 

Utilice el tipo de conjunto de Python. Eso sería lo más pythonico. 🙂

Además, como es nativo, también debería ser el método más optimizado.

Ver:

http://docs.python.org/library/stdtypes.html#set

http://docs.python.org/library/sets.htm (para python más antiguo)

 # Using Python 2.7 set literal format. # Otherwise, use: l1 = set([1,2,6,8]) # l1 = {1,2,6,8} l2 = {2,3,5,8} l3 = l1 - l2 

Como alternativa, también puede usar el filter con la expresión lambda para obtener el resultado deseado. Por ejemplo:

 >>> l1 = [1,2,6,8] >>> l2 = set([2,3,5,8]) # v `filter` returns the a iterator object. Here I'm type-casting # v it to `list` in order to display the resultant value >>> list(filter(lambda x: x not in l2, l1)) [1, 6] 

Comparación de rendimiento

Aquí estoy comparando el rendimiento de todas las respuestas mencionadas aquí. Como se esperaba, la operación basada en el set de Arkku es la más rápida.

  • La diferencia de conjuntos de Arkku – Primero (0.124 usec por bucle)

     mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2" 10000000 loops, best of 3: 0.124 usec per loop 
  • Lista de comprensión de Daniel Pryden con búsqueda de set – Segundo (0.302 usec por bucle)

     mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]" 1000000 loops, best of 3: 0.302 usec per loop 
  • La comprensión de la lista de anillos en una lista simple – Tercera (0.552 usec por bucle)

     mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]" 1000000 loops, best of 3: 0.552 usec per loop 
  • filter usa Moinuddin Quadri – Cuarto (0,972 usec por bucle)

     mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)" 1000000 loops, best of 3: 0.972 usec per loop 
  • Akshay Hazari utiliza una combinación de reduce + filter – Quinto (3.97 usec por bucle)

     mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)" 100000 loops, best of 3: 3.97 usec per loop 

PS: set no mantiene el orden y elimina los elementos duplicados de la lista. Por lo tanto, no utilice la diferencia establecida si necesita alguno de estos.

Solución alternativa:

 reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])