在熊猫中获取与给定日期时间最接近的时间戳的行的有效方法

Posted

技术标签:

【中文标题】在熊猫中获取与给定日期时间最接近的时间戳的行的有效方法【英文标题】:Efficient way to get row with closest timestamp to a given datetime in pandas 【发布时间】:2020-04-25 13:16:14 【问题描述】:

我有一个包含大约 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

我发现了一些与获取最接近给定时间的时间戳相关的帖子:Find closest row of DataFrame to given time in 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]

有什么有效的方法来解决这个问题?

【问题讨论】:

【参考方案1】:

嗯,不确定这是否会更有效,但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')

该方法本质上是将前一天的值合并到第二天,然后匹配到最近的时间戳。

【讨论】:

【参考方案2】:

假设您的日期已排序,一种快速完成此操作的方法是使用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

使用这种方法,可以在几十毫秒内计算出包含 100 万行的数据帧:

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

但是请注意,这并没有找到滞后一天的 最近 值,而是一天滞后 后的最接近的值。如果您想要在任一方向上最接近的值,则需要修改此方法。

【讨论】:

我试过了,发现虽然我在 '2019-08-01 14:53:01' 有记录,但在 '2019-08-02 09:42:46' 有'滞后' ' 是 'Nan',它不会显示 '2019-08-01 14:53:01' 的值,因为它最接近 '2019-08-02 09:42:46' 减去一天吗?跨度>

以上是关于在熊猫中获取与给定日期时间最接近的时间戳的行的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

熊猫 - 按时间接近加入

按时间戳列过滤/选择熊猫数据帧的行

仅返回一列中的日期与另一列中的日期最接近的行?

熊猫以时间为索引获取特定日期的行数

创建函数以使当前日期以没有时间戳的字符串格式显示的最有效方法是啥? [复制]

重置熊猫时间戳的时间部分