Skip to main content
 首页 » 编程设计

python之Pandas 日期间隔的滚动窗口计数

2025年05月04日21lidabo

我有项目的历史记录及其相关的计划开始和结束时间:

id   planned_start planned_end 
1    2017-09-12    2017-09-13 
2    2017-09-12    2017-09-14 
3    2017-09-12    2017-09-13 
4    2017-09-13    2017-09-13 
5    2017-09-12    2017-09-12 
6    2017-09-12    2017-09-20 
7    2017-09-14    2017-09-15 
8    2017-09-14    2017-09-20 

我想统计上述项目每个开始日期的并发项目数。这是我的逻辑:

for project_id in df['id']: 
    start_date = df[df['id'] == project_id]['planned_start'].values[0] 
    concurrent_projects = df[(df['planned_start'] <= start_date) & (df['planned_end'] >= start_date)] 
    df.ix[df['id'] == project_id, 'concurrent_projects'] = concurrent_projects.shape[0] 

产生这个:

   id planned_start planned_end  concurrent_projects 
0   1    2017-09-12  2017-09-13                  5.0 
1   2    2017-09-12  2017-09-14                  5.0 
2   3    2017-09-12  2017-09-13                  5.0 
3   4    2017-09-13  2017-09-13                  5.0 
4   5    2017-09-12  2017-09-12                  5.0 
5   6    2017-09-12  2017-09-20                  5.0 
6   7    2017-09-14  2017-09-15                  4.0 
7   8    2017-09-14  2017-09-20                  4.0 

但是,我知道上面的 for 循环在时间方面是多么次优。实际上,我有超过 500,000 个项目需要计算。有人可以就如何加快速度提供一些建议吗?我知道必须有一个 pure-pandas 甚至 numpy 的解决方案来杀死我上面的内容。

请您参考如下方法:

矢量化方式...但会耗尽内存。仍在研究更好的矢量化方式。我有概念,只是在吃晚饭时处理细节。

s = df.planned_start.values 
e = df.planned_end.values 
 
s_ = s >= s[:, None] 
e_ = s <= e[:, None] 
 
df.assign(concurrent_projects=(e_ & s_).sum(0)) 
 
   id planned_start planned_end  concurrent_projects 
0   1    2017-09-12  2017-09-13                    5 
1   2    2017-09-12  2017-09-14                    5 
2   3    2017-09-12  2017-09-13                    5 
3   4    2017-09-13  2017-09-13                    5 
4   5    2017-09-12  2017-09-12                    5 
5   6    2017-09-12  2017-09-20                    5 
6   7    2017-09-14  2017-09-15                    4 
7   8    2017-09-14  2017-09-20                    4 

抱歉,我没有时间解释。但我不想让你悬着

k = len(df) 
 
d = np.column_stack([df.planned_start.values, df.planned_end.values + 1]).ravel() 
 
i = np.tile([1, -1], k) 
a = d.argsort() 
 
f = np.arange(k).repeat(2) 
r = np.zeros(k, int) 
z = np.zeros(k, int) 
m = np.zeros(k, int) 
 
cumsum = 0 
 
for j in range(f.size): 
    x = f[a[j]] 
    y = i[a[j]] 
    r[x] = cumsum 
    z[x] = (y + 1) // 2 
    r += y * z 
    m = np.column_stack([m, r]).max(1) 
    cumsum += y 
 
m 
 
array([5, 5, 5, 5, 5, 5, 4, 4])