Как распарсить колонку json в pandas?

В датафрейме есть колонка со строками вида: {\'name\': {\'type\': \'STRING\', \'value\': \'test\'}, \'company\': {\'type\': \'STRING\', \'value\': \'ООО "Рога копыта"\'}, \'role\': {\'type\': \'STRING\', \'value\': \'пользователь\'}}

Пытаюсь распарсить на отдельные колонки, каждый кастомный параметр, взяв название колонки кастомного параметра и заполнив его значением в value.

Мой код:

temp_table = pd.json_normalize(data.custom)
list_col = [x for x in temp_table.columns if x.split('.')[1] == 'type']
temp_table = temp_table.drop(columns=list_col)
temp_table.columns = [x.split('.')[0] for x in temp_table.columns]
data = data[['col1', 'col2']].join(temp_table)

Проблема, что при pd.json_normalize(data.custom) у меня возвращается пустая таблица. Мои предположения были, что проблема была в одинарных кавычках, я попробовала их заменить:

data.custom=data.custom.apply(lambda x: x.replace("'", '"'))
data.custom=data.custom.str.replace('"', '\\\"', regex=True)

не помогло. И пыталась привести строки в соответствие так:

data.custom=data.custom.apply(lambda x: json.loads(json.dumps(x)))

тоже не помогло. Теперь полагаю, что я не правильно установила причину. Возможно проблема в целом в моем коде.


Ответы (2 шт):

Автор решения: Oopss
import pandas as pd
import json


your_dict = {'name': {'type': 'STRING', 'value': 'test'},
 'company': {'type': 'STRING', 'value': 'ООО "Рога копыта"'},
  'role': {'type': 'STRING', 'value': 'пользователь'}}

#Словарь в json
your_json_string=json.dumps(your_dict)

#JSON  d JSON объект
data = json.loads(your_json_string)

#Датафрейм
df = pd.DataFrame(data)

print(df)

         name            company          role
type   STRING             STRING        STRING
value    test  ООО "Рога копыта"  пользователь
→ Ссылка
Автор решения: Алексей Р

Комментарии в коде

import ast  # импортируем библиотеку ast для безопасного преобразования текста в словарь

df = pd.DataFrame({'Код': [1001, 1003, 1005],
                   'custom': ["""{'name': {'type': 'STRING', 'value': 'test'}, 'company': {'type': 'STRING', 'value': 'ООО "Рога копыта"'}, 'role': {'type': 'STRING', 'value': 'пользователь'}}""",
                              """{'name': {'type': 'STRING', 'value': 'Вася'}, 'company': {'type': 'STRING', 'value': 'ООО "Ромашка"'}, 'role': {'type': 'STRING', 'value': 'админ'}}""",
                              """{'name': {'type': 'STRING', 'value': 'Петя'}, 'company': {'type': 'STRING', 'value': 'ООО "Ронин"'}, 'role': {'type': 'STRING', 'value': 'продавец'}}"""]})

df.custom = df.custom.transform(ast.literal_eval)  # преобразуем текст в словари в каждой ячейке столбца custom
fields = list(set(df.custom.transform(list).sum()))  # из этого же столбца вытаскиваем множество ключей - они станут названиями столбцов
df[fields] = df['custom'].apply(lambda x: pd.Series({k: x[k]['value'] for k in x}))  # разбираем словари на столбцы
df = df.drop(columns='custom')  # удаляем исходный столбец custom, если он уже не нужен
print(df)
    Код  name            company          role
0  1001  test  ООО "Рога копыта"  пользователь
1  1003  Вася      ООО "Ромашка"         админ
2  1005  Петя        ООО "Ронин"      продавец

Другой подход - сделать список словарей, затем за 1 операцию преобразовать во фрейм. Протестил на 810 к строчек, отработало за 33 секунды.

import ast  # импортируем библиотеку ast для безопасного преобразования текста в словарь
import time
start = time.time()
mul = 270_000 #000
df = pd.DataFrame({'Код': [1001, 1003, 1005] * mul,
                   'custom': ["""{'name': {'type': 'STRING', 'value': 'test'}, 'company': {'type': 'STRING', 'value': 'ООО "Рога копыта"'}, 'role': {'type': 'STRING', 'value': 'пользователь'}}""",
                              """{'name1': {'type': 'STRING', 'value': 'Вася'}, 'company1': {'type': 'STRING', 'value': 'ООО "Ромашка"'}, 'role1': {'type': 'STRING', 'value': 'админ'}}""",
                              """{'name2': {'type': 'STRING', 'value': 'Петя'}, 'company2': {'type': 'STRING', 'value': 'ООО "Ронин"'}, 'role2': {'type': 'STRING', 'value': 'продавец'}}"""] * mul})

lst = []
dicts = df.custom.transform(ast.literal_eval)  # преобразуем текст в словари в каждой ячейке столбца custom
dicts.apply(lambda x: lst.append({k: x[k]['value'] for k in x}))
df = pd.DataFrame(lst)
print(df.head())
print((time.time()-start), 'сек.')
   name            company          role name1       company1  role1 name2     company2     role2
0  test  ООО "Рога копыта"  пользователь   NaN            NaN    NaN   NaN          NaN       NaN
1   NaN                NaN           NaN  Вася  ООО "Ромашка"  админ   NaN          NaN       NaN
2   NaN                NaN           NaN   NaN            NaN    NaN  Петя  ООО "Ронин"  продавец
3  test  ООО "Рога копыта"  пользователь   NaN            NaN    NaN   NaN          NaN       NaN
4   NaN                NaN           NaN  Вася  ООО "Ромашка"  админ   NaN          NaN       NaN
32.78659248352051 сек.

То же одной строкой (цепочкой методов):

df = pd.DataFrame(df.custom.transform(ast.literal_eval).apply(lambda x: {k: x[k]['value'] for k in x}).to_list())  # преобразуем текст в словари в каждой ячейке столбца custom, переводим в список словарей и создаем новый фрейм
→ Ссылка