如何根据多个条件在Pandas中对时间序列数据帧进行切片?

时间:2014-09-15 18:58:13

标签: python pandas time-series dataframe subset

我需要根据以下两个条件拍摄时间序列数据框:

  1. 每个切片的开始日期在第二个数据框索引中找到。
  2. 每个切片的开始时间和切片的长度是您的功能的参数。
  3. 让我们看一个例子

    df1 - 这是我们从

    获取切片的地方
                         A  B      
    DateTime                                               
    2011-01-02 00:00:00  1  2  
    2011-01-02 04:00:00  2  4    
    2011-01-02 08:00:00  3  5      
    2011-01-02 12:00:00  2  6   
    2011-01-02 16:00:00  5  6
    2011-01-02 20:00:00  2  1
    2011-01-03 00:00:00  5  2 
    2011-01-03 04:00:00  3  3
    2011-01-03 08:00:00  2  2
    2011-01-03 12:00:00  0  4
    2011-01-03 16:00:00  5  4
    2011-01-03 20:00:00  1  1
    
    <class 'pandas.tseries.index.DatetimeIndex'>
    [2011-01-02 00:00:00, ..., 2011-01-03 20:00:00]
    Length: 12, Freq: 240T, Timezone: None
    

    df2 - 这是切片开始的日期部分所在。

                         N  
    DateTime                                                                  
    2011-01-10 00:00:00  1  
    2011-03-10 00:00:00  2
    
    <class 'pandas.tseries.index.DatetimeIndex'>
    [2011-01-02, ..., 2011-01-03]
    Length: 2, Freq: None, Timezone: None
    

    假设我们想要df1['A']的间隔length=4'04:00:00'的每个时间间隔的开始时间df2 ....所需的输出示例如下:

    func(df1['A'], df2, lenght=4, start_time='04:00')
    
                         A    
    DateTime                                                 
    2011-01-02 04:00:00  2   
    2011-01-02 08:00:00  3    
    2011-01-02 12:00:00  2   
    2011-01-02 16:00:00  5     
    2011-01-03 04:00:00  3   
    2011-01-03 08:00:00  2   
    2011-01-03 12:00:00  0   
    2011-01-03 16:00:00  5   
    

    需要考虑的事项:

    • df1的频率不需要始终是&#39; 240T&#39;
    • df2中的日期不需要连续,我只是为了简单的例子而设置它。
    • 并非df1上的所有日期都在df2上,但df2的所有日期都在df1中
    • df2中的列N可以忽略
    • df2 freq属性始终是&#39;无&#39;
    • 切片的长度可以是任何东西,因此它们可以是多天。

    我尝试了什么:

    在这里有一些帮助我尝试了这种方法,但只有当df的频率都是“无”时才能正常工作。

    def next_n_asof(x, t, n):
        i = np.argmax(df1.index >= t)
        return x[i:i + n]
    
    pd.concat(next_n_asof(df1.A, t, 4)
                   for t in df2.index)
    

    提前致谢

1 个答案:

答案 0 :(得分:2)

next_n_asof中的一个非常小的变化会产生预期的结果。如果不是

i = np.argmax(df1.index >= t)

你使用

i = np.argmax(df1.index > t)

然后您的代码生成

2011-01-02 04:00:00    2
2011-01-02 08:00:00    3
2011-01-02 12:00:00    2
2011-01-02 16:00:00    5
2011-01-03 04:00:00    3
2011-01-03 08:00:00    2
2011-01-03 12:00:00    0
2011-01-03 16:00:00    5
Name: A, dtype: int64

也许我误解了这个问题,因为这似乎太容易了。


尽管如此,这里有一个可能更快的替代方案: 请注意,此代码使用带有for-loop次迭代的len(df2.index)

pd.concat(next_n_asof(df1.A, t, 4) for t in df2.index)

您可以改为使用

start = df1.index.get_indexer_for(df2.index)

查找df2.index中的时间戳等于df1.index中的时间戳的索引。例如,

In [93]: df1.index.get_indexer_for(df2.index)
Out[93]: array([0, 6])

使用DatetimeIndex的get_indexer_for方法比使用此列表推导更快:

In [101]: [np.argmax(df1.index >= t) for t in df2.index]
Out[101]: [0, 6]

In [103]: %timeit [np.argmax(df1.index >= t) for t in df2.index]
10000 loops, best of 3: 85.5 µs per loop

In [104]: %timeit df1.index.get_indexer_for(df2.index)
100000 loops, best of 3: 14.5 µs per loop

从那里开始为你希望选择的df1中的行创建所有所需索引的布尔掩码并不难:

mask = np.zeros(len(df), dtype='bool')
for i in range(length):
    mask[start+i] = True

然后,您可以使用

df1中选择所需的行
df1.loc[mask]

而不是创建(可能)许多较小的DataFrame然后 将它们与pd.concat连接起来,如果有很多子数据框,则速度较慢。

因此,此替代方法使用for-loop次迭代交换len(df2.index) 对于for-loop进行n=4次迭代(在您提出的示例问题中)。如果是df2n很小,这种替代方法应该更快。


import numpy as np
import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3, 2, 5, 2, 5, 3, 2, 0, 5, 1],
                    'B': [2, 4, 5, 6, 6, 1, 2, 3, 2, 4, 4, 1]},
                   index=pd.date_range('2011-1-2', '2011-01-03 20:00', freq='240T'))

df2 = pd.DataFrame({'N': 1}, index=pd.date_range('2011-1-2', '2011-01-03'))

def next_n_asof(x, t, n):
    i = np.argmax(df1.index > t)
    return x[i:i + n]

print(pd.concat(next_n_asof(df1.A, t, 4)
               for t in df2.index))


def func(df, index, length):
    start = df.index.get_indexer_for(index)
    mask = np.zeros(len(df), dtype='bool')
    for i in range(length):
        mask[start+i] = True
    return df.loc[mask]

index = df2.index + pd.DateOffset(hour=4)
print(func(df1['A'], index, length=4))

产量

2011-01-02 04:00:00    2
2011-01-02 08:00:00    3
2011-01-02 12:00:00    2
2011-01-02 16:00:00    5
2011-01-03 04:00:00    3
2011-01-03 08:00:00    2
2011-01-03 12:00:00    0
2011-01-03 16:00:00    5
Name: A, dtype: int64