В моем кадре данных ниже я пытаюсь создать производные показатели из 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
Объяснить,
Надеюсь, это объясняет вещи яснее
Я попробовал использовать векторизованную операцию Pandas, которую я бы предпочел, с чем-то вроде приведенного ниже, но она возвращает от -3 до -1.
rolling_max = df['execution'].rolling('3s').max().shift(periods=1, freq='1s')
Код
Найдите максимальное значение с интервалом 200 мс (окно с центром = False). Это максимальное значение за предыдущий интервал 200 мс, так как оно находится справа)
Найдите максимальное значение с интервалом 200 мс (окно с центром = True). Это максимальное значение за предыдущие 100 мс до следующих 100 мс, поскольку оно находится в центре)
Найдите максимальное из этих значений как 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
).
Я добавил более подробную информацию в сообщение, упростил примеры и объяснил причину ожидаемого результата в каждой строке, надеюсь, это объясняет яснее.
@IGottaLearnMath Похоже, исправлена совсем другая проблема. В этом случае лучше создать новый вопрос, а не вносить большие изменения, которые сделают бесполезным существующий ответ. Я все равно обновил ответ.
Хорошо, я могу создать новый вопрос. Первоначальный вопрос был аналогичной проблемой. Я упростил его, просто используя секунды, чтобы сделать его более читабельным и обеспечить ожидаемый результат, которому можно было бы следовать логически.
@IGottaLearnMath Я думаю, что вопрос в порядке, однако вы могли бы его уточнить. IIUC, вы хотите иметь возможность использовать гибкое окно, которое начинается в определенное время до вашего текущего времени и заканчивается в определенное время после (с возможностью, чтобы это время было отрицательным).
@PandaKim, вы можете обновить свой ответ, чтобы сделать его общим (независимо от переданных значений времени начала и окончания)
Я изменил вопрос, чтобы включить более точное описание
Итак, как можно масштабировать здесь временное окно, скажем, мы хотим просмотреть диапазон от 5 до 10 секунд или от 500 мс до 1 секунды?
@mozway Я обновил ответ в ответ на ваш запрос. Кроме того, я думаю, что лучше задать новый вопрос, когда есть серьезная редакция, но даже если ее нет, лучше оставить старый вопрос и обновить новый вопрос.
Подход 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, вы можете установить подпись 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 какую версию панд вы использовали? Это отлично работает на пандах 2.2.2.
1.3.5 — невозможно обновить, так как это блокнот, размещенный на внешнем сервере aws. Спасибо, но уверен, что ваш ответ правильный. Я найду способ обойти это. Отметить решение как правильное.
@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)
.
@IGottaLearnMath Я также добавил подход, который должен быть независимым от версии, хотя я не тестировал его на пандах 1.3.5.
Отлично, спасибо большое
Вы тестировали этот подход? Вы можете определить значения до и после по своему усмотрению, см. обновление.