← Все курсы · Курс Python · Все руководства
pandas pct_change и когда: процентное изменение и текущие итоги
В конце этой страницы вы можете превратить транзакции в ежемесячные серии, добавить изменения за период и совокупный итог, получить столбец «доля от общего объема», сгладить зашумленную линию и запустить все это отдельно для каждой группы. Прошло около двадцати пяти минут, и каждое число ниже получено в результате выполнения кода.
Вот чем заняться сегодня с сериалом, который у вас уже есть. Сосчитайте его строки по количеству периодов в вашем диапазоне дат. Если ваши данные охватывают период с января по май, а ряд состоит из четырех строк, период ничего не дал, он так и не стал строкой, и каждое изменение цифры после разрыва сравнивает неправильную пару.
Краткая версия: pct_change() делит каждое значение на значение в строке выше; cumsum() добавляет все до текущей строки включительно. Оба полагают, что строки, которые вы им дали, соответствуют периодам, которые вы имели в виду.
Идея состоит в том, что происходит, когда предыдущий период равен нулю, поэтому картина понятна.
- Сначала доберитесь до серии, и два пути
- pct_change
- Пустой период и какой метод его создает
- Деление на ноль дает бесконечность
- когда и чек, который его закрывает
- Доля от общего числа
- катится и расширяется
- Совокупно на группу
- Полное до и после
- Пограничные случаи, которые стоит знать
- Почему это работает
- Использование этого в вашем собственном проекте
- Все на одном экране
1. Сначала доберитесь до серии, и двумя способами
И итоговые изменения, и совокупные итоги работают со строками по порядку, поэтому первое задание — это одна строка за период. У pandas есть два способа сделать это, и они не согласны.
o.groupby(o.order_date.dt.to_period("M"))["revenue"].sum()
2026-01 2630
2026-02 2245
2026-03 2585
2026-05 2430
o.set_index("order_date")["revenue"].resample("MS").sum()
2026-01-01 2630
2026-02-01 2245
2026-03-01 2585
2026-04-01 0
2026-05-01 2430
Четыре ряда против пяти. groupby может создать строку только для значения, которое существует в данных, поэтому месяц без заказов ничего не дает. resample сначала строит обычный календарь по всему диапазону, а затем заполняет его, поэтому апрель отображается с нулем.
Это самая полезная вещь на этой странице, и о ней стоит сказать прямо: передискретизация дает вам календарь бесплатно, а группировка - нет. Тот же пробел необходимо устранить вручную в электронной таблице и путем присоединения к сгенерированному календарю в SQL; здесь это имя метода.
resample нужен индекс даты и времени, вот что set_index("order_date") предназначено для, а строка представляет собой частоту: "MS" на начало месяца, "ME" на конец месяца, "W" на неделю, "D" на каждый день, "QS" для начала четверти. Выберите начало месяца, если у вас нет причины, потому что метка 2026-04-01 читается как апрель, а метка 2026-04-30 читается как май кем-то, кто спешит.
2. pct_change
Один метод, и он сравнивает каждую строку с строкой над ней.
s.pct_change() a proportion: -0.1464
(s.pct_change() * 100).round(1) a percentage: -14.6
В четырехрядной версии:
2026-01 2630 NaN
2026-02 2245 -14.6
2026-03 2585 15.1
2026-05 2430 -6.0
Проверьте вручную: 2245, разделенное на 2630, равно 0,8536, минус 1 равно -0,1464, что составляет -14,6%. Первый ряд NaN потому что над ним ничего нет, и это правильно: оставьте его, а не заполняйте нулем, потому что ноль будет претендовать на плоский рост в период, когда рост не определен.
periods= меняет, насколько далеко назад это выглядит. periods=12 в ежемесячном ряду это год к году, и это сравнение, которое вам нужно для чего-либо сезонного, и это означает только двенадцать месяцев, если каждый месяц представляет собой строку.
3. Пустой период и какой метод его создает
Перед пояснением: взгляните еще раз на таблицу из четырех строк. Май составляет -6,0%. Скажите, какие два месяца можно сравнить.
Май и март. pct_change выглядит один Строка назад, а ряд выше мая — это март, потому что апрель так и не стал рядом. Эта цифра представляет собой реальное сравнение двух реальных месяцев, напечатанное в столбце с надписью «Месячное изменение».
Теперь тот же расчет для серии с повторной выборкой, где существует апрель:
2026-01-01 2630 NaN
2026-02-01 2245 -14.6
2026-03-01 2585 15.1
2026-04-01 0 -100.0
2026-05-01 2430 inf
Пять рядов и три цифры изменили значение. Апрель — это видимый ноль и истинное −100,0%. Май уже не −6,0%; это inf, это четвертый раздел.
Вторая версия выглядит более запутанной и является более честной. Первая версия является аккуратной, потому что в ней отсутствует строка, а аккуратность, вызванная отсутствием данных, является наиболее убедительной ошибкой.
Скажите вслух, какой вывод вы бы сделали из таблицы из четырех строк. Что-то вроде «Май был немного ниже апреля», то есть предложение о месяце, в течение которого вообще ничего не происходило.
4. Деление на ноль дает бесконечность
Апрель был нулевым, поэтому изменение мая равно 2430, разделенному на 0, и pandas возвращаются. inf а не повышать.
s.pct_change().tolist()
[nan, -0.1464, 0.1514, -1.0, inf]
Это математически правильный ответ, и его ужасно оставлять в отчете, потому что он будет отформатирован как пробел, ошибка или что-то абсурдное в зависимости от того, что будет читаться дальше. Рост из ничего не имеет процента: любое увеличение с нуля бесконечно в процентном отношении, поэтому не существует разумного числа, которое можно было бы вывести.
Специально преобразуйте его в отсутствующее значение, чтобы нижестоящий код обрабатывал его как неизвестное, а не как очень большое число:
import numpy as np
change = s.pct_change().replace([np.inf, -np.inf], np.nan)
[nan, -0.1464, 0.1514, -1.0, nan]
Чего не следует делать, так это fillna(0), который сообщает о 0% росте за месяц, который вырос с нуля до 2430. Это не компромисс по округлению, это противоположно тому, что произошло.
И что бы вы ни показывали, указывайте абсолютное изменение рядом с процентом. Февральское значение -14,6% также равно -385; Мэйский рост не имеет процентного значения и составляет +2430. Счет и норма читаются по-разному, и их пару гораздо труднее неправильно прочитать, чем каждый из них по отдельности.
5. когда и чек, который его закрывает
cumsum() добавляет каждое значение ко всему, что перед ним.
2026-01-01 2630 2630
2026-02-01 2245 4875
2026-03-01 2585 7460
2026-04-01 0 7460
2026-05-01 2430 9890
Строка Эйприл содержит фиксированную сумму итогов, и это совершенно правильно: ничего не произошло, поэтому промежуточная сумма не изменилась, и шаг в строке скорее виден, чем отсутствует.
Последнее значение — 9890, что является суммой всей таблицы. Это равенство является проверкой, и оно должно быть утверждением, а не взглядом:
assert s.cumsum().iloc[-1] == s.sum()
Если это не удалось, либо строка была продублирована до накопления, обычно путем слияния, либо серия содержит отсутствующее значение. Оба меняют последнее число, и ни один из них не меняет ничего видимого в столбце.
6. Доля от общего количества
Совокупная сумма, разделенная на общую сумму, — это столбец, который люди действительно читают, потому что процент года — это предложение, а 7460 — число.
(s.cumsum() / s.sum() * 100).round(1)
2026-01-01 26.6
2026-02-01 49.3
2026-03-01 75.4
2026-04-01 75.4
2026-05-01 100.0
К концу марта было получено три четверти годовой выручки. И последнее значение — 100,0, это тот же чек, что и в пятом разделе, но в другой шляпе: если итоговая доля не равна точно сотне, что-то не складывается.
Та же форма дает простую акцию, а не накопительную, s / s.sum(), и оба заслуживают внимания: один говорит, насколько большим был каждый месяц, другой говорит, какая часть года прошла к тому времени.
7. прокатка и расширение
Два окна, и разница в том, движется ли старт.
s.rolling(3).mean().round(2) s.expanding().sum()
2026-01-01 NaN 2630
2026-02-01 NaN 4875
2026-03-01 2486.67 7460
2026-04-01 1610.00 7460
2026-05-01 1671.67 9890
rolling(3) представляет собой окно фиксированной ширины, которое перемещается: каждое значение представляет собой среднее значение этой строки и двух предшествующих ей. expanding() начинается с самого начала и растет, поэтому expanding().sum() это точно cumsum() и два столбца выше совпадают.
Проверьте одно скользящее значение: три месяца, заканчивающиеся в марте, составляют 2630, 2245 и 2585, что в среднем составляет 2486,67. Первые две строки NaN потому что трех значений еще нет, и это правильное значение по умолчанию: min_periods=1 заполнил бы их средними значениями одного и двух значений, которые выглядят как средние значения за три месяца, но таковыми не являются.
Сглаживание предназначено для считывания направления из зашумленной линии, а честная версия делает это симметрично или не делает вообще. Скользящая средняя всегда отстает от объекта, который она сглаживает, поэтому точка поворота появляется позже, чем она произошла; использовать center=True когда вы описываете прошлое, и скользящее окно, когда вы притворяетесь, что не знаете будущего.
8. Суммарно на группу
Все вышеперечисленное работает для каждой группы с одним дополнительным вызовом, а порядок имеет большее значение, чем обычно, поскольку накопление соответствует порядку строк.
c = o.sort_values("order_date")
c["running"] = c.groupby("region")["revenue"].cumsum()
last row of each region:
East 2026-05-04 600 3040
North 2026-05-11 510 2495
South 2026-05-18 880 2670
West 2026-05-25 440 1685
Каждый регион накапливается отдельно и заканчивается своей собственной суммой, и эти четыре суммы в сумме составляют 9890, поэтому каждая строка учитывается ровно один раз.
тот sort_values спереди не является обязательным. groupby(...).cumsum() накапливается в любом порядке, в котором находятся строки, поэтому несортированный фрейм дает промежуточный итог, который является арифметически правильным и хронологически бессмысленным. Сортируйте сначала каждый раз и по столбцу, который разрывает связи, чтобы результат был одинаковым при каждом запуске.
pct_change работает так же, groupby(...)["revenue"].pct_change(), и он правильно возвращает NaN для первого ряда каждой группы, а не переносить их с предыдущей группы.
Представьте на мгновение свою наиболее часто используемую серию. Он построен с groupby или с resample, и знаете ли вы, пропущены ли какие-либо из его периодов?
Полное до и после
Те же шестнадцать заказов, тот же вопрос: как движется выручка?
До
s = o.groupby(o.order_date.dt.to_period("M"))["revenue"].sum()
s.pct_change()
2026-01 2630 NaN
2026-02 2245 -14.6
2026-03 2585 15.1
2026-05 2430 -6.0
Четыре чистых строки и ни одной ошибки говорят о том, что май упал на 6% по сравнению с предыдущим месяцем. За месяц до этого он вообще не продавался и его нет на странице.
после
s = o.set_index("order_date")["revenue"].resample("MS").sum()
out = s.to_frame("revenue")
out["change"] = s.diff() # absolute, always readable
out["pct"] = (s.pct_change().replace([np.inf,-np.inf], np.nan) * 100).round(1)
out["cumsum"] = s.cumsum()
out["share"] = (s.cumsum() / s.sum() * 100).round(1)
out["ma3"] = s.rolling(3).mean().round(2)
assert s.cumsum().iloc[-1] == s.sum() # 9890
assert out["share"].iloc[-1] == 100.0
revenue change pct cumsum share ma3
2026-01-01 2630 NaN NaN 2630 26.6 NaN
2026-02-01 2245 -385.0 -14.6 4875 49.3 NaN
2026-03-01 2585 340.0 15.1 7460 75.4 2486.67
2026-04-01 0 -2585.0 -100.0 7460 75.4 1610.00
2026-05-01 2430 2430.0 NaN 9890 100.0 1671.67
Утверждение, и именно поэтому первое решение на странице — это имя метода: те же шестнадцать заказов дают четыре строки и кругленькую -6,0% или пять строк, -100% и неопределенное изменение, и разница лишь в том, попросили ли вы у pandas календарь.
Пограничные случаи, которые стоит знать
Шесть, которые появятся.
Несортированный сериал. Оба метода используют порядок строк, поэтому кадр, который не соответствует порядку дат, создает столбец изменений и промежуточный итог, которые являются арифметически правильными и ничего не описывают. Сортируйте заранее, всегда.
Отсутствующие значения. A NaN в середине ряда распространяется: cumsum отмечает эту позицию NaN и продолжает делать все остальное, и pct_change возвращает NaN для двух строк, а не для одной, поскольку задействованы и значение, и его сосед. Решите, является ли разрыв нулем или неизвестным, прежде чем накапливать.
Отрицательные значения. Если период может быть отрицательным, месяц возврата, pct_change дает ответы, которые арифметически верны и сообщают противоположное тому, что произошло. Сообщите там об абсолютном изменении и сбросьте процент.
Частичный текущий период. Последняя строка серии с повторной выборкой почти всегда неполная, поэтому она всегда выглядит как коллапс. Исключите это или пометьте.
Повторная выборка по индексу без даты и времени. Он повышает, что является хорошим результатом; если ваши даты являются строками, вы узнаете об этом здесь, и руководство по read_csv здесь вы это исправите.
diff против pct_change. Они отвечают на разные вопросы, а их имена подталкивают к неправильному выбору. diff является абсолютным изменением и всегда определяется; pct_change представляет собой относительное изменение и не определено с нулевой базой. Опубликуйте оба.
Почему это работает
Вся страница включает разницу между серией, индексированной по произошедшим значениям, и серией, индексированной по каждому периоду в диапазоне. groupby создает первое, поскольку группировка может создать группу только там, где существует строка. resample создает второй, поскольку он создает индекс на основе диапазона и частоты перед просмотром данных. Все, что следует за этим, вводящее в заблуждение -6,0%, честное -100%, бесконечность, плоский шаг в кумулятивной линии, находится после того, какой из этих двух индексов вы выбрали, и ни один из методов не предупреждает вас о том, что выбор был сделан.
Сообщить о количестве рядом со скоростью тоже не суета. Люди более точно рассуждают о частотах, чем о соотношениях, вычисленных на их основе, а представление лежащих в их основе чисел вместе с процентами достоверно повышает степень понимания процентов (Гигеренцер & Хоффраге, 1995, Психологический обзор, 102(4), 684–704). Это аргумент в пользу diff колонна, стоящая рядом с pct столбец: -385 и -14,6% вместе не могут быть неправильно истолкованы так, как можно прочитать только -14,6%, а в строке, где процент не определен, абсолютное изменение по-прежнему совершенно информативно на уровне +2430.
Сглаживание в седьмом разделе — это грубый член семьи, за которым стоит много мыслей. Локальные методы аппроксимации оценивают значение в каждой точке по отношению к ее соседям с весами, которые падают с расстоянием именно потому, что простое скользящее среднее одинаково обрабатывает каждую точку в окне и отстает от ряда, который оно описывает (Кливленд & Девлин, 1988, Журнал Американской статистической ассоциации, 83(403), 596–610). Трехмесячное скользящее среднее подходит для определения направления и не является моделью, и различие становится важным, как только кто-то продлевает линию вправо.
Одно замечание в шпаргалке ниже. Он создан для того, чтобы его просматривали и вспоминали, а не перечитывали, потому что самостоятельное тестирование превосходит повторное изучение очень большого количества экспериментов, при этом наибольшее преимущество достигается за счет значимых задержек (Роуленд, 2014, Психологический вестник, 140(6), 1432–1463).
Использование этого в вашем собственном проекте
Переписывать каждый временной ряд нет необходимости. Вместо этого сделайте это по порядку.
- Посчитайте строки по периодам. Одно сравнение — и обнаруживается целый класс ошибок с отсутствием периода.
- Выключатель
groupbyкresampleгде угодно период может быть пустым, что вполне реально. - Сортировать явно перед любым изменением или накопительным столбцом, а также чем-то, что разрывает связи.
- Заменить бесконечности пропущенными значениями, никогда с нулем.
- Помещать
diffрядом сpct_change, поэтому всегда существует определенное число. - Утвердить два закрытия: последнее совокупное значение равно сумме, а последняя доля равна 100.
Если у вас под рукой есть бумага, один дополнительный эскиз стоит пяти минут. Нарисуйте ряд полей, по одному на каждый период вашего диапазона отчетности, и заполните те, в которых, как вы знаете, есть данные. Пустые поля — это строки groupby вам не дадут, а их рисование занимает около минуты в год.
Все на одном экране
Это лист поиска. Закройте правую колонку, проработайте левую и произнесите каждый ответ вслух, прежде чем проверять его.
| вещь | Что он делает |
|---|---|
| Шаг первый | Одна строка за период. И изменение, и накопление требуют порядка. |
groupby(to_period("M")) | Только периоды, которые существуют в данных. Здесь четыре ряда. |
resample("MS") | Сначала строит календарь. Пять рядов, апрель на нуле. |
| Частотные строки | D, W, MS, ME, QS. Предпочитайте начало месяца для ярлыков. |
pct_change() | Каждая строка против предыдущей. Пропорция, а не процент. |
| Первый ряд | NaN. Оставь это; ноль будет требовать стабильного роста. |
periods=12 | Год за годом, только если каждый месяц подряд. |
| Отсутствующий период | Заставляет pct_change молча сравнивать два разных периода. |
| Нулевая база | возвращает inf, это не ошибка. Заменить на NaN. |
| Никогда | fillna(0) в столбце изменений. |
diff() | Абсолютное изменение. Всегда определено. Опубликуйте и это. |
cumsum() | Текущий итог. Флет на нулевом периоде, что правильно. |
| Чек | Последняя совокупная сумма равна сумме. Утверждайте это. |
| Доля от общего числа | cumsum() / sum(). Последнее значение должно быть 100,0. |
rolling(3).mean() | Исправлено окно, которое скользит. Первые строки NaN. |
expanding().sum() | Окно, которое растет. Идентичен камсуму. |
| За группу | groupby(col)["x"].cumsum(). Всегда сначала сортируйте. |
resample скорее, чем groupby для чего-либо периодического. Это единственная строка на этой странице, которая делает отсутствующий месяц видимым, а не отсутствующим, и все остальные проблемы здесь являются следствием того, что строка так и не была создана. Если серия ведет себя не так, как описано на этой странице, существует общая причина. шлейф диагностики на предмет застревания.Еще одна мысль, и мне бы искренне хотелось услышать ответы других людей. Моя серия представляла собой еженедельную серию, построенную с помощью группировки, где неделя простоя просто не была неудачной, поэтому о восстановлении сообщалось как о спаде в течение месяца, и график изгибался в неправильном направлении, и каждое число на нем было правильным по отдельности. Как отсутствие менструации повлияло на опубликованный вами тренд?
Ссылки
- Гигеренцер Г. и Хоффраж У. (1995). Как улучшить байесовские рассуждения без инструкций: частотные форматы. Психологический обзор, 102(4), 684–704.
- Кливленд, В.С., и Девлин, С.Дж. (1988). Локально взвешенная регрессия: подход к регрессионному анализу путем локальной подгонки. Журнал Американской статистической ассоциации, 83(403), 596–610.
- Роуленд, Калифорния (2014). Влияние тестирования и повторного исследования на удержание: метааналитический обзор эффекта тестирования. Психологический вестник, 140(6), 1432–1463.
Python для аналитиков это 203 страницы, сдвиг, pct_change и когда, разобранные, включая то, что каждая из них делает с первой строкой.
Python для аналитиков, 19 $ →тот Курс Python дает вам повторения: ежемесячные ряды, построенные в обоих направлениях, нулевые базы для обработки, накопительные столбцы для согласования и флэш-карточки, на которых метод создает календарь. Теперь вы владеете методом; комплект - это практика. Ничего устанавливать, учетная запись не требуется.
Откройте комплект Python →