pandas groupby nested json

A menudo utilizo pandas groupby para generar tablas astackdas. Pero luego a menudo quiero dar salida a las relaciones anidadas resultantes a json. ¿Hay alguna forma de extraer un archivo json nested de la tabla astackda que produce?

Digamos que tengo un df como:

year office candidate amount 2010 mayor joe smith 100.00 2010 mayor jay gould 12.00 2010 govnr pati mara 500.00 2010 govnr jess rapp 50.00 2010 govnr jess rapp 30.00 

Puedo hacer:

 grouped = df.groupby('year', 'office', 'candidate').sum() print grouped amount year office candidate 2010 mayor joe smith 100 jay gould 12 govnr pati mara 500 jess rapp 80 

¡Hermoso! Por supuesto, lo que realmente me gustaría hacer es anidar json a través de un comando en las líneas de grouped.to_json. Pero esa característica no está disponible. ¿Alguna solución?

Entonces, lo que realmente quiero es algo como:

 {"2010": {"mayor": [ {"joe smith": 100}, {"jay gould": 12} ] }, {"govnr": [ {"pati mara":500}, {"jess rapp": 80} ] } } 

Don

No creo que haya algo integrado en los pandas para crear un diccionario nested de datos. A continuación se muestra un código que debería funcionar en general para una serie con un MultiIndex, usando un valor defaultdict

El código de anidamiento itera a través de cada nivel del MultIndex, agregando capas al diccionario hasta que la capa más profunda se asigna al valor Serie.

 In [99]: from collections import defaultdict In [100]: results = defaultdict(lambda: defaultdict(dict)) In [101]: for index, value in grouped.itertuples(): ...: for i, key in enumerate(index): ...: if i == 0: ...: nested = results[key] ...: elif i == len(index) - 1: ...: nested[key] = value ...: else: ...: nested = nested[key] In [102]: results Out[102]: defaultdict( at 0x7ff17c76d1b8>, {2010: defaultdict(, {'govnr': {'pati mara': 500.0, 'jess rapp': 80.0}, 'mayor': {'joe smith': 100.0, 'jay gould': 12.0}})}) In [106]: print json.dumps(results, indent=4) { "2010": { "govnr": { "pati mara": 500.0, "jess rapp": 80.0 }, "mayor": { "joe smith": 100.0, "jay gould": 12.0 } } } 

Eché un vistazo a la solución anterior y descubrí que solo funciona para 3 niveles de anidamiento. Esta solución funcionará para cualquier número de niveles.

 levels = len(grouped.index.levels) dicts = [{} for i in range(levels)] last_index = None for index,value in grouped.itertuples(): if not last_index: last_index = index for (ii,(i,j)) in enumerate(zip(index, last_index)): if not i == j: ii = levels - ii -1 dicts[:ii] = [{} for _ in dicts[:ii]] break for i, key in enumerate(reversed(index)): dicts[i][key] = value value = dicts[i] last_index = index result = json.dumps(dicts[-1]) 

Soy consciente de que esta es una pregunta antigua, pero encontré el mismo problema recientemente. Aquí está mi solución. Tomé prestadas muchas cosas del ejemplo de chrisb (¡Gracias!).

Esto tiene la ventaja de que puede pasar un lambda para obtener el valor final de la enumeración que desee, así como para cada grupo.

 from collections import defaultdict def dict_from_enumerable(enumerable, final_value, *groups): d = defaultdict(lambda: defaultdict(dict)) group_count = len(groups) for item in enumerable: nested = d item_result = final_value(item) if callable(final_value) else item.get(final_value) for i, group in enumerate(groups, start=1): group_val = str(group(item) if callable(group) else item.get(group)) if i == group_count: nested[group_val] = item_result else: nested = nested[group_val] return d 

En la pregunta, llamarías a esta función como:

 dict_from_enumerable(grouped.itertuples(), 'amount', 'year', 'office', 'candidate') 

El primer argumento también puede ser un conjunto de datos, que ni siquiera requieren pandas.