Pandas groupby для вложенного json
Я часто использую pandas groupby для создания стоп-таблиц. Но тогда я часто хочу вывести полученные вложенные отношения в json. Есть ли способ извлечь вложенный json файл из таблицы, который он создает?
Скажем, у меня есть df:
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
Я могу сделать:
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
Красивая! Конечно, мне бы хотелось, чтобы я ввел json через команду по строкам grouped.to_json. Но эта функция недоступна. Любые обходные пути?
Итак, я действительно хочу что-то вроде:
{"2010": {"mayor": [
{"joe smith": 100},
{"jay gould": 12}
]
},
{"govnr": [
{"pati mara":500},
{"jess rapp": 80}
]
}
}
Дон
Ответы
Ответ 1
Я не думаю, что есть что-то встроенное в pandas для создания вложенного словаря данных. Ниже приведен код, который должен работать вообще для серии с MultiIndex, используя defaultdict
Код вставки повторяется через каждый уровень MultIndex, добавляя слои в словарь до тех пор, пока самый глубокий уровень не будет присвоен значению Series.
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(<function <lambda> at 0x7ff17c76d1b8>, {2010: defaultdict(<type 'dict'>, {'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
}
}
}
Ответ 2
Я посмотрел на решение выше и понял, что оно работает только для 3 уровней вложенности. Это решение подойдет для любого количества уровней.
import json
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])
Ответ 3
Я знаю, что это старый вопрос, но недавно я столкнулся с той же проблемой. Вот мое решение. Я заимствовал много вещей из примера chrisb (Спасибо!).
Это имеет то преимущество, что вы можете передать лямбду, чтобы получить окончательное значение из любого нужного вам числа, а также для каждой группы.
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
В этом вопросе вы бы назвали эту функцию следующим:
dict_from_enumerable(grouped.itertuples(), 'amount', 'year', 'office', 'candidate')
Первым аргументом может быть также массив данных, даже не требующий pandas.