2011-06-04 11 views
11
>>from itertools import groupby 
>>keyfunc = lambda x : x > 500 
>>obj = dict(groupby(range(1000), keyfunc)) 
>>list(obj[True]) 
[999] 
>>list(obj[False]) 
[] 

rango (1000) es, obviamente, ordenados de forma predeterminada para la condición (x> 500).
Estaba esperando que los números del 0 al 999 se agrupen en un dict por la condición (x> 500). Pero el diccionario resultante tenía solo 999.
¿dónde están los otros números ?. ¿Alguien puede explicar lo que está sucediendo aquí?python groupby comportamiento?

Respuesta

17

Desde el docs:

el grupo volvió en sí es un iterador que comparte la iterables subyacente con groupby(). Debido a que la fuente se comparte, cuando se avanza el objeto groupby(), el grupo anterior ya no es visible. Por lo tanto, si se necesita que los datos más adelante, debe ser almacenado como una lista [.]

Y se va a almacenar en iteradores obj y materializándolos más tarde.

In [21]: dict((k, list(g)) for k, g in groupby(range(10), lambda x : x > 5)) 
Out[21]: {False: [0, 1, 2, 3, 4, 5], True: [6, 7, 8, 9]} 
3

Lo que se echa en falta es que las iteraciones GroupBy-función más de su dada range(1000), volviendo así los valores de 1000. Solo está guardando el último, en su caso 999. Lo que tienes que hacer es, es iterar sobre los valores de retorno y guardarlos en su diccionario:

dictionary = {} 
keyfunc = lambda x : x > 500 
for k, g in groupby(range(1000), keyfunc): 
    dictionary[k] = list(g) 

Así que el que se obtendría el resultado esperado:

{False: [0, 1, 2, ...], True: [501, 502, 503, ...]} 

Para obtener más información, consulte la Python documenta aproximadamente itertools groupby.

7

El iterador groupby devuelve tuplas del resultado de la función de agrupamiento y un nuevo iterador que está vinculado al mismo iterador "externo" en el que está trabajando el operador groupby. Cuando aplique dict() al iterador devuelto por groupby sin consumir este iterador "interno", groupby tendrá que avanzar el iterador "externo" por usted. Debe darse cuenta de que la función groupby no actúa en una secuencia, sino que convierte dicha secuencia en un iterador por usted.

Quizás esto se explica mejor con algunas metáforas y movimientos manuales. Por favor sigue mientras formamos una línea de cubo.

Imagina iteradores como una persona que extrae agua en baldes de un pozo. Él tiene un número ilimitado de cubos para usar, pero el pozo puede ser finito. Cada vez que le pidas a esta persona un cubo de agua, él saca un cubo nuevo del pozo de agua y se lo pasa.

En el caso groupby, inserta a otra persona en su cadena de cubos en ciernes. Esta persona no pasa balones de inmediato. Le pasa el resultado de las instrucciones que le dio más otra persona cada vez que solicita un depósito, quien luego le pasará los depósitos a través de la persona groupby a quien pregunte, siempre que coincidan con el mismo resultado de las instrucciones. El pasador de cubos groupby dejará de pasar estos cubos si el resultado de las instrucciones cambia.Por lo tanto, well proporciona intervalos a groupby, que pasa esto a una persona por grupo, group A, group B, y así sucesivamente.

En su ejemplo, el agua está numerada, pero solo puede haber 1000 cubos extraídos del pozo. Esto es lo que sucede cuando se pasan luego a la persona a la llamada groupbydict():

  1. Su llamada dict() pide groupby de un cubo. Ahora, groupby pide un balde de la persona en el pozo, recuerda el resultado de las instrucciones dadas, aferrándose al balde. Al dict() pasará el resultado de las instrucciones (False) más una nueva persona, group A. El resultado se almacena como la clave, y la persona group A, que quiere extraer cubos, se almacena como valor. Esta persona es , no, pero aún pide cubos, porque nadie lo está pidiendo.

  2. Su llamada dict() pregunta groupby por otro cubo. groupby tiene estas instrucciones, y busca el próximo cubo donde cambia el resultado. Todavía estaba aferrado al primer cubo, nadie lo pidió, por lo que arroja este cubo. En cambio, solicita el siguiente cubo del pozo y usa sus instrucciones. El resultado es el mismo que antes, ¡así que arroja este nuevo cubo también! Más agua pasa por el piso, y así pasan los siguientes 499 cubos. Solo cuando se pasa el depósito con el número 501 cambia el resultado, por lo que ahora groupby encuentra a otra persona para dar instrucciones (persona group B), junto con el nuevo resultado, True, pasando estos dos a dict().

  3. Su llamada dict() almacena True como clave, y la persona group B como el valor. group B no hace nada, nadie le pide agua.

  4. Su dict() pide otro cucharón. groupby derrama más agua, hasta que contiene un cubo con el número 999, y la persona en el pozo se encoge de hombros y dice que ahora el pozo está vacío. groupby dice dict() el pozo está vacío, no hay más cubos, ¿podría dejar de preguntar? Todavía contiene el cubo con el número 999, porque nunca tiene que dejar espacio para el siguiente cubo del pozo.

  5. Ahora viene, pidiendo dict() para la cosa asociada con la clave True, que es la persona group B. Pase group B a list(), que por lo tanto pedirá group B para todos los cubos group B pueden obtener. group B se remonta a groupby, que tiene un solo cubo, el cubo con el número 999, y el resultado de las instrucciones para este cubo coinciden con lo que group B está buscando. Así que este cubo group B da a list(), luego se encoge de hombros porque no hay más cubos, porque groupby se lo dijo.

  6. Luego, pregunte dict() por la persona asociada con la clave False, que es la persona group A. Por ahora, groupby no tiene nada que ofrecer, el pozo está seco y está parado en un charco de 999 cubos de agua con números flotando. Su segundo list() no obtiene nada.

¿La moraleja de esta historia? ¡Solicite inmediatamente todos los baldes de agua cuando hable con groupby, porque los derramará todos si no lo hace! Los iteradores son como las escobas en la fantasía, moviendo diligentemente el agua sin comprender, y es mejor que se quede sin agua si no sabe cómo controlarla.

Aquí es código que haría lo que espera (con un poco menos de agua para evitar inundaciones):

>>> from itertools import groupby 
>>> keyfunc = lambda x : x > 5 
>>> obj = dict((k, list(v)) for k, v in groupby(range(10), keyfunc)) 
>>> obj(True) 
[0, 1, 2, 3, 4, 5] 
>>> obj(False) 
[6, 7, 8, 9] 
+0

Quizás quiso decir "El aprendiz de brujo"? ¿O tal vez Fantasia tiene escobas para llevar agua también? –

+0

@ReblochonMasque The Sorcerer's Apprentice [es una parte de Fantasia] (https://en.wikipedia.org/wiki/Fantasia_%281940_film%29#Program). –

+0

Ok, gracias @MartijnPieters, no lo sabía. –