#python #pandas #dictionary #json-normalize
#python #панды #словарь #json-нормализовать #pandas
Вопрос:
Я использую pd.json_normalize
, чтобы разбить "sections"
поле в этих данных на строки. Это работает нормально, за исключением строк, где "sections"
это пустой список.
Этот идентификатор полностью игнорируется и отсутствует в конечном сплющенном фрейме данных. Мне нужно убедиться, что у меня есть хотя бы одна строка для каждого уникального идентификатора в данных (некоторые идентификаторы могут содержать много строк до одной строки для каждого уникального идентификатора, для каждого уникального section_id
, question_id
и answer_id
по мере того, как я добавляю больше полей в данные):
{'_id': '5f48f708fe22ca4d15fb3b55',
'created_at': '2020-08-28T12:22:32Z',
'sections': []}]
Примерные данные:
sample = [{'_id': '5f48bee4c54cf6b5e8048274',
'created_at': '2020-08-28T08:23:00Z',
'sections': [{'comment': '',
'type_fail': None,
'answers': [{'comment': 'stuff',
'feedback': [],
'value': 10.0,
'answer_type': 'default',
'question_id': '5e59599c68369c24069630fd',
'answer_id': '5e595a7c3fbb70448b6ff935'},
{'comment': 'stuff',
'feedback': [],
'value': 10.0,
'answer_type': 'default',
'question_id': '5e598939cedcaf5b865ef99a',
'answer_id': '5e598939cedcaf5b865ef998'}],
'score': 20.0,
'passed': True,
'_id': '5e59599c68369c24069630fe',
'custom_fields': []},
{'comment': '',
'type_fail': None,
'answers': [{'comment': '',
'feedback': [],
'value': None,
'answer_type': 'not_applicable',
'question_id': '5e59894f68369c2398eb68a8',
'answer_id': '5eaad4e5b513aed9a3c996a5'},
{'comment': '',
'feedback': [],
'value': None,
'answer_type': 'not_applicable',
'question_id': '5e598967cedcaf5b865efe3e',
'answer_id': '5eaad4ece3f1e0794372f8b2'},
{'comment': "stuff",
'feedback': [],
'value': 0.0,
'answer_type': 'default',
'question_id': '5e598976cedcaf5b865effd1',
'answer_id': '5e598976cedcaf5b865effd3'}],
'score': 0.0,
'passed': True,
'_id': '5e59894f68369c2398eb68a9',
'custom_fields': []}]},
{'_id': '5f48f708fe22ca4d15fb3b55',
'created_at': '2020-08-28T12:22:32Z',
'sections': []}]
Тесты:
df = pd.json_normalize(sample)
df2 = pd.json_normalize(df.to_dict(orient="records"), meta=["_id", "created_at"], record_path="sections", record_prefix="section_")
На данный момент мне не хватает строки для идентификатора «5f48f708fe22ca4d15fb3b55», которая мне все еще нужна.
df3 = pd.json_normalize(df2.to_dict(orient="records"), meta=["_id", "created_at", "section__id", "section_score", "section_passed", "section_type_fail", "section_comment"], record_path="section_answers", record_prefix="")
Могу ли я как-то изменить это, чтобы убедиться, что я получаю минимум одну строку на идентификатор? Я имею дело с миллионами записей и не хочу позже осознавать, что в моих окончательных данных отсутствовали некоторые идентификаторы. Единственное решение, которое я могу придумать, — это нормализовать каждый фрейм данных, а затем снова присоединить его к исходному фрейму данных.
Ответ №1:
- Лучший способ решить проблему — исправить
dict
- Если
sections
это пустое полеlist
, заполните его[{'answers': [{}]}]
for i, d in enumerate(sample):
if not d['sections']:
sample[i]['sections'] = [{'answers': [{}]}]
df = pd.json_normalize(sample)
df2 = pd.json_normalize(df.to_dict(orient="records"), meta=["_id", "created_at"], record_path="sections", record_prefix="section_")
# display(df2)
section_comment section_type_fail section_answers section_score section_passed section__id section_custom_fields _id created_at
0 NaN [{'comment': 'stuff', 'feedback': [], 'value': 10.0, 'answer_type': 'default', 'question_id': '5e59599c68369c24069630fd', 'answer_id': '5e595a7c3fbb70448b6ff935'}, {'comment': 'stuff', 'feedback': [], 'value': 10.0, 'answer_type': 'default', 'question_id': '5e598939cedcaf5b865ef99a', 'answer_id': '5e598939cedcaf5b865ef998'}] 20.0 True 5e59599c68369c24069630fe [] 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z
1 NaN [{'comment': '', 'feedback': [], 'value': None, 'answer_type': 'not_applicable', 'question_id': '5e59894f68369c2398eb68a8', 'answer_id': '5eaad4e5b513aed9a3c996a5'}, {'comment': '', 'feedback': [], 'value': None, 'answer_type': 'not_applicable', 'question_id': '5e598967cedcaf5b865efe3e', 'answer_id': '5eaad4ece3f1e0794372f8b2'}, {'comment': 'stuff', 'feedback': [], 'value': 0.0, 'answer_type': 'default', 'question_id': '5e598976cedcaf5b865effd1', 'answer_id': '5e598976cedcaf5b865effd3'}] 0.0 True 5e59894f68369c2398eb68a9 [] 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z
2 NaN NaN [{}] NaN NaN NaN NaN 5f48f708fe22ca4d15fb3b55 2020-08-28T12:22:32Z
df3 = pd.json_normalize(df2.to_dict(orient="records"), meta=["_id", "created_at", "section__id", "section_score", "section_passed", "section_type_fail", "section_comment"], record_path="section_answers", record_prefix="")
# display(df3)
comment feedback value answer_type question_id answer_id _id created_at section__id section_score section_passed section_type_fail section_comment
0 stuff [] 10.0 default 5e59599c68369c24069630fd 5e595a7c3fbb70448b6ff935 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z 5e59599c68369c24069630fe 20 True NaN
1 stuff [] 10.0 default 5e598939cedcaf5b865ef99a 5e598939cedcaf5b865ef998 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z 5e59599c68369c24069630fe 20 True NaN
2 [] NaN not_applicable 5e59894f68369c2398eb68a8 5eaad4e5b513aed9a3c996a5 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z 5e59894f68369c2398eb68a9 0 True NaN
3 [] NaN not_applicable 5e598967cedcaf5b865efe3e 5eaad4ece3f1e0794372f8b2 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z 5e59894f68369c2398eb68a9 0 True NaN
4 stuff [] 0.0 default 5e598976cedcaf5b865effd1 5e598976cedcaf5b865effd3 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z 5e59894f68369c2398eb68a9 0 True NaN
5 NaN NaN NaN NaN NaN NaN 5f48f708fe22ca4d15fb3b55 2020-08-28T12:22:32Z NaN NaN NaN NaN NaN
Комментарии:
1. Заполнение пустых значений с помощью list empty dict сработало для меня и позволило мне использовать json_normalize чисто (по сравнению с использованием другой библиотеки). Единственное, что мне приходилось учитывать, это когда вы нормализуете поле, а другие поля имеют тип списка — мне пришлось преобразовать эти столбцы списка в строки. Теперь у меня есть рабочее решение, спасибо.
2. @ldacey Я рад, что это сработало для вас. Работа с неправильно структурированными файлами JSON может быть болезненной.
3. @TrentonMcKinney Елки-палки! Я только что столкнулся с этой проблемой. Спасибо за ваше решение здесь!
4. @ScottBoston Привет, Скотт! Рад, что это сработало для вас. Объекты JSON могут быть такой проблемой. Упс! Сказал то же самое 1,5 года назад. Я поддерживаю этот комментарий.
Ответ №2:
Это известная проблема с json_normalize
. Я не нашел способа сделать это с помощью json_normalize
. Вы можете попробовать использовать flatten_json что-то вроде этого:
import flatten_json as fj
dic = (fj.flatten(d) for d in sample)
df = pd.DataFrame(dic)
print(df)
_id created_at sections_0_comment ... sections_1__id sections_1_custom_fields sections
0 5f48bee4c54cf6b5e8048274 2020-08-28T08:23:00Z ... 5e59894f68369c2398eb68a9 [] NaN
1 5f48f708fe22ca4d15fb3b55 2020-08-28T12:22:32Z NaN ... NaN NaN []
Комментарии:
1. Известная проблема, поскольку в отчете о текущей проблеме есть отчет, или я должен создать его потенциально? Я чувствую, что должно быть хотя бы предупреждение, поскольку вы можете пропускать данные, не зная, почему. Я только что протестировал модуль flatten_json, спасибо — есть ли шанс, что он может сглаживать / разбивать данные на строки вместо столбцов? Это поле содержит пользовательские поля, которые могут быть добавлены к результатам в любой момент, существуют буквально тысячи уникальных идентификаторов вопросов и ответов, поэтому наличие столбца для каждого из них у меня не сработает.
2. Здесь открыта проблема: github.com/pandas-dev/pandas/issues/21830
3. Чтобы разбить на строки, вам нужно выполнить цикл через dict, а затем выровнять их с помощью
flatten
функции4. Круто, дай мне попробовать. Я также создаю новую проблему, которая казалась немного более конкретной: github.com/pandas-dev/pandas/issues/36245 с примером точного вывода, который я ожидаю при использовании мета-столбцов