Python-一百万行表中日期的矢量化差异

Python-一百万行表中日期的矢量化差异,python,pandas,date,numpy,Python,Pandas,Date,Numpy,我有以下数据帧: Date 2018-04-10 21:05:00 2018-04-10 21:05:00 2018-04-10 21:10:00 2018-04-10 21:15:00 2018-04-10 21:35:00 我的目标是计算每次前后20分钟的行数(包括前后时间相同的行)。如下所示: Date nr_20_min_bef nr_2

我有以下数据帧:

Date                    
2018-04-10 21:05:00        
2018-04-10 21:05:00        
2018-04-10 21:10:00        
2018-04-10 21:15:00     
2018-04-10 21:35:00     
我的目标是计算每次前后20分钟的行数(包括前后时间相同的行)。如下所示:

Date                   nr_20_min_bef    nr_20_min_after   
2018-04-10 21:05:00          2                 4                                 
2018-04-10 21:05:00          2                 4  
2018-04-10 21:10:00          3                 2
2018-04-10 21:15:00          4                 2
2018-04-10 21:35:00          2                 1
我曾尝试执行for循环来迭代所有行,问题是整个系列有超过百万行,因此我正在寻找一个更有效的解决方案。我目前的做法是使用以下功能:

import datetime
import pandas

df = pd.DataFrame(pd.to_datetime(['2018-04-10 21:05:00',        
'2018-04-10 21:05:00',        
'2018-04-10 21:10:00',        
'2018-04-10 21:15:00',     
'2018-04-10 21:35:00']),columns = ['Date'])

nr_20_min_bef = []
nr_20_min_after = []

for i in range(0, len(df)):
    nr_20_min_bef.append(df.Date.between(df.Date[i] - 
pd.offsets.DateOffset(minutes=20), df.Date[i], inclusive = True).sum())
    nr_20_min_after.append(df.Date.between(df.Date[i], df.Date[i] + 
pd.offsets.DateOffset(minutes=20), inclusive = True).sum())
对于这种情况,矢量化的解决方案可能是理想的,但是,我真的不知道如何做到这一点


提前感谢。

我认为您可以使用
apply
即使它不是矢量化的方式,也应该比使用
for
循环更快,例如:

#first create the timedelta of 20 minutes
dt_20 = pd.Timedelta(minutes=20)
# then apply on the first column
df['nr_20_min_bef'] = df['Date'].apply(lambda x: df['Date'][((x - dt_20) <= df['Date'] ) 
                                                            & (x >=df['Date'])].count())

df['nr_20_min_after'] = df['Date'].apply(lambda x: df['Date'][(x <= df['Date'] )& 
                                                              ((x + dt_20) >= df['Date'])].count())

我想以后再去

好消息是可以将其矢量化。 坏消息是。。。这并不简单

以下是基准测试代码:


现在,
alt
在做什么?也许使用您发布的
df
查看一个小示例最简单:

df = pd.DataFrame(pd.to_datetime(['2018-04-10 21:05:00',        
                                  '2018-04-10 21:05:00',        
                                  '2018-04-10 21:10:00',        
                                  '2018-04-10 21:15:00',     
                                  '2018-04-10 21:35:00']),columns = ['Date'])
主要思想是使用
Series.rolling
执行滚动求和。当 Series有一个DatetimeIndex,
Series。滚动
可以接受序列的时间频率 窗口大小。所以我们可以用固定的可变窗口计算滚动和 时间跨度。因此,第一步是使日期成为DatetimeIndex:

df['Date'] = pd.to_datetime(df['Date'])
df['num'] = 1
df = df.set_index('Date')
由于
df
具有重复的日期,请按DatetimeIndex值分组并计算重复的数量:

dup_count = df.groupby(level=0)['num'].count()
# Date
# 2018-04-10 21:05:00    2
# 2018-04-10 21:10:00    1
# 2018-04-10 21:15:00    1
# 2018-04-10 21:35:00    1
# Name: num, dtype: int64
现在计算重复计数的滚动和:

result = dup_count.rolling('20T', closed='both').sum()
# Date
# 2018-04-10 21:05:00    2.0
# 2018-04-10 21:10:00    3.0
# 2018-04-10 21:15:00    4.0
# 2018-04-10 21:35:00    2.0
# Name: num, dtype: float64
维奥拉,那是
nr\u 20\u min\u bef
。有20分钟长
closed='both'
指定每个窗口都包括其左端点和右端点

现在,如果只计算
nr\u 20\u min\u之后的
就这么简单的话。理论上,我们所需要做的就是颠倒
dup\u count
中的行的顺序,然后计算另一个滚动和。不幸的是,
Series.rolling
要求DatetimeIndex单调递增:

由于明显的道路被堵塞,我们绕道而行:

max_date = df.index.max()
min_date = df.index.min()
dup_count_reversed = df.groupby((max_date - df.index)[::-1] + min_date)['num'].count()
# Date
# 2018-04-10 21:05:00    1
# 2018-04-10 21:25:00    1
# 2018-04-10 21:30:00    1
# 2018-04-10 21:35:00    2
# Name: num, dtype: int64
这将生成一个新的伪datetime DatetimeIndex,以便按以下方式分组:

In [288]: (max_date - df.index)[::-1] + min_date
Out[288]: 
DatetimeIndex(['2018-04-10 21:05:00', '2018-04-10 21:25:00',
               '2018-04-10 21:30:00', '2018-04-10 21:35:00',
               '2018-04-10 21:35:00'],
              dtype='datetime64[ns]', name='Date', freq=None)
这些值可能不在
df.index
——但这没关系。我们唯一需要的是,值是单调递增的,并且日期时间之间的差异 当反转时,对应于
df.index
中的差异

现在,使用此反向重复计数,我们可以通过采用滚动总和来享受大胜利(在性能方面):

result = dup_count_reversed.rolling('20T', closed='both').sum()
# Date
# 2018-04-10 21:05:00    1.0
# 2018-04-10 21:25:00    2.0
# 2018-04-10 21:30:00    2.0
# 2018-04-10 21:35:00    4.0
# Name: num, dtype: float64
result
具有我们希望的
nr\u 20\u min\u后的值
,但顺序相反, 而且索引错误。以下是我们可以纠正的方法:

result = pd.Series(result.values[::-1], dup_count.index)
# Date
# 2018-04-10 21:05:00    4.0
# 2018-04-10 21:10:00    2.0
# 2018-04-10 21:15:00    2.0
# 2018-04-10 21:35:00    1.0
# dtype: float64

这基本上就是
alt

发布文本的全部内容,而不是图片。并使用for-loop显示您的代码。谢谢您的建议。还添加了代码。感谢您的回答!如果在其他列中验证了特定条件,您是否知道一种只计算日期的解决方案?那么,如果在其他列中验证了某个特定字符串,那么要计算20分钟前后的日期数?因此,我们不仅要有nr_20_min_bef和nr_20_min_after列,还要有nr_20_min_bef_variablex和nr_20_min_after_variablex列(表示variablex在不到30分钟之前出现在行上的次数)。我不确定我是否完全理解这个新问题。但是如果你发布了一个新的问题和所有的细节(一个像你在这里提供的玩具例子非常感谢),我很乐意看一看。就这样做了。非常感谢你的帮助!给你:我假设这个解决方案可能与这里介绍的非常相似。但是,我并没有真正做到这一点。当应用于两个系列的日期时间值时,是否可以调整此解决方案?我的意思是:我们不计算同一系列中20分钟之前的日期数量,而是计算附加系列中20分钟之前和之后的值(而不是我们在本例中所做的当前系列)。一个简单的解决方案是迭代第一个系列的每个日期,并检查第二个系列前后20分钟的日期数。然而,由于它是一个100万行的表,这太耗时了。提前谢谢!
max_date = df.index.max()
min_date = df.index.min()
dup_count_reversed = df.groupby((max_date - df.index)[::-1] + min_date)['num'].count()
# Date
# 2018-04-10 21:05:00    1
# 2018-04-10 21:25:00    1
# 2018-04-10 21:30:00    1
# 2018-04-10 21:35:00    2
# Name: num, dtype: int64
In [288]: (max_date - df.index)[::-1] + min_date
Out[288]: 
DatetimeIndex(['2018-04-10 21:05:00', '2018-04-10 21:25:00',
               '2018-04-10 21:30:00', '2018-04-10 21:35:00',
               '2018-04-10 21:35:00'],
              dtype='datetime64[ns]', name='Date', freq=None)
result = dup_count_reversed.rolling('20T', closed='both').sum()
# Date
# 2018-04-10 21:05:00    1.0
# 2018-04-10 21:25:00    2.0
# 2018-04-10 21:30:00    2.0
# 2018-04-10 21:35:00    4.0
# Name: num, dtype: float64
result = pd.Series(result.values[::-1], dup_count.index)
# Date
# 2018-04-10 21:05:00    4.0
# 2018-04-10 21:10:00    2.0
# 2018-04-10 21:15:00    2.0
# 2018-04-10 21:35:00    1.0
# dtype: float64