Нашли ошибку?
Скачать чистый
урок 3 из 4≈ 20 минупражнения: 0 из 4

Длинный и широкий формат

Одни и те же данные в двух формах: широкой — для чтения глазами, длинной — для вычислений. melt переводит широкую таблицу в длинную, pivot — обратно; чем pivot отличается от pivot_table.

Python запустится при первом выполнении

Таблицу с продажами по месяцам можно записать двумя способами. В широком формате каждый месяц — отдельный столбец. В длинном — месяц записан значением в столбце, и на каждую пару «город, месяц» приходится своя строка.

Данные одни и те же, форма разная. Широкая таблица компактна, её удобно читать. Длинная удобна для расчётов: по столбцу month можно группировать и фильтровать — а по названиям столбцов нельзя. Поэтому данные из отчётов и электронных таблиц, которые обычно приходят широкими, перед анализом часто «расплавляют».

melt: из широкой в длинную

[ ]Широкая таблица: месяцы в столбцах
import pandas as pd

wide = pd.DataFrame({
    "city": ["Омск", "Тула"],
    "jan": [120, 95],
    "feb": [130, 105],
})
wide
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
city jan feb
0 Омск 120 130
1 Тула 95 105

Метод melt («расплавить») превращает столбцы в строки:

[ ]Длинная таблица: строка на каждую пару «город, месяц»
long = wide.melt(id_vars="city", var_name="month", value_name="cups")
long
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
city month cups
0 Омск jan 120
1 Тула jan 95
2 Омск feb 130
3 Тула feb 105
  • id_vars — столбцы, которые остаются как есть: они определяют наблюдение (здесь город);
  • все остальные столбцы «плавятся»: их названия уходят в один новый столбец, значения — в другой;
  • var_name и value_name — названия этих двух новых столбцов.

Было 2 строки × 2 столбца с числами — стало 4 строки: по одной на каждую ячейку.

Если названия не задать, столбцы получат имена variable и value — лучше сразу дать им осмысленные:

[ ]Без var_name и value_name
wide.melt(id_vars="city")
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
city variable value
0 Омск jan 120
1 Тула jan 95
2 Омск feb 130
3 Тула feb 105

С длинной таблицей работают все знакомые приёмы:

[ ]Группировка и фильтр по месяцу
print(long.groupby("month")["cups"].sum())
print(long[long["cups"] > 100])
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
month
feb    235
jan    215
Name: cups, dtype: int64
   city month  cups
0  Омск   jan   120
2  Омск   feb   130
3  Тула   feb   105

Теперь настоящие данные — баллы 30 студентов за первый семестр, по столбцу на предмет:

[ ]Ведомость в широком формате
scores = pd.read_csv("data/scores.csv")
scores.head(3)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
student_id math physics programming history english
0 S01 68 49 68 82 81
1 S02 70 66 68 77 87
2 S03 61 58 72 60 70
[ ]Упражнение 1Расплавить ведомость решено

Переведите таблицу scores в длинный формат и сохраните в marks: столбцы student_id, subject (название предмета) и score (балл). В n_marks сохраните число строк.

marks = ...
n_marks = ...

[ ]Упражнение 2Вопросы к длинной таблице решено

По таблице marks сохраните: в subject_mean — средний балл по каждому предмету, округлённый до одного знака; в weak — оценки ниже 50 баллов, отсортированные от самой низкой.

subject_mean = ...
weak = ...

В широкой таблице на вопрос «у кого и по какому предмету меньше 50» пришлось бы отвечать отдельно для каждого столбца. В длинной это один фильтр.

pivot: из длинной в широкую

Обратное превращение делает pivot. Ему нужно сказать, что будет в строках, что в столбцах и откуда брать значения:

[ ]Обратно в широкую форму
long.pivot(index="city", columns="month", values="cups")
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
month feb jan
city
Омск 130 120
Тула 105 95

Города оказались в индексе, а над названиями столбцов появилась подпись month. Сами столбцы встали по алфавиту: feb раньше jan. Чтобы получить совсем обычную таблицу, индекс превращают в столбец, а подпись убирают:

[ ]Плоская широкая таблица
back = long.pivot(index="city", columns="month", values="cups").reset_index()
back.columns.name = None
back
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
city feb jan
0 Омск 130 120
1 Тула 105 95

pivot и pivot_table

Названия похожи, и параметры те же — index, columns, values. Разница одна, но важная: pivot только переставляет значения и ничего не считает. Поэтому каждой паре «строка, столбец» должно соответствовать ровно одно значение. В полной ведомости у каждого студента по каждому предмету две оценки — за два семестра:

[ ]Ведомость в длинном формате
grades = pd.read_csv("data/grades.csv")
grades.head(4)
Выводсохранённый · нажмите «Выполнить», чтобы получить свой
student group subject term score
0 Анна А. ИТ-21 Математика 1 68
1 Анна А. ИТ-21 Математика 2 71
2 Анна А. ИТ-21 Физика 1 49
3 Анна А. ИТ-21 Физика 2 60
[ ]Два значения на одну ячейкуошибка: ValueError
grades.pivot(index="student", columns="subject", values="score")
Ошибкасохранённый · нажмите «Выполнить», чтобы получить свой
ValueError: Index contains duplicate entries, cannot reshape

Правило простое: переставить — pivot, посчитать — pivot_table.

Вопрос
t = pd.DataFrame({"who": ["a", "a", "b", "b"], "what": ["x", "y", "x", "y"], "n": [1, 2, 3, 4]})
print(t.pivot(index="who", columns="what", values="n").shape)

Что выведет этот код?

[ ]Упражнение 3Ведомость первого семестра решено

Сохраните в first_term строки таблицы grades за первый семестр (term равен 1). Переведите их в широкий формат и сохраните в sheet: студенты в строках, предметы в столбцах, в ячейках — баллы.

first_term = ...
sheet = ...

[ ]Упражнение 4Кто вырос за год решено

Здесь значения нужно посчитать — значит, pivot_table. Сохраните в terms таблицу: студенты в строках, семестры в столбцах, в ячейках — средний балл студента за семестр. Добавьте столбец growth — прирост среднего балла от первого семестра ко второму. В best_growth сохраните имя студента с наибольшим приростом.

terms = ...
# добавьте в terms столбец growth
best_growth = ...

Итоги

  • Широкий формат — значения признака в названиях столбцов; длинный — в отдельном столбце.
  • melt(id_vars=..., var_name=..., value_name=...) — из широкого в длинный.
  • pivot(index=..., columns=..., values=...) — из длинного в широкий; требует одно значение на ячейку.
  • pivot_table делает то же, но сворачивает несколько значений функцией aggfunc.
  • Для расчётов удобнее длинная таблица, для показа — широкая.

Подробнее в справочнике

Нашли ошибку?