Эффективный способ получить строку с ближайшей временной меткой к заданной дате в pandas - PullRequest
2 голосов
/ 08 января 2020

У меня есть большой фрейм данных, который содержит около 7 000 000 строк данных временных рядов, которые выглядят так:

timestamp               | values 
2019-08-01 14:53:01     | 20.0
2019-08-01 14:53:55     | 29.0
2019-08-01 14:53:58     | 22.4
...
2019-08-02 14:53:25     | 27.9

Я хочу создать столбец, который представляет собой версию с задержкой в ​​1 день для каждой строки, так как мой временные метки не совпадают идеально, я не могу использовать обычный метод shift(). Результат будет примерно таким:

timestamp               | values | lag
2019-08-01 14:53:01     | 20.0   | Nan
2019-08-01 14:53:55     | 29.0   | Nan
2019-08-01 14:53:58     | 22.4   | Nan
...
2019-08-02 14:53:25     | 27.9   | 20.0

Я нашел несколько сообщений, связанных с получением ближайшей метки времени к заданному времени: Найти ближайшую строку DataFrame к заданному времени в Pandas и опробовал методы, он выполняет свою работу, но занимает слишком много времени, вот что у меня есть:

def get_nearest(data, timestamp):
    index = data.index.get_loc(timestamp,"nearest")
    return data.iloc[index, 0]
df['lag'] = [get_nearest(df, dt) for dt in df.index]

Какие-нибудь эффективные способы решения проблемы?

Ответы [ 2 ]

0 голосов
/ 08 января 2020

Хмммм, не уверен, будет ли это работать эффективнее, но merge_asof - это подход, на который стоит обратить внимание, так как он не требует udf.

df['date'] = df.timestamp.dt.date
df2 = df.copy()
df2['date'] = df2['date'] + pd.to_timedelta(1,unit ='D')
df2['timestamp'] = df2['timestamp'] + pd.to_timedelta(1,unit ='D')
pd.merge_asof(df,df2, on = 'timestamp', by = 'date', direction = 'nearest')

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

0 голосов
/ 08 января 2020

Если ваши даты отсортированы, один из способов сделать это быстро - использовать pd.DateTimeIndex.searchsorted, чтобы найти все совпадающие даты за O[N log N] время.

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

import numpy as np
import pandas as pd
np.random.seed(0)

df = pd.DataFrame(
  {'values': np.random.rand(10)},
  index=sorted(np.random.choice(pd.date_range('2019-08-01', freq='T', periods=10000), 10, replace=False))
)

def add_lag(df):
  ind = df.index.searchsorted(df.index - pd.DateOffset(1))
  out_of_range = (ind <= 0) | (ind >= df.shape[0])
  ind[out_of_range] = 0
  lag = df['values'].values[ind]
  lag[out_of_range] = np.nan
  df['lag'] = lag
  return df

add_lag(df)
                       values       lag
2019-08-01 06:17:00  0.548814       NaN
2019-08-01 10:51:00  0.715189       NaN
2019-08-01 13:56:00  0.602763       NaN
2019-08-02 09:50:00  0.544883  0.715189
2019-08-03 14:06:00  0.423655  0.423655
2019-08-04 03:00:00  0.645894  0.423655
2019-08-05 07:40:00  0.437587  0.437587
2019-08-07 00:41:00  0.891773  0.891773
2019-08-07 07:05:00  0.963663  0.891773
2019-08-07 15:55:00  0.383442  0.891773

При таком подходе кадр данных с 1 миллионом строк может быть вычислен за десятки миллисекунд:

df = pd.DataFrame(
  {'values': np.random.rand(1000000)},
  index=sorted(np.random.choice(pd.date_range('2019-08-01', freq='T', periods=10000000), 1000000, replace=False))
)

%timeit add_lag(df)
# 10 loops, best of 3: 71.5 ms per loop

Обратите внимание, что это не находит ближайшее значение с лагом в один день, но ближайшее значение после лагом в один день. Если вам нужно ближайшее значение в любом направлении, вам нужно изменить этот подход.

...