2012-03-05 8 views
6

Tengo n listas de números. Quiero asegurarme de que cada lista contenga elementos únicos para esa lista en particular. Es decir. No hay duplicados "compartidos" en ninguno de los demás.
Esto es realmente fácil de hacer con dos listas, pero un poco más complicado con n listas.Manera más limpia de eliminar elementos comunes de la lista en varias listas en python

e.g. 
mylist = [ 
[1, 2, 3, 4], 
[2, 5, 6, 7], 
[4, 2, 8, 9] 
] 

se convierte en:

mylist = [ 
[1, 3], 
[5, 6, 7], 
[8, 9] 
] 
+4

¿Por qué 2 no está en ninguna de las tres listas, mientras que 4 sigue presente en la primera lista? –

+1

¿Le importa si se conserva la orden? – wim

+0

Use una bolsa ('default_dict') para compilar una lista" vista ". Reemplace cada lista de 'mylist' (la llamaré' sublist') con un generador que busque 'seen' coincidente: si se encuentra, no lo incluya en la' sublist' final. Si no lo encuentra, agréguelo a la bolsa. – Droogans

Respuesta

5
from collections import Counter 
from itertools import chain 

mylist = [ 
    [1,2,3,4], 
    [2,5,6,7,7], 
    [4,2,8,9] 
] 

counts = Counter(chain(*map(set,mylist))) 

[[i for i in sublist if counts[i]==1] for sublist in mylist] 
#[[1, 3], [5, 6, 7, 7], [8, 9]] 
+0

Esto es realmente bueno, pero preferiría no tener que importar el contador y la cadena, supongo que esto podría reducir ligeramente el tiempo de ejecución (?). – LittleBobbyTables

+0

!!! Estaba buscando una manera de hacer 'chain (* mylist)' en mi respuesta de una manera elegante. Muy agradable. Vaya, y ni siquiera necesito '.get()' como en mi respuesta porque, por supuesto, siempre estará definido. Estoy borrando mi respuesta porque la tuya es casi exactamente la misma, pero estrictamente mejor. – ninjagecko

+2

@MatthewRNYC: no debe temer usar colecciones básicas como lo sugiere esta respuesta. Además, no veo ninguna razón para que 'chain' y el constructor' Counter' no sean 'O (N)'. – ninjagecko

2

Esto lo hace en un tiempo lineal, 2 pases. Supongo que quieres conservar duplicados dentro de una lista; Si no, esto se puede simplificar un poco:

>>> import collections, itertools 
>>> counts = collections.defaultdict(int) 
>>> for i in itertools.chain.from_iterable(set(l) for l in mylist): 
...  counts[i] += 1 
... 
>>> for l in mylist: 
...  l[:] = (i for i in l if counts[i] == 1) 
... 
>>> mylist 
[[1, 3], [5, 6, 7], [8, 9]] 
+0

Esto deja en artículos vistos una vez, no estoy seguro si el OP quiere que ... – wim

+0

@wim, gracias, arreglado. – senderle

1

Ya que no se preocupan por fin, puede quitar fácilmente duplicados usando el conjunto de la resta y la conversión de volver a la lista. Aquí está en un monstruo de una sola línea:

>>> mylist = [ 
... [1, 2, 3, 4], 
... [2, 5, 6, 7], 
... [4, 2, 8, 9] 
... ] 
>>> mynewlist = [list(set(thislist) - set(element for sublist in mylist for element in sublist if sublist is not thislist)) for thislist in mylist] 
>>> mynewlist 
[[1, 3], [5, 6, 7], [8, 9]] 

Nota: Esto no es muy eficiente porque los duplicados se vuelven a calcular para cada fila. Si esto es un problema o no depende del tamaño de su información.

+1

¡Esto es una bestia!:) – LittleBobbyTables

+0

Aunque parece una operación costosa. Si tiene listas 'n' con elementos' m' cada una, tiene algo como 'O (n * n-1 * m)' (eso es solo para iterar sobre cada elemento de cada sublista). ¿O estoy equivocado? –

+0

Lamentablemente tengo que -1: esto recalcula todos los duplicados para cada lista, lo que resulta en aproximadamente 'O (N^(3/2))' trabajo suponiendo que el número de sublistas es como 'sqrt (N)'. Tampoco conserva el orden de una lista (aunque si las listas estuvieran ordenadas, podría volver a ordenarlas, a un costo multiplicativo 'O (log (sublistN))' extra). Yo personalmente iría con la solución 'Counter' que creo que es' O (N) '. – ninjagecko

0

set() es el enfoque correcto. aunque no tiene que usar una lista de comprensión.

Sin importaciones adicionales:

mylist = [ 
[1, 2, 3, 4], 
[2, 5, 6, 7], 
[4, 2, 8, 9] 
] 
>>> result_list = [] 
>>> for test_list in mylist: 
...  result_set = set(test_list) 
...  for compare_list in mylist: 
...   if test_list != compare_list: 
...    result_set = result_set - set(compare_list) 
...  result_list.append(result_set) 
... 
>>> result_list 
[set([1, 3]), set([5, 6, 7]), set([8, 9])] 
0

Esta es mi solución, utilizando Counter para construir un conjunto de todos los números comunes, y entonces, sólo se hace una diferencia de conjuntos:

from collections import Counter 

def disjoin(lsts): 
    c = Counter(num for lst in lsts for num in lst) 
    common = set(x for x,v in c.items() if v > 1) 
    result = [] 
    for lst in lsts: 
     result.append(set(lst) - common) 
    return result 

Ejemplo:

>>> remove_common(mylist) 
[set([1, 3]), set([5, 6, 7]), set([8, 9])] 
Cuestiones relacionadas