python list intersection

python - ¿Encontrar intersección de dos listas anidadas?



intersection (18)

Versión de comprensión de lista pura

>>> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] >>> c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] >>> c1set = frozenset(c1)

Variante aplanada:

>>> [n for lst in c2 for n in lst if n in c1set] [13, 32, 7, 13, 28, 1, 6]

Variante anidada:

>>> [[n for n in lst if n in c1set] for lst in c2] [[13, 32], [7, 13, 28], [1, 6]]

Sé cómo obtener una intersección de dos listas planas:

b1 = [1,2,3,4,5,9,11,15] b2 = [4,5,6,7,8] b3 = [val for val in b1 if val in b2]

o

def intersect(a, b): return list(set(a) & set(b)) print intersect(b1, b2)

Pero cuando tengo que encontrar una intersección para listas anidadas, comienzan mis problemas:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

Al final me gustaría recibir:

c3 = [[13,32],[7,13,28],[1,6]]

¿Pueden ustedes echarme una mano con esto?

Relacionado


¿Considera que [1,2] cruza con [1, [2]] ? Es decir, ¿son solo los números que te interesan o la estructura de la lista también?

Si solo son los números, investigue cómo "aplanar" las listas, luego use el método set() .


Como se definió intersect , una lista básica de comprensión es suficiente:

>>> c3 = [intersect(c1, i) for i in c2] >>> c3 [[32, 13], [28, 13, 7], [1, 6]]

Mejora gracias a la observación de S. Lott y la observación asociada de TM.

>>> c3 = [list(set(c1).intersection(i)) for i in c2] >>> c3 [[32, 13], [28, 13, 7], [1, 6]]


Dado:

> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] > c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]]

Me parece que el siguiente código funciona bien y tal vez sea más conciso si se usa la operación de configuración:

> c3 = [list(set(f)&set(c1)) for f in c2]

Llegó:

> [[32, 13], [28, 13, 7], [1, 6]]

Si el pedido es necesario:

> c3 = [sorted(list(set(f)&set(c1))) for f in c2]

tenemos:

> [[13, 32], [7, 13, 28], [1, 6]]

Por cierto, para un estilo más python, este también está bien:

> c3 = [ [i for i in set(f) if i in c1] for f in c2]


Debe aplanar usando este código (tomado de http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks ), el código no está probado, pero estoy bastante seguro de que funciona:

def flatten(x): """flatten(sequence) -> list Returns a single, flat list which contains all elements retrieved from the sequence and all recursively contained sub-sequences (iterables). Examples: >>> [1, 2, [3,4], (5,6)] [1, 2, [3, 4], (5, 6)] >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, MyVector(8,9,10)]) [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]""" result = [] for el in x: #if isinstance(el, (list, tuple)): if hasattr(el, "__iter__") and not isinstance(el, basestring): result.extend(flatten(el)) else: result.append(el) return result

Una vez que haya aplanado la lista, realice la intersección de la forma habitual:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] def intersect(a, b): return list(set(a) & set(b)) print intersect(flatten(c1), flatten(c2))


El enfoque funcional:

input_list = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]] result = reduce(set.intersection, map(set, input_list))

y se puede aplicar al caso más general de las listas 1+.


El operador & toma la intersección de dos conjuntos.

{1, 2, 3} y {2, 3, 4} Salida [1]: {2, 3}


No es necesario definir la intersección. Ya es una parte de primera clase del set.

>>> b1 = [1,2,3,4,5,9,11,15] >>> b2 = [4,5,6,7,8] >>> set(b1).intersection(b2) set([4, 5])


No sé si llego tarde a tu pregunta. Después de leer su pregunta, encontré una función intersect () que puede funcionar tanto en la lista como en la lista anidada. Usé la recursión para definir esta función, es muy intuitivo. Espero que sea lo que buscas:

def intersect(a, b): result=[] for i in b: if isinstance(i,list): result.append(intersect(a,i)) else: if i in a: result.append(i) return result

Ejemplo:

>>> c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] >>> c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] >>> print intersect(c1,c2) [[13, 32], [7, 13, 28], [1, 6]] >>> b1 = [1,2,3,4,5,9,11,15] >>> b2 = [4,5,6,7,8] >>> print intersect(b1,b2) [4, 5]


Para definir una intersección que tenga en cuenta correctamente la cardinalidad de los elementos, use Counter :

from collections import Counter >>> c1 = [1, 2, 2, 3, 4, 4, 4] >>> c2 = [1, 2, 4, 4, 4, 4, 5] >>> list((Counter(c1) & Counter(c2)).elements()) [1, 2, 4, 4, 4]


Para las personas que solo buscan encontrar la intersección de dos listas, el autor de la pregunta proporcionó dos métodos:

b1 = [1,2,3,4,5,9,11,15] b2 = [4,5,6,7,8] b3 = [val for val in b1 if val in b2]

y

def intersect(a, b): return list(set(a) & set(b)) print intersect(b1, b2)

Pero hay un método híbrido que es más eficiente, porque solo tienes que hacer una conversión entre lista / conjunto, en lugar de tres:

b1 = [1,2,3,4,5] b2 = [3,4,5,6] s2 = set(b2) b3 = [val for val in b1 if val in s2]

Esto se ejecutará en O (n), mientras que su método original que incluye la comprensión de listas se ejecutará en O (n ^ 2)


Podemos usar métodos de ajuste para esto:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] result = [] for li in c2: res = set(li) & set(c1) result.append(list(res)) print result


También estaba buscando una manera de hacerlo, y finalmente terminó así:

def compareLists(a,b): removed = [x for x in a if x not in b] added = [x for x in b if x not in a] overlap = [x for x in a if x in b] return [removed,added,overlap]


Una forma pitónica de tomar la intersección de 2 listas es:

[x for x in list1 if x in list2]


Si tu quieres:

c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] c3 = [[13, 32], [7, 13, 28], [1,6]]

Entonces aquí está tu solución para Python 2:

c3 = [filter(lambda x: x in c1, sublist) for sublist in c2]

En Python 3, el filter devuelve un iterable en lugar de una list , por lo que debe ajustar filter llamadas de filter con list() :

c3 = [list(filter(lambda x: x in c1, sublist)) for sublist in c2]

Explicación:

La parte del filtro toma el elemento de cada sub-lista y verifica si está en la lista de fuentes c1. La lista de comprensión se ejecuta para cada sub-lista en c2.


# Problem: Given c1 and c2: c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] # how do you get c3 to be [[13, 32], [7, 13, 28], [1, 6]] ?

Aquí hay una forma de configurar c3 que no involucra conjuntos:

c3 = [] for sublist in c2: c3.append([val for val in c1 if val in sublist])

Pero si prefiere usar solo una línea, puede hacer esto:

c3 = [[val for val in c1 if val in sublist] for sublist in c2]

Es una lista de comprensión dentro de una lista de comprensión, que es un poco inusual, pero creo que no debería tener demasiados problemas para seguirla.


c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] c3 = [list(set(c2[i]).intersection(set(c1))) for i in xrange(len(c2))] c3 ->[[32, 13], [28, 13, 7], [1, 6]]


c1 = [1, 6, 7, 10, 13, 28, 32, 41, 58, 63] c2 = [[13, 17, 18, 21, 32], [7, 11, 13, 14, 28], [1, 5, 6, 8, 15, 16]] c3 = [list(set(i) & set(c1)) for i in c2] c3 [[32, 13], [28, 13, 7], [1, 6]]

Para mi esta es una forma muy elegante y rápida de hacerlo :)