Найти разницу в ряду pandas с индексом даты и времени, указывающим период в freq

#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 поддерживает только периоды фиксированного размера.

Некоторые решения, которые я пробовал:

  1. Можно попробовать shift серию, а затем получить разницу. Проблема с этим заключается в том, что метод сдвига pandas сдвигает индекс, что делает невозможным вычитание двух рядов. Например, a shift(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
  
  1. Использование скользящего окна:
 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. Обновлен ответ, чтобы сделать его более общим