在数据帧中有效地获取可变长度的时间片

时间:2017-10-24 05:03:13

标签: python pandas

我想使用DatetimeIndex(类似于resample或groupby操作)有效地对DataFrame进行切片,但所需的时间切片长度不同。

这通过循环(参见下面的代码)相对容易,但是对于大的时间序列,多个切片很快变慢。关于矢量化/提高速度的任何建议?

import pandas as pd, datetime as dt, numpy as np

#Example DataFrame with a DatetimeIndex
idx = pd.DatetimeIndex(start=dt.datetime(2017,1,1), end=dt.datetime(2017,1,31), freq='h')
df = pd.Series(index = idx, data = np.random.rand(len(idx)))

#The slicer dataframe contains a series of start and end windows
slicer_df = pd.DataFrame(index = [1,2])
slicer_df['start_window'] = [dt.datetime(2017,1,2,2), dt.datetime(2017,1,6,12)]
slicer_df['end_window'] = [dt.datetime(2017,1,6,12), dt.datetime(2017,1,15,2)]

#The results should be stored to a dataframe, indexed by the index of the slicer dataframe
#This is the loop that I would like to vectorise
slice_results = pd.DataFrame()
slice_results['total'] = None
for index, row in slicer_df.iterrows():
    slice_results.loc[index,'total'] = df[(df.index >= row.start_window) &
                                          (df.index <= row.end_window)].sum()

NB。我刚刚意识到我的特定数据集具有相邻的窗口(即,一个窗口的开始对应于它之前的窗口的结尾),但窗口的长度不同。感觉应该有一种方法来执行groupby或类似的只有一次通过df ...

3 个答案:

答案 0 :(得分:2)

你可以将它作为一个应用来实现,它会结果而不是迭代地更新DataFrame:

In [11]: slicer_df.apply((lambda row: \
              df[(df.index >= row.start_window) 
               & (df.index <= row.end_window)].sum()), axis=1)
Out[11]:
1     36.381155
2    111.521803
dtype: float64

答案 1 :(得分:0)

我想出了一个矢量化方法,它依赖于变化长度的“窗口”,它们总是彼此相邻,即。窗口的开头与之前窗口的结尾相同。

        col1      col2      
Day0     'A'        NaN      
Day1     'B'        'C'       
Day2     'C'        'A'

当然这仅适用于窗户相邻的情况,即。它们不能重叠或有任何差距。如果有人有更通用的方法适用于上述方法,我很乐意看到它!

答案 2 :(得分:0)

您可以使用searchsorted对其进行矢量化(假设日期时间索引已排序,否则首先排序):

In [11]: inds = np.searchsorted(df.index.values, slicer_df.values)

In [12]: s = df.cumsum()  # only sum once!

In [13]: pd.Series([s[end] - s[start-1] if start else s[end] for start, end in inds], slicer_df.index)
Out[13]:
1     36.381155
2    111.521803
dtype: float64

那里还有一个循环,但它现在便宜很多了!

这使我们得到一个完全矢量化的解决方案(它有点神秘):

In [21]: inds2 = np.maximum(1, inds)  # see note

In [22]: inds2[:, 0] -= 1

In [23]: inds2
Out[23]:
array([[ 23,  96],
       [119, 336]])

In [24]: x = s[inds2]

In [25]: x
Out[25]:
array([[  11.4596498 ,   47.84080472],
       [  55.94941276,  167.47121538]])

In [26]: x[:, 1] - x[:, 0]
Out[26]: array([  36.38115493,  111.52180263])

注意:当开始日期在第一个日期之前,我们要避免开始索引从0回滚到-1(这意味着数组结束即下溢)。 < / p>