Как разложить JSON структуру в плоскую таблицу (CSV)?
Есть такая JSON структура (упрощенный вариант):
[
{
"Код": "p12",
"Название": "HN088",
"Экстра": {
"Ширина": "360 мм",
"Глубина": "175 мм",
"Толщина": "22.8 мм",
"Вес": "2100 г",
"Цвет": "Серый"
}
},
{
"Код": "p14",
"Название": "HN092",
"Экстра": {
"Ширина": "300 мм",
"Глубина": "270 мм",
"Толщина": "19.4 мм",
"Вес": "2330 г"
}
}
]
Такой JSON на раз раскладывается в Pandas/tablib итп. И все бы ничего, да в итоге нужна плоская таблица именно такого формата:
|-----------------------------------------------------------------------------------------------------------------|
| Код | Название | Тип | Значение | Тип | Значение | Тип | Значение | Тип | Значение | Тип | Значение |
|-----------------------------------------------------------------------------------------------------------------|
| p12 | HN088 | Ширина | 360 мм | Глубина | 175 мм | Толщина | 22.8 мм | Вес | 2100 г | Цвет | Серый |
| p14 | HN092 | Ширина | 300 мм | Глубина | 270 мм | Толщина | 19.4 мм | Вес | 2330 г | Цвет | |
|-----------------------------------------------------------------------------------------------------------------|
Т.е. каждый ключ у "Экстра" (вложенного словаря) становится значением в столбце Тип на всю высоту самой таблицы (даже если ключ встречается хотя бы один раз, как здесь "Цвет"), которая зависит от количества корневых словарей в корневом списке JSON, а значение ключа должно соотносится только к своему ключу. Как-то запутанно звучит, извините.
Примеры выглядят понятнее, чем моя попытка объяснить.
В общем, с Pandas мне удается разложить только вида:
------------------
Код | Название | Экстра.Ширина | Экстра.Глубина | Экстра.Толщина | Экстра.Вес | Экстра.Цвет
p12 | HN088 | 360 мм | 175 мм | 22.8 мм | 2100 г | Серый
p14 | HN092 | 300 мм | 270 мм | 19.4 мм | 2330 г |
Может, кто-то сталкивался с похожим или знает, как такое сделать на том же Pandas?
Или, что было бы лучше, на чистом Python, чтобы увидеть сам алгоритм и быстрее понять, а не подкапотную магию Pandas.
Ответы (2 шт):
Решение с минимальным количеством "костылей":
df = (pd
.json_normalize(data)
.set_index(["Код", "Название"])
.rename(columns=lambda c: c.replace("Экстра.", "")))
res = pd.DataFrame(index=df.index)
for i, col in enumerate(df.columns[::-1]):
res.insert(0, f"Значение.{i}", df[col])
res.insert(0, f"Тип.{i}", [col] * len(df))
res = res.rename(columns=lambda c: c.split(".")[0]).reset_index()
результат:
In [409]: res
Out[409]:
Код Название Тип Значение Тип Значение Тип Значение Тип Значение Тип Значение
0 p12 HN088 Ширина 360 мм Глубина 175 мм Толщина 22.8 мм Вес 2100 г Цвет Серый
1 p14 HN092 Ширина 300 мм Глубина 270 мм Толщина 19.4 мм Вес 2330 г Цвет NaN
import pandas as pd
df = pd.read_json("test.json")
df2 = pd.json_normalize(df["Экстра"])
for x in range(0, (len(df2.columns))*2,2):
df2.insert(x, "Тип", df2.columns[x], allow_duplicates=True)
df2.columns = ["Значение" if "Тип" not in x else x for x in df2.columns]
df = pd.concat([df.drop(['Экстра'], axis=1), df2], axis=1)
df:
Код Название Тип Значение Тип Значение Тип Значение Тип \
0 p12 HN088 Ширина 360 мм Глубина 175 мм Толщина 22.8 мм Вес
1 p14 HN092 Ширина 300 мм Глубина 270 мм Толщина 19.4 мм Вес
Значение Тип Значение
0 2100 г Цвет Серый
1 2330 г Цвет NaN