I have the following table and I want to count the number of active jobs, per client, on each day in 2020. A job is active if the date falls on or between its start_date and end_date.
| job | client | start_date | end_date |
|---|---|---|---|
| AA001 | ALPHA | 2020/12/19 | 2020/12/28 |
| AA002 | ALPHA | 2020/04/03 | 2020/10/10 |
| AA003 | BRAVO | 2020/10/11 | 2020/10/11 |
| AA004 | CHARLIE | 2020/04/06 | 2020/11/15 |
| AA005 | ALPHA | 2020/04/01 | 2020/04/30 |
| AA006 | CHARLIE | 2020/05/01 | 2020/06/03 |
| AA007 | BRAVO | 2020/06/04 | 2020/06/17 |
| AA008 | BRAVO | 2020/06/18 | 2020/07/01 |
| AA009 | CHARLIE | 2020/07/02 | 2020/08/04 |
| AA010 | ALPHA | 2020/05/05 | 2020/08/06 |
| AA011 | BRAVO | 2020/10/12 | 2020/11/04 |
For instance, here is how many jobs were active for client ALPHA at the beginning of April:
| Date | Client | Active jobs |
|---|---|---|
| ALPHA | 2020-04-01 | 1 |
| ALPHA | 2020-04-02 | 1 |
| ALPHA | 2020-04-03 | 2 |
| ALPHA | 2020-04-04 | 2 |
| ALPHA | 2020-04-05 | 2 |
| ALPHA | 2020-04-06 | 2 |
| ALPHA | 2020-04-07 | 2 |
| ALPHA | 2020-04-08 | 2 |
| ALPHA | 2020-04-09 | 2 |
| ALPHA | 2020-04-10 | 2 |
I can solve this problem using nested loops, e.g.
groups = df.groupby(["client"])
dates = pd.date_range('2020-01-01','2020-12-01', freq='D')
for client, jobs in groups:
for date in dates:
active_jobs = jobs.loc[(jobs.start_date <= date) & (jobs.end_date >= date)]
print(date,client,len(active_jobs))
(Explanation: group rows by client, construct a list of dates, then for each date for each client, find/count the rows where start_date <= date and end_date >= date.)
Of course my real data is much larger than this and looping is very inefficient. How do I rewrite my query to take advantage of vectorization?