Как сделать сводную ("перевернуть") таблицу?
Есть таблица с данными:
ID модели | модель | категория характеристики | название характеристики | характеристика
Пример :
1 | Phillips 100 | size | length | 100
1 | Phillips 100 | size | width | 200
1 | Phillips 100 | size | height | 200
1 | Phillips 100 | screen| diagonal | 25
1 | Phillips 100 | price | | 100$
2 | Phillips 200 | size | width | 300
2 | Phillips 200 | size | height | 300
3 | Phillips 300 | tech | smart tv| 3 generation
То есть, количество строк для каждой модели может отличаться, в третьей колонке может быть пустое значение.
Я хочу из этого сделать CSV для импорта в БД, то есть подобной структуры:
id| name | size_length| size_width| size_height|screen_diagonal|price|tech_smart_tv
1 | Phillips 100| 100 | 200 | 200 | 25 | 100$| 3 generation
Перепробовал много вариантов, но пока без особых успехов.
Сейчас на этапе создания цикла, который бы парсил нужные значения в пределах id, сохранял и переходил к следующему id. Но пока всё работает некорректно.
Может быть есть какие-то стандарты решения подобных задач?
Пока я действую по такой логике:
f = open('folder/table.csv', 'r')
lines = f.readlines()
#Далее создаю цикл while, для разделения по id
i = 0
while i < int(lines[-1].split(';')[0]):
#Достаю список элементов с id 1 (и далее по циклу)
target = [item for item in lines if item.split(';')[0] == str(i+1)]
for lis in target:
#Разбиваю строки по ячейкам
ttarg = lis.split(';')
# А вот дальше начинаются проблемы, т.к. не могу корректно вытащить
# нужные мне значения
# Пытаюсь подобным образом:
try:
n_nn = [item for item in ttarg if ttarg[2].find('ch_cat')
!= -1
and ttarg[3].find('ch_name')!= -1 ]
except:
pass
# Но это работает некорректно, как и опробованные ранее
варианты(инкремент while на месте)
Ответы (2 шт):
Автор решения: MaxU
→ Ссылка
Воспользуйтесь модулем Pandas:
import pandas as pd # pip install pandas
df = pd.read_csv(filename, sep=";", header=None,
names=["id", "name", "attr1", "attr2", "val"])
df = df.assign(col=df.pop("attr1")+"_"+df.pop("attr2").fillna(""))
df["col"] = df["col"].str.replace(r"_$", "")
res = (df.pivot_table(index=["id", "name"], columns="col",
values="val", aggfunc="first", fill_value="")
.reset_index())
res.to_csv("c:/temp/res.csv", index=False, sep=";")
результат (c:/temp/res.csv):
id;name;price;screen_diagonal;size_height;size_length;size_width;tech_smart tv
1;Phillips 100;100$;25;200;100;200;
2;Phillips 200;;;300;;300;
3;Phillips 300;;;;;;3 generation
Автор решения: MiniMax
→ Ссылка
Можно сделать так:
- Разбиваем строку по
;и соединяем поля 3 и 4, таким образом сразу получая названия будущих столбцов типаsize_width,size_heightи т.д. Формируем древовидный словарь вида:
{ id : { name : { size_width : 100, size_height : 200, ... } } }- Параллельно запоминаем уникальные заголовки столбцов (
size_width,size_height, ...), они понадобятся для печати. - Печатаем словарь, перебирая по очереди айдишники и разворачивая каждый в
csvстроку. Сначала идут поляidиname, остальные в соответствии с упорядоченным списком заголовков.
Решение
with open("input.txt", 'r') as in_file:
headers = set()
nested_dct = {}
for line in in_file:
lst = line.rstrip('\n').split(';')
lst[2] = f"{lst[2]}_{lst[3]}" if lst[3] != '' else lst[2]
del lst[3]
i_d = lst[0]
name = lst[1]
merged_ch_name = lst[2]
if i_d not in nested_dct:
nested_dct[i_d] = {name : {}}
nested_dct[i_d][name][merged_ch_name] = lst[3]
headers.add(merged_ch_name)
headers = sorted(headers)
print(';'.join(['id', 'name'] + headers))
for i_d in sorted(nested_dct.keys()):
# popitem() нужен так как название неизвестно - не можем указать
# ключ стандартным способом: nested_dct[i_d]['ключ неизвестен'],
# однако известно, что под айдишником может быть только 1 модель =>
# вынимаем её.
# ключ это имя - идёт в name (например "Phillips 100"),
# значение - словарь со всеми характеристиками, идёт в rest.
name, rest = nested_dct[i_d].popitem()
row = f"{i_d};{name}"
# Перебираем все заголовки, проверяя имеет ли данная модель
# значение в этом столбце: да - пишем, нет - пропуск.
for header in headers:
row += ';'
if header in rest:
row += rest[header]
print(row)
input.txt
1;Phillips 100;size;length;100
1;Phillips 100;size;width;200
1;Phillips 100;size;height;200
1;Phillips 100;screen;diagonal;25
1;Phillips 100;price;;100$
2;Phillips 200;size;width;300
2;Phillips 200;size;height;300
3;Phillips 300;tech;smart tv;3 generation
Output
id;name;price;screen_diagonal;size_height;size_length;size_width;tech_smart tv
1;Phillips 100;100$;25;200;100;200;
2;Phillips 200;;;300;;300;
3;Phillips 300;;;;;;3 generation