От широких датафреймов в Pandas к длинным и обратно
Изменение размерности датафреймов Pandas — одна из наиболее типовых задач при обработке данных в аналитике. Датафрейм можно переводить и из длинного формата в широкий, и из широкого в длинный. А чем же они отличаются друг от друга?
Давайте рассмотрим простой пример. В таблице ниже показаны средние цены на пять категорий продуктов питания по всем городам США в период с января 2020 года по апрель 2022 года.
Датафрейм слева имеет длинный формат. Столбцы Series ID
и Item
представляют категорию продуктов питания. Year Month
— это столбец, содержащий все месяцы с января 2020 года по апрель 2022 года. Avg. Price ($)
содержит значения средней цены, соответствующие каждому месяцу в столбце Year Month
.
Обратите внимание на то, что подобный формат данных подразумевает повторение каждой категории продуктов питания (Item
) для каждого соответствующего периода. Хотя у нас есть всего лишь пять категорий продуктов питания, в общей сложности в таблице 139 строк, что делает форму текущего датафрейма «длинной».
Датафрейм в правой же части, напротив, имеет широкий формат. В нём каждая строка представляет собой уникальную категорию продуктов питания. Мы преобразуем столбец Year Month
в левом датафрейме таким образом, чтобы каждый месяц находился в отдельном столбце, придавая правому датафрейму «широкую» форму. Так значения столбца Year Month
в левой таблице теперь становятся названиями столбцов в правой таблице, а в них указана Avg. Price ($)
для каждой категории и периода соответственно.
Теперь, когда мы понимаем разницу между широким и длинным форматом данных, давайте посмотрим, как мы можем легко делать такие преобразования в Pandas. Будем использовать всё тот же набор данных. Данные можно скачать здесь. Давайте сначала прочитаем данные из CSV-файла:
import pandas as pd import numpy as np df = pd.read_csv('file.csv') def f(row): if row['Series ID'] == 'APU0000709112': val = 'Milk, Whole per gal' elif row['Series ID'] == 'APU0000708111': val = 'Eggs, large per doz' elif row['Series ID'] == 'APU0000702111': val = 'Bread, whilte per lb' elif row['Series ID'] == 'APU0000703112': val = 'Ground beef,100% per lb' elif row['Series ID'] == 'APU0000706111': val = 'Chicken, whole per lb' else: val = 'NA' return val df['Item']= df.apply(f, axis=1) df.style.set_properties(subset=['Item'], **{'width': '300px'}) df = df[['Series ID','Item','Label','Value']] df = df.rename(columns={'Label': 'Year Month', 'Value': 'Avg. Price ($)'})
Изменение формы с длинной на широкую
Как мы уже выяснили ранее, по факту этот датафрейм имеет длинную форму. Чтобы изменить его размерность на широкую, мы можем использовать метод Pandas pd.pivot()
.
pd.pivot(df, index=..., columns=..., values=...)
columns
: столбец, используемый для создания столбцов нового фрейма (например,Year Month
).values
: столбцы, используемые для заполнения значений (ячеек) нового фрейма (например,Avg. Price ($)
).index
: столбец, используемый для создания индексов (строк) нового фрейма (например,Series ID
иItem
). Если None, используется существующий индекс.
# Изменение длинной формы на широкую df_wide = pd.pivot(df, index=['Series ID', 'Item'], columns = 'Year Month', values = 'Avg. Price ($)') # Перестановка новых столбцов в правильном порядке cols = df['Year Month'].unique() df_wide = df_wide[cols]
Изменение формы с широкой на длинную
Теперь, как нам вернуть широкоформатные данные обратно в длинный формат? Для этого мы можем использовать метод Pandas pd.melt()
.
pd.melt(df, id_vars=, value_vars=, var_name=, value_name=, ignore_index=)
id_vars
: столбцы, используемые в качестве идентификаторов.value_vars
: столбцы, которые нужно вернуть в прежний формат (преобразовать в значения датафрейма). В нашем примере это был бы список столбцов года/месяца ("2020 Jan", "2020 Feb", "2020 Mar" и т.д.)var_name
: название, используемое для столбца "variable".value_name
: название, используемое для столбца "value".ignore_index
: если "True", исходный индекс игнорируется. Если "False", исходный индекс сохраняется.
year_list = list(df_wide.columns) df_long = pd.melt(df_wide, value_vars = year_list, value_name = 'Avg. Price ($)', ignore_index = False)
Итого: если вам нужно изменить размер фрейма данных Pandas с длинного на широкий, используйте pd.pivot()
. Если вам нужно изменить размер фрейма данных Pandas с широкого на длинный, используйте pd.melt()
.
Спасибо за чтение! Мы надеемся, что вы найдёте это краткое руководство полезным.
Источник: Towards Data Science