Как сгладить фрейм данных pandas с некоторыми столбцами как json?
у меня есть фрейм данных df
, который загружает данные из базы данных. Большинство столбцов-это строки json, а некоторые-даже список jsons. Например:
id name columnA columnB
1 John {"dist": "600", "time": "0:12.10"} [{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "3rd", "value": "200"}, {"pos": "total", "value": "1000"}]
2 Mike {"dist": "600"} [{"pos": "1st", "value": "500"},{"pos": "2nd", "value": "300"},{"pos": "total", "value": "800"}]
...
как вы можете видеть, все строки не имеют одинаковое количество элементов в строках json для столбца.
что мне нужно сделать, это сохранить нормальные колонки как id
и name
, как есть и сгладить столбцы json, как так
id name columnA.dist columnA.time columnB.pos.1st columnB.pos.2nd columnB.pos.3rd columnB.pos.total
1 John 600 0:12.10 500 300 200 1000
2 Mark 600 NaN 500 300 Nan 800
Я пробовал использовать json_normalize
как так что
from pandas.io.json import json_normalize
json_normalize(df)
но, кажется, есть некоторые проблемы с keyerror
. Как правильно это сделать?
2 ответов
вот решение, используя json_normalize()
снова с помощью пользовательской функции, чтобы получить данные в правильном формате, понятном .
import ast
from pandas.io.json import json_normalize
def only_dict(d):
'''
Convert json string representation of dictionary to a python dict
'''
return ast.literal_eval(d)
def list_of_dicts(ld):
'''
Create a mapping of the tuples formed after
converting json strings of list to a python list
'''
return dict([(list(d.values())[1], list(d.values())[0]) for d in ast.literal_eval(ld)])
A = json_normalize(df['columnA'].apply(only_dict).tolist()).add_prefix('columnA.')
B = json_normalize(df['columnB'].apply(list_of_dicts).tolist()).add_prefix('columnB.pos.')
наконец, присоединиться к DFs
по общему индексу к вам:
df[['id', 'name']].join([A, B])
EDIT: - согласно комментарию @MartijnPieters, рекомендуемым способом декодирования строк json было бы использовать json.loads()
что гораздо быстрее по сравнению с использованием ast.literal_eval()
если вы знаете, что источником данных является JSON.
создайте пользовательскую функцию для сглаживания columnB
затем использовать pd.concat
def flatten(js):
return pd.DataFrame(js).set_index('pos').squeeze()
pd.concat([df.drop(['columnA', 'columnB'], axis=1),
df.columnA.apply(pd.Series),
df.columnB.apply(flatten)], axis=1)