Python 基于两个日期时间值对数据帧进行分组

Python 基于两个日期时间值对数据帧进行分组,python,pandas,Python,Pandas,我有一个数据集,它显示了一组进程的开始和结束时间戳。我想计算任何给定分钟内活动的进程数。pandas group/merge/join操作似乎都不适合这里。以下是一个测试数据集: _range = pd.date_range(start='2020-03-01', end='2020-03-02', freq='1H') start_ts = [random.choice(_range) for _ in range(48)] end_ts = [r + datetime.timedelta(m

我有一个数据集,它显示了一组进程的开始和结束时间戳。我想计算任何给定分钟内活动的进程数。pandas group/merge/join操作似乎都不适合这里。以下是一个测试数据集:

_range = pd.date_range(start='2020-03-01', end='2020-03-02', freq='1H')
start_ts = [random.choice(_range) for _ in range(48)]
end_ts = [r + datetime.timedelta(minutes=random.randint(1, 120)) for r in start_ts]

_processes = [random.choice(['Django', 'Flask', 'Pyramid', 'CherryPie']) for _ in start_ts]
df = pd.DataFrame({'start_time': start_ts, 'end_time': end_ts, 'process': _processes})
df.head()
           start_time            end_time    process
0 2020-03-01 00:00:00 2020-03-01 00:01:00     Django
1 2020-03-01 01:00:00 2020-03-01 01:04:00     Django
2 2020-03-01 02:00:00 2020-03-01 02:05:00      Flask
3 2020-03-01 03:00:00 2020-03-01 03:09:00      Flask
4 2020-03-01 04:00:00 2020-03-01 04:26:00  CherryPie
我必须计算2020/03/01至2020/03/02之间每1分钟的活动进程数。 以下是我能想到的一个解决方案:

  • 在所述开始/结束日期之间创建1分钟间隔的数据帧
  • 对于每1分钟的间隔,在主数据帧上迭代,如果开始或结束时间在给定的分钟之间,则备份记录
以下是一个示例解决方案:

df_stat = pd.DataFrame(index=pd.date_range(start='2020-03-01', end='2020-03-02', freq='1T'), columns=['count'])
for ts in df_stat.index:
    df_stat.loc[ts] = len(df[(df.start_time <= ts) & (df.end_time >= ts)])
df_stat.head()
                     count
2020-03-01 00:00:00      2
2020-03-01 00:01:00      2
2020-03-01 00:02:00      2
2020-03-01 00:03:00      2
2020-03-01 00:04:00      2
df_stat=pd.DataFrame(index=pd.date_范围(start='2020-03-01',end='2020-03-02',freq='1T'),列=['count']))
对于df_统计索引中的ts:
df_stat.loc[ts]=len(df[(df.start_time=ts)])
df_统计头()
计数
2020-03-01 00:00:00      2
2020-03-01 00:01:00      2
2020-03-01 00:02:00      2
2020-03-01 00:03:00      2
2020-03-01 00:04:00      2

这看起来不是一个优雅的解决方案。当观察窗口较大时,迭代可能需要更长的时间。是否有任何本机pandas运算符可供我们在此处使用?

创建一分钟间隔的数据帧(或序列)确实是合适的。但是,根据输入的不同,迭代原始数据帧并相应增加计数可能会更快(与您的解决方案相比):

df_stat=pd.DataFrame(索引=pd.date_范围(开始时间=2020-03-01',结束时间=2020-03-02',频率=1T'),
列=['count'])
df_stat.fillna(0,原地=真)
对于df.index中的i:
df_stat.loc[df.start_time.loc[i]:df.end_time.loc[i]+=1

创建一分钟间隔的数据帧(或一个系列)确实是合适的。但是,根据输入的不同,迭代原始数据帧并相应增加计数可能会更快(与您的解决方案相比):

df_stat=pd.DataFrame(索引=pd.date_范围(开始时间=2020-03-01',结束时间=2020-03-02',频率=1T'),
列=['count'])
df_stat.fillna(0,原地=真)
对于df.index中的i:
df_stat.loc[df.start_time.loc[i]:df.end_time.loc[i]+=1

再想一想,我想出了一个方法,希望它能足够快地处理您的数据。关键的想法是我们可以将开始时间和结束时间解耦,因为不管哪个开始时间属于哪个结束时间。重要的是在任何给定时刻有多少过程已经开始,有多少过程已经结束。因此,如果我们的代码以1开始,以-1结束,那么运行进程的数量就是这些进程的累积和

#创建一个带有“1”列的数据框,用于启动进程
#和一个'-1'-列用于结束进程
df_times=df.drop('过程',轴=1)
df_times.columns=[1,-1]
#将两列熔化为一列(增量列)
df_long=pd.melt(df_times,var_name='inc',value_name='time')
#按时间分组,如果事件一致,则将增量相加
df_stat=df_long.groupby('time').sum()
#现在,运行进程的计数是增量的累积和
df_stat['count']=df_stat.inc.cumsum()
df_统计下降('inc',轴=1,在位=True)
#向上采样到分钟频率
df_stat=df_stat.resample('1T').ffill()

再想一想,我想出了一个方法,希望它能足够快地处理您的数据。关键的想法是我们可以将开始时间和结束时间解耦,因为不管哪个开始时间属于哪个结束时间。重要的是在任何给定时刻有多少过程已经开始,有多少过程已经结束。因此,如果我们的代码以1开始,以-1结束,那么运行进程的数量就是这些进程的累积和

#创建一个带有“1”列的数据框,用于启动进程
#和一个'-1'-列用于结束进程
df_times=df.drop('过程',轴=1)
df_times.columns=[1,-1]
#将两列熔化为一列(增量列)
df_long=pd.melt(df_times,var_name='inc',value_name='time')
#按时间分组,如果事件一致,则将增量相加
df_stat=df_long.groupby('time').sum()
#现在,运行进程的计数是增量的累积和
df_stat['count']=df_stat.inc.cumsum()
df_统计下降('inc',轴=1,在位=True)
#向上采样到分钟频率
df_stat=df_stat.resample('1T').ffill()

谢谢@Arne。这肯定比我最初的提议有所改进,但仍然不够快,无法遍历50k+记录。谢谢@Arne。这无疑是对我最初的建议的改进,仍然没有足够快的速度来迭代50k+记录。对于一个相当简单的任务来说,这看起来有点复杂,但这是可行的(至少对于我拥有的样本数据集),性能符合我的期望!对于一个相当简单的任务来说,看起来有点复杂,但这确实有效(至少对于我拥有的样本数据集是如此),而且性能符合我的预期!