#python #pandas #time-series
#python #pandas #временные ряды
Вопрос:
У меня есть pd.Series
объект с индексом даты и времени. Интервалы не имеют фиксированной продолжительности, но могут быть переменными. Например
ts A
10:13:00.018 100
10:13:00.023 101
10:13:00.059 102
10:13:01.123 103
10:13:01.198 104
10:13:01.520 105
Мне нужно вычислить diff
ряд для интервала, скажем 100ms
. Нынешний метод pandas для diff поддерживает только периоды фиксированного размера.
Некоторые решения, которые я пробовал:
- Можно попробовать
shift
серию, а затем получить разницу. Проблема с этим заключается в том, что метод сдвига pandas сдвигает индекс, что делает невозможным вычитание двух рядов. Например, ashift(freq='100ms')
приведет к:
ts A
10:13:00.118 100
10:13:00.123 101
10:13:00.159 102
10:13:01.223 103
10:13:01.298 104
10:13:01.620 105
- Использование скользящего окна:
df.rolling(window='100ms', min_periods=2).apply(lambda x: x.iloc[-1] - x.iloc[0])
Этот метод работает отлично, но имеет серьезный недостаток, заключающийся в том, что он очень медленный (что и ожидалось, поскольку нам нужны только два крайних значения окна, но он извлекает все окно).
Итак, есть ли более простой и эффективный способ выполнения этой diff
операции для серии, подобной приведенной выше? Я чувствую, что такое решение должно где-то существовать, но я не могу ни угадать, ни найти на форумах.
Ответ №1:
Вы ищете что-то подобное:
import pandas as pd
import datetime
data={'ts':['10:13:00.018','10:13:00.023','10:13:00.059','10:13:01.123','10:13:01.198','10:13:01.520']
,"A":[100,101,102,103,104,105]};
df = pd.DataFrame(data)
df['ts']=pd.to_datetime(df['ts'])
df.set_index('ts',inplace=True)
print(df)
df_Date=pd.date_range(start=df.index.min(), end=(df.index.max() datetime.timedelta(microseconds=100000)), freq='100ms')
df=df.reindex(df_Date,method='ffill',fill_value=None)
df['diff']=df['A'].shift(-1)-df['A']
print(df)
Результат:
A diff
2020-11-06 10:13:00.018 100 2.0
2020-11-06 10:13:00.118 102 0.0
2020-11-06 10:13:00.218 102 0.0
2020-11-06 10:13:00.318 102 0.0
2020-11-06 10:13:00.418 102 0.0
2020-11-06 10:13:00.518 102 0.0
2020-11-06 10:13:00.618 102 0.0
2020-11-06 10:13:00.718 102 0.0
2020-11-06 10:13:00.818 102 0.0
2020-11-06 10:13:00.918 102 0.0
2020-11-06 10:13:01.018 102 0.0
2020-11-06 10:13:01.118 102 2.0
2020-11-06 10:13:01.218 104 0.0
2020-11-06 10:13:01.318 104 0.0
2020-11-06 10:13:01.418 104 0.0
2020-11-06 10:13:01.518 104 1.0
2020-11-06 10:13:01.618 105 NaN
Комментарии:
1. Спасибо за идею. Я верю
resample
, что pandas (то, что вы сделали) должно быть достойным приближением к тому, что мне нужно.2. Обычная разница (после первоначальной повторной выборки) заняла 2 минуты для всех моих данных по сравнению с 15 минутами с текущим окном.. Это даже тогда, когда после повторной выборки у меня было в 10 раз больше данных!
Ответ №2:
Мне пришлось создать пользовательскую функцию, которая выполняла бы скользящую сумму.
from datetime import datetime
from collections import deque
import pandas as pd
def rolling_window_diff(series, time_window_in_millis):
rolling_sum = 0
dq = deque()
res_list = {}
for index, value in series.items():
while len(dq) > 0:
lindex, lval = dq[-1]
tdiff_ms = (index - lindex).total_seconds() * 1000
if tdiff_ms > time_window_in_millis:
rolling_sum -= lval
dq.pop()
else:
break
rolling_sum = value
dq.append((index, value))
res_list.append({'time': index, 'rolling_window_sum': rolling_sum})
return pd.DataFrame(res_list)
Предполагается, что временные метки в серии pd представлены в формате python-datetime и расположены в порядке возрастания (иначе вы можете сначала отсортировать их).
Комментарии:
1. спасибо за фрагмент кода. Я считаю, что это должно сработать, но я искал более стандартную функцию / преобразование из pandas, чтобы работать с общими аргументами, такими как «1s», «10 мс» и т.д.
2. Конечно, это довольно просто внести это изменение
3. Обновлен ответ, чтобы сделать его более общим