2025, Dec 05 18:02

Интерполяция почасового ряда в pandas без длинных разрывов

Как разбить временной ряд по разрывам и интерполировать только внутри сегментов: pandas, resample и interpolate для почасовых данных без ложных заполнений.

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

Постановка задачи

Представьте DataFrame с временным индексом, в котором отсутствуют отдельные часы. Наивная связка resample плюс interpolate заполняет каждую дырку, включая большие. Например:

frame = frame.resample('1h').first()
frame = frame.interpolate(method='time')
frame

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

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

Ресемплинг формирует регулярный почасовой индекс, а интерполяция по времени рассчитывает значения между ближайшими известными точками, опираясь на их метки времени. По своей природе она не знает, что четырех- или пятичасовой разрыв следует считать границей. Если правило гласит «не интерполировать через разрывы длиннее 3 часов», сначала нужно обнаружить такие промежутки и интерполировать только внутри непрерывных участков.

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

Разница индекса между соседними строками показывает места больших скачков. Посчитайте разность последовательных меток времени, сравните её с выбранным порогом и накапливайте группы с помощью скользящей суммы. Каждая такая группа — непрерывный сегмент. Ресемплируйте и интерполируйте по сегментам, заполняя лишь короткие пробелы.

Полное решение

Ниже приведён фрагмент, который создаёт сегменты на разрывах строго больше трёх часов и возвращает либо словарь почасовых DataFrame после интерполяции, либо список, который можно позже склеить.

limit = pd.Timedelta(hours=3)
markers = (frame.index.diff() > limit)
parts_map = {f'part{ix}': chunk.resample('1h').first().interpolate(method='time')
             for ix, (grp, chunk) in enumerate(frame.groupby(markers.cumsum()))}

Если удобнее получить список и затем объединить его обратно:

segments = [chunk.resample('1h').first().interpolate(method='time')
            for grp, chunk in frame.groupby(markers.cumsum())]

Это даёт ожидаемые три сегмента для примерных данных — теперь с почасовым шагом и интерполяцией только внутри непрерывных блоков:

{'part0':                         A
 2023-03-18 05:00:00   3.0
 2023-03-18 06:00:00   4.0
 2023-03-18 07:00:00  24.4,
 'part1':                         A
 2023-03-18 12:00:00  5.60
 2023-03-18 13:00:00  3.40
 2023-03-18 14:00:00  3.95
 2023-03-18 15:00:00  4.50,
 'part2':                        A
 2023-03-18 20:00:00  8.8
 2023-03-18 21:00:00  3.2}

Как работает группировка

Идея проста. Разность индекса подсвечивает места, где разрывы превышают порог. Преобразование этих булевых меток в группы через накопленную сумму даёт непрерывные ярлыки, которые можно передать в groupby. Внутри каждой группы временные метки достаточно близки, чтобы после ресемплинга оправданно интерполировать. Между группами интерполяции нет — граница сохраняется за счёт сегментации.

Почему это стоит знать

Приведение временных рядов к регулярной сетке — частый шаг подготовки для моделирования, мониторинга и аналитики. Бездумная интерполяция через длительные простои или провалы сбора может породить вводящие в заблуждение артефакты. Сегментация по размеру разрыва позволяет сохранить удобство resample и interpolate, одновременно соблюдая требования к качеству данных. К тому же упрощается последующая логика: каждый сегмент — это аккуратный, выровненный по часам блок с реалистичными заполнениями.

Практические рекомендации

Находите большие скачки через index.diff и сравнение с порогом Timedelta. Превратите эти скачки в метки непрерывных групп с помощью cumsum. Применяйте resample и интерполяцию по времени только внутри каждой группы. Если затем нужно собрать серию обратно, храните список и конкатенируйте позже; если требуются отдельные артефакты — храните словарь с ключами по индексу сегмента. Такой подход делает интерполяцию честной, а почасовые данные — надёжными.