Suponga que tengo un conjunto de pares de datos donde el índice 0 es el valor y el índice 1 es el tipo:
input = [
('11013331', 'KAT'),
('9085267', 'NOT'),
('5238761', 'ETH'),
('5349618', 'ETH'),
('11788544', 'NOT'),
('962142', 'ETH'),
('7795297', 'ETH'),
('7341464', 'ETH'),
('9843236', 'KAT'),
('5594916', 'ETH'),
('1550003', 'ETH')
]
Quiero agruparlos por su tipo (por la primera cadena indexada) como tal:
result = [
{
type:'KAT',
items: ['11013331', '9843236']
},
{
type:'NOT',
items: ['9085267', '11788544']
},
{
type:'ETH',
items: ['5238761', '962142', '7795297', '7341464', '5594916', '1550003']
}
]
¿Cómo puedo lograr esto de manera eficiente?
[('11013331', 'red', 'KAT'), ('9085267', 'blue' 'KAT')]
donde el último elemento de la tupla es clave y los dos primeros como valor? El resultado debería ser así: resultado = [{tipo: 'KAT', elementos: [('11013331', rojo), ('9085267', azul)]}]from operator import itemgetter
d= {}; for k,v in input: d.setdefault(k, []).append(v)
El
itertools
módulo integrado de Python en realidad tiene unagroupby
función, pero para eso los elementos que se van a agrupar primero deben ordenarse de manera que los elementos que se agrupen sean contiguos en la lista:Ahora la entrada se ve así:
groupby
devuelve una secuencia de 2 tuplas, de la forma(key, values_iterator)
. Lo que queremos es convertir esto en una lista de dictados donde el "tipo" es la clave, y "elementos" es una lista de los elementos número 0 de las tuplas devueltas por el valor_iterador. Me gusta esto:Ahora
result
contiene su dict deseado, como se indica en su pregunta.Sin embargo, puede considerar hacer un solo resumen de esto, tecleado por tipo, y cada valor que contenga la lista de valores. En su forma actual, para encontrar los valores de un tipo en particular, deberá iterar sobre la lista para encontrar el dict que contiene la clave correspondiente 'tipo' y luego obtener el elemento 'elementos' de él. Si usa un solo dict en lugar de una lista de dictados de 1 elemento, puede encontrar los elementos para un tipo particular con una sola búsqueda con clave en el dict maestro. Usando
groupby
, esto se vería así:result
ahora contiene este dict (es similar alres
defaultdictdict intermedio en la respuesta de @ KennyTM)(Si desea reducir esto a una sola línea, puede:
o usando la nueva forma de comprensión dict:
fuente
También me gustó la agrupación simple de pandas . es potente, simple y más adecuado para grandes conjuntos de datos
result = pandas.DataFrame(input).groupby(1).groups
fuente
Esta respuesta es similar a la respuesta de @ PaulMcG pero no requiere ordenar la entrada.
Para aquellos en programación funcional,
groupBy
se puede escribir en una línea (¡sin incluir las importaciones!), Y a diferencia deitertools.groupby
esto, no requiere que se ordene la entrada:(La razón de
... or grp
lalambda
es que para que estereduce()
funcione, laslambda
necesidades para devolver su primer argumento, porquelist.append()
siempre devuelveNone
elor
siempre volverágrp
. Es decir, se trata de un truco para conseguir alrededor de restricción del pitón que una lambda sólo puede evaluar una sola expresión.)Esto devuelve un dict cuyas claves se encuentran al evaluar la función dada y cuyos valores son una lista de los elementos originales en el orden original. Para el ejemplo del OP, llamar esto como
groupBy(lambda pair: pair[1], input)
devolverá este dict:Y según la respuesta de @ PaulMcG, se puede encontrar el formato solicitado por el OP envolviéndolo en una lista de comprensión. Entonces esto lo hará:
fuente
La siguiente función agrupará rápidamente ( no se requiere clasificación ) tuplas de cualquier longitud por una clave que tenga algún índice:
En el caso de su pregunta, el índice de clave por el que desea agrupar es 1, por lo tanto:
da
que no es exactamente el resultado que solicitó, pero que bien podría satisfacer sus necesidades.
fuente
fuente