Как сгладить фрейм данных 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])

Image


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)

enter image description here