← Все курсы · Курс Python · Все руководства

pandas pct_change и когда: процентное изменение и текущие итоги

Два односложных метода и выбор, который вы делаете перед каждым из них. · Часть Analyst Prep Kit

К , аналитик данных · Опубликовано

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

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

Краткая версия: pct_change() делит каждое значение на значение в строке выше; cumsum() добавляет все до текущей строки включительно. Оба полагают, что строки, которые вы им дали, соответствуют периодам, которые вы имели в виду.

Идея состоит в том, что происходит, когда предыдущий период равен нулю, поэтому картина понятна.

You cannot grow by a percentage from nothing Three bar positions stand on a baseline, labelled Mar, Apr and May. The March position holds a tall bar and the May position holds a slightly shorter tall bar. The April position holds no bar at all; there is only a short flat mark sitting on the baseline where a bar would start, drawn in amber to show a value of zero. An arc runs from the top of the March bar down to the April mark, and the figure minus one hundred percent is printed on it, which is a perfectly ordinary answer. A second arc runs from the April mark up to the top of the May bar, and the symbol printed on that one is not a percentage at all but the sideways figure eight that means infinity. The picture shows that a fall to nothing has an answer and a rise from nothing does not. MarMay Apr −100% ∞
С марта по апрель — это падение до нуля, то есть минус сто процентов, и это вполне отчетливо. С апреля по май — это подъем из ничего, который вообще не имеет никакого процента, и pandas возвращают скорее бесконечность, чем ошибку.
Что вы узнаете
  1. Сначала доберитесь до серии, и два пути
  2. pct_change
  3. Пустой период и какой метод его создает
  4. Деление на ноль дает бесконечность
  5. когда и чек, который его закрывает
  6. Доля от общего числа
  7. катится и расширяется
  8. Совокупно на группу
  9. Полное до и после
  10. Пограничные случаи, которые стоит знать
  11. Почему это работает
  12. Использование этого в вашем собственном проекте
  13. Все на одном экране
Каждое число на этой странице является реальным. Таблица заказов из шестнадцати строк, используемая во всем этом наборе руководств, работает в pandas 3.0.2. Он действует с 5 января по 25 мая 2026 г. и вообще не содержит апрельских заказов, что не предусмотрено для этой страницы; это та же самая таблица, которую используют все остальные гиды.

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).

Использование этого в вашем собственном проекте

Переписывать каждый временной ряд нет необходимости. Вместо этого сделайте это по порядку.

  1. Посчитайте строки по периодам. Одно сравнение — и обнаруживается целый класс ошибок с отсутствием периода.
  2. Выключатель groupby к resample где угодно период может быть пустым, что вполне реально.
  3. Сортировать явно перед любым изменением или накопительным столбцом, а также чем-то, что разрывает связи.
  4. Заменить бесконечности пропущенными значениями, никогда с нулем.
  5. Помещать diff рядом с pct_change, поэтому всегда существует определенное число.
  6. Утвердить два закрытия: последнее совокупное значение равно сумме, а последняя доля равна 100.

Если у вас под рукой есть бумага, один дополнительный эскиз стоит пяти минут. Нарисуйте ряд полей, по одному на каждый период вашего диапазона отчетности, и заполните те, в которых, как вы знаете, есть данные. Пустые поля — это строки groupby вам не дадут, а их рисование занимает около минуты в год.

Подробнее об этом и еще что-нибудь подобное. Все инструкции находятся в одном месте на индекс направляющих. Та же арифметика и та же ловушка в электронной таблице Месяц за месяцем в Excel, и в базе данных это промежуточная сумма в SQL.

Все на одном экране

Это лист поиска. Закройте правую колонку, проработайте левую и произнесите каждый ответ вслух, прежде чем проверять его.

вещьЧто он делает
Шаг первыйОдна строка за период. И изменение, и накопление требуют порядка.
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 для чего-либо периодического. Это единственная строка на этой странице, которая делает отсутствующий месяц видимым, а не отсутствующим, и все остальные проблемы здесь являются следствием того, что строка так и не была создана. Если серия ведет себя не так, как описано на этой странице, существует общая причина. шлейф диагностики на предмет застревания.

Еще одна мысль, и мне бы искренне хотелось услышать ответы других людей. Моя серия представляла собой еженедельную серию, построенную с помощью группировки, где неделя простоя просто не была неудачной, поэтому о восстановлении сообщалось как о спаде в течение месяца, и график изгибался в неправильном направлении, и каждое число на нем было правильным по отдельности. Как отсутствие менструации повлияло на опубликованный вами тренд?

Ссылки

Промежуточная сумма отличается на одну строку, и код выглядит совершенно правильно.

Python для аналитиков это 203 страницы, сдвиг, pct_change и когда, разобранные, включая то, что каждая из них делает с первой строкой.

Python для аналитиков, 19 $ →
Потренируйтесь на серии с дыркой.

тот Курс Python дает вам повторения: ежемесячные ряды, построенные в обоих направлениях, нулевые базы для обработки, накопительные столбцы для согласования и флэш-карточки, на которых метод создает календарь. Теперь вы владеете методом; комплект - это практика. Ничего устанавливать, учетная запись не требуется.

Откройте комплект Python →