Панды находят максимум за предыдущий интервал времени

В моем кадре данных ниже я пытаюсь создать производные показатели из execution_size в скользящем временном интервале.

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

Так, например, если start of the time interval is -1s and the end is -2s, я пытаюсь получить для каждой строки максимальное значение execution_size в окне от 2 с до временной метки строки до 1 с до временной метки строки.

              timestamp     execution_size   expected_output
1    2023-10-02 07:31:42     150              NaN
2    2023-10-02 07:31:42.5   180              NaN
3    2023-10-02 07:31:43     425              150
4    2023-10-02 07:31:43.5   11               180
5    2023-10-02 07:31:44     NaN              425
6    2023-10-02 07:31:45     122              425
7    2023-10-02 07:31:45.5   NaN              11

Объяснить,

  • В строках 1 и 2 нет ничего в окне от -1 до -2 секунд перед этим.
  • В строке 3 имеется строка 1 (поскольку она находится в пределах окна от -1 до -2 с). Строка 2, хотя размер выполнения больше, чем строка 1, не помещается в это окно, поэтому не учитывается.
  • Строка 4 содержит строки 1 и 2 в окне, поэтому выбирается большее значение строки 2.
  • В строке 5 в окне есть строки 1,2,3, в качестве максимальной выбрана строка 3.
  • В строке 6 есть строки 3, 4, 5 в окне, выбирается строка 3.
  • В окне строка 7 есть строки 4 и 5, выбирается строка 4.

Надеюсь, это объясняет вещи яснее

Я попробовал использовать векторизованную операцию Pandas, которую я бы предпочел, с чем-то вроде приведенного ниже, но она возвращает от -3 до -1.

rolling_max = df['execution'].rolling('3s').max().shift(periods=1, freq='1s')

Вы тестировали этот подход? Вы можете определить значения до и после по своему усмотрению, см. обновление.

mozway 12.07.2024 10:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
79
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Код

  1. Найдите максимальное значение с интервалом 200 мс (окно с центром = False). Это максимальное значение за предыдущий интервал 200 мс, так как оно находится справа)

  2. Найдите максимальное значение с интервалом 200 мс (окно с центром = True). Это максимальное значение за предыдущие 100 мс до следующих 100 мс, поскольку оно находится в центре)

  3. Найдите максимальное из этих значений как np.fmax.

Это максимальное значение от последних 200 мс до следующих 100 мс.

import numpy as np    
out = np.fmax(
    df['execution_size'].rolling('200ms').max(),
    df['execution_size'].rolling('200ms', center=True).max()
)

вне

2023-10-02 07:31:42.694293342    425.0
2023-10-02 07:31:42.694382842    425.0
2023-10-02 07:31:42.694396562    425.0
2023-10-02 07:31:42.915111402      NaN
2023-10-02 07:31:43.030137780    122.0
2023-10-02 07:31:43.100974920    122.0
2023-10-02 07:31:43.101437340    122.0
2023-10-02 07:31:43.790334200      NaN
2023-10-02 07:31:44.754131658     45.0
2023-10-02 07:31:44.754304038     45.0
2023-10-02 07:31:44.754389398     45.0
2023-10-02 07:31:44.754457458     45.0
2023-10-02 07:31:44.754745838     45.0
2023-10-02 07:31:44.754758978     45.0
Name: execution_size, dtype: float64

Пример кода предыдущего вопроса

import pandas as pd
idx = pd.to_datetime(['2023-10-02 07:31:42.694293342', '2023-10-02 07:31:42.694382842', '2023-10-02 07:31:42.694396562','2023-10-02 07:31:42.915111402', '2023-10-02 07:31:43.030137780', '2023-10-02 07:31:43.100974920','2023-10-02 07:31:43.101437340', '2023-10-02 07:31:43.790334200', '2023-10-02 07:31:44.754131658','2023-10-02 07:31:44.754304038', '2023-10-02 07:31:44.754389398', '2023-10-02 07:31:44.754457458','2023-10-02 07:31:44.754745838', '2023-10-02 07:31:44.754758978'])
data = {'execution_size': [None, None, 425, None, 11, None, 122, None, None, 42, None, 45, None, None]}
df = pd.DataFrame(data, index=idx)

Обновить ответ на дополнительный вопрос

shift, gap = '1s', '1s' # shift is -1s(last rolling end point), gap is -2s to -1s

out = pd.concat(
    [df, df.shift(freq=shift).rename({'execution_size': 'val'}, axis=1)], axis=1
).rolling(gap, closed='both')['val'].max()[df.index]

вне

timestamp
2023-10-02 07:31:42.000      NaN
2023-10-02 07:31:42.500      NaN
2023-10-02 07:31:43.000    150.0
2023-10-02 07:31:43.500    180.0
2023-10-02 07:31:44.000    425.0
2023-10-02 07:31:45.000    425.0
2023-10-02 07:31:45.500     11.0
Name: val, dtype: float64

Пример кода нового вопроса

import pandas as pd
idx = pd.to_datetime(['2023-10-02 07:31:42', '2023-10-02 07:31:42.5', '2023-10-02 07:31:43', '2023-10-02 07:31:43.5', '2023-10-02 07:31:44', '2023-10-02 07:31:45','2023-10-02 07:31:45.5'], format='mixed')
df = pd.DataFrame({'execution_size': [150, 180, 425, 11, None, 122, None]}, index=idx)

Это хороший хак, обратите внимание, что это стало возможным благодаря природе функции max, которую невозможно напрямую реализовать для более сложных агрегатов (таких как mean или std).

mozway 12.07.2024 10:14

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

IGottaLearnMath 12.07.2024 10:37

@IGottaLearnMath Похоже, исправлена ​​совсем другая проблема. В этом случае лучше создать новый вопрос, а не вносить большие изменения, которые сделают бесполезным существующий ответ. Я все равно обновил ответ.

Panda Kim 12.07.2024 11:05

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

IGottaLearnMath 12.07.2024 11:17

@IGottaLearnMath Я думаю, что вопрос в порядке, однако вы могли бы его уточнить. IIUC, вы хотите иметь возможность использовать гибкое окно, которое начинается в определенное время до вашего текущего времени и заканчивается в определенное время после (с возможностью, чтобы это время было отрицательным).

mozway 12.07.2024 11:22

@PandaKim, вы можете обновить свой ответ, чтобы сделать его общим (независимо от переданных значений времени начала и окончания)

mozway 12.07.2024 11:25

Я изменил вопрос, чтобы включить более точное описание

IGottaLearnMath 12.07.2024 11:30

Итак, как можно масштабировать здесь временное окно, скажем, мы хотим просмотреть диапазон от 5 до 10 секунд или от 500 мс до 1 секунды?

IGottaLearnMath 12.07.2024 11:50

@mozway Я обновил ответ в ответ на ваш запрос. Кроме того, я думаю, что лучше задать новый вопрос, когда есть серьезная редакция, но даже если ее нет, лучше оставить старый вопрос и обновить новый вопрос.

Panda Kim 12.07.2024 11:56
Ответ принят как подходящий

Подход pandas заключался бы в создании специального прокручивающегося окна Индексатора (здесь PrePostWindow), который будет принимать ваши параметры -200 мс/+100 мс:

df['max_execution_in_window'] = (df['execution_size']
                                 .rolling(PrePostWindow(df['timestamp'],
                                          pre='-200ms', post='100ms'))
                                 .max()
                                )

Полный код с кодом пользовательского окна:

from pandas.api.indexers import BaseIndexer
import numpy as np

class PrePostWindow(BaseIndexer):
    def __init__(self, index_array, pre='0', post='0'):
        super().__init__()
        self.index_array = np.asarray(index_array)
        self.pre = pd.Timedelta(pre)
        self.post = pd.Timedelta(post)

    def get_window_bounds(self,
                          num_values=0,     # not implemented
                          min_periods=None, # not implemented
                          center=None,      # not implemented
                          closed=None,      # not implemented
                          step=None,        # not implemented
                         ):
        start = np.searchsorted(self.index_array,
                                self.index_array + pd.Timedelta(self.pre))
        end = np.searchsorted(self.index_array,
                              self.index_array + pd.Timedelta(self.post),
                              side='right'
                             )
        return start, end

df['max_execution_in_window'] = df['execution_size'].rolling(PrePostWindow(df['timestamp'], pre='-200ms', post='100ms')).max()

Выход:

                       timestamp  execution_size  max_execution_in_window
0  2023-10-02 07:31:42.694293342             NaN                    425.0
1  2023-10-02 07:31:42.694382842             NaN                    425.0
2  2023-10-02 07:31:42.694396562           425.0                    425.0
3  2023-10-02 07:31:42.915111402             NaN                      NaN
4  2023-10-02 07:31:43.030137780            11.0                    122.0
5  2023-10-02 07:31:43.100974920             NaN                    122.0
6  2023-10-02 07:31:43.101437340           122.0                    122.0
7  2023-10-02 07:31:43.790334200             NaN                      NaN
8  2023-10-02 07:31:44.754131658             NaN                     45.0
9  2023-10-02 07:31:44.754304038            42.0                     45.0
10 2023-10-02 07:31:44.754389398             NaN                     45.0
11 2023-10-02 07:31:44.754457458            45.0                     45.0
12 2023-10-02 07:31:44.754745838             NaN                     45.0
13 2023-10-02 07:31:44.754758978             NaN                     45.0

обновленный пример

df['max_execution_in_window'] = (df['execution_size']
                                 .rolling(PrePostWindow(df['timestamp'],
                                          pre='-2s', post='-1s'))
                                 .max()
                                )

                timestamp  execution_size  expected_output  max_execution_in_window
0 2023-10-02 07:31:42.000           150.0              NaN                      NaN
1 2023-10-02 07:31:42.500           180.0              NaN                      NaN
2 2023-10-02 07:31:43.000           425.0            150.0                    150.0
3 2023-10-02 07:31:43.500            11.0            180.0                    180.0
4 2023-10-02 07:31:44.000             NaN            425.0                    425.0
5 2023-10-02 07:31:45.000           122.0            425.0                    425.0
6 2023-10-02 07:31:45.500             NaN             11.0                     11.0

независимая подпись версии pandas

Чтобы обобщить описанный выше подход к предыдущей версии pandas, вы можете установить подпись get_window_bounds программно (эти параметры здесь не используются, но, тем не менее, являются обязательными):

from pandas.api.indexers import BaseIndexer
import inspect
import numpy as np

class PrePostWindow(BaseIndexer):
    def __init__(self, index_array, pre='0', post='0'):
        super().__init__()
        self.index_array = np.asarray(index_array)
        self.pre = pd.Timedelta(pre)
        self.post = pd.Timedelta(post)

    def get_window_bounds(self, **kwargs):
        start = np.searchsorted(self.index_array,
                                self.index_array + pd.Timedelta(self.pre))
        end = np.searchsorted(self.index_array,
                              self.index_array + pd.Timedelta(self.post),
                              side='right'
                             )
        return start, end
    
    get_window_bounds.__signature__ = inspect.signature(BaseIndexer.get_window_bounds)

Кажется, это не с моей стороны. ValueError: PrePostWindow не реализует правильную подпись для get_window_bounds

IGottaLearnMath 12.07.2024 11:44

@IGottaLearnMath какую версию панд вы использовали? Это отлично работает на пандах 2.2.2.

mozway 12.07.2024 11:45

1.3.5 — невозможно обновить, так как это блокнот, размещенный на внешнем сервере aws. Спасибо, но уверен, что ваш ответ правильный. Я найду способ обойти это. Отметить решение как правильное.

IGottaLearnMath 12.07.2024 11:49

@IGottaLearnMath точная подпись get_window_bounds не важна для вычислений для этого пользовательского окна (мы не используем параметры). Вы можете попробовать адаптировать его к подписи, которая использовалась в pandas 1.3.5 (см. здесь). Так и должно быть get_window_bounds(self, num_values=0, window_size=0, min_periods=None, center=None, closed=None, win_type=None).

mozway 12.07.2024 11:52

@IGottaLearnMath Я также добавил подход, который должен быть независимым от версии, хотя я не тестировал его на пандах 1.3.5.

mozway 12.07.2024 12:02

Отлично, спасибо большое

IGottaLearnMath 12.07.2024 12:14

Другие вопросы по теме