2025, Dec 21 18:02

Почему is_year_end в Pandas зависит от частоты DatetimeIndex

Разбираем, как is_year_end в Pandas зависит от частоты DatetimeIndex: для рабочих дней помечается последний рабочий день, для дневной — 31 декабря.

Когда вы работаете с временными рядами Pandas, свойства вроде is_year_end кажутся обманчиво простыми. Кажется естественным ждать, что они пометят 31 декабря как конец года. Но стоит переключить DateTimeIndex на рабочие дни — и результат меняется: «концом года» становится последний рабочий день. Поведение корректно и задумано именно так, но почему так происходит, легко не заметить.

Как воспроизвести ситуацию

Код ниже строит два диапазона за один и тот же период. Первый использует частоту рабочих дней (B). Второй — стандартную дневную частоту (D). Логика одинакова, но из‑за частоты индекса результат различается.

from datetime import datetime
import pandas as pd

ver = pd.__version__
# ver имеет значение '2.0.0'

# Индекс по рабочим дням
biz_idx = pd.date_range(start='2017-01-01', end='2018-01-01', freq='B')

# Посмотрим хвост и флаги is_year_end
last_10_biz = biz_idx[-10:]
flags_biz = last_10_biz.is_year_end
end_biz = biz_idx[biz_idx.is_year_end]

# Индекс по календарным дням
day_idx = pd.date_range(start='2017-01-01', end='2018-01-01')

# Посмотрим хвост и флаги is_year_end
last_10_day = day_idx[-10:]
flags_day = last_10_day.is_year_end
end_day = day_idx[day_idx.is_year_end]

При частоте «рабочие дни» выборка возвращает 2017-12-29 — это действительно был последний рабочий день года. При дневной частоте выборка даёт 2017-12-31, последний календарный день года. Одно и то же свойство, но разные итоги — целиком из‑за частоты DatetimeIndex.

Почему так происходит

Официальное описание кратко объясняет поведение:

is_year_end Логическое значение, указывающее на последний день года (определяется частотой)

Ключевая фраза — «определяется частотой». Если ваш индекс состоит из рабочих дней, аксессор не проверяет, является ли метка времени 31 декабря. Он смотрит, является ли метка времени последним рабочим днём в этом году. Если индекс дневной, проверяется последний календарный день. Это согласуется с реальными сценариями, где значимый «конец года» зависит от ритма ряда. Как справедливо отмечают практики, не каждый «год» закрывается 31 декабря. Университет может считать 06/30 концом года; некоторые розничные организации закрывают год 01/31. Свойство, учитывающее частоту, поддерживает такие фискальные календари.

Что делать: выбирайте частоту, соответствующую вашей цели

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

import pandas as pd

# Конец календарного года (последний календарный день)
cal_idx = pd.date_range(start='2017-01-01', end='2018-01-01')
calendar_year_end = cal_idx[cal_idx.is_year_end]
# Результат: DatetimeIndex(['2017-12-31'], dtype='datetime64[ns]', freq='D')

# Конец рабочего года (последний рабочий день в году)
work_idx = pd.date_range(start='2017-01-01', end='2018-01-01', freq='B')
business_year_end = work_idx[work_idx.is_year_end]
# Результат: DatetimeIndex(['2017-12-29'], dtype='datetime64[ns]', freq='B')

Дополнительные параметры не требуются. Один и тот же аксессор подстраивает логику под частоту индекса.

Почему это важно

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

Итоги

is_year_end — это не жёсткая проверка на 31 декабря. Это свойство, чувствительное к частоте: на индексе рабочих дней оно помечает последний рабочий день, на дневном индексе — последний календарный день. Выберите частоту индекса, которая соответствует вашему определению «года», будь то календарный или фискальный, — и аксессор сделает правильный выбор. Если поведение кажется неожиданным, сначала проверьте частоту DatetimeIndex — это источник истины для того, как временные аксессоры трактуют свою логику.