使用 date_range 按月分发数据 - 改进解决方案

Distribute out data on month using date_range - improve solution

我的数据框(下面的 df)有两个日期列,DATE_IN 和 DATE_OUT。我想按月 (df_target) 分发此数据。 df_target 有两列,PERIOD 由 df 中的所有包含日期​​组成,'n',设置为零。

这个想法很简单。如果一个 ID 的日期范围是 12 个月,比如 2015 年 7 月到 2016 年 7 月之间。那么我想在 df_target.

中每个对应的月份加 1
import pandas as pd
from datetime import datetime
from datetime import timedelta
import numpy as np

df=pd.DataFrame({'ID': range(0,10),
              'DATE_IN':['2015-05-23', '2016-08-13', '2014-06-13', '2014-03-19', '2013-01-13',
                        '2014-03-13', '2014-04-27', '2014-02-13', '2015-03-03', '2016-03-13'],
              'DATE_OUT':['2015-08-12', '2017-09-30', '2017-05-17', '2015-12-24', '2015-02-15',
                        '2017-03-19', '2016-02-20', '2015-01-10', '2015-09-21', '2016-04-23'],
              'CODE':[10,10,10,10,10,10,10,10,10,10]
                          })

df['DATE_IN'] = pd.to_datetime(df.DATE_IN)
df['DATE_OUT'] = pd.to_datetime(df.DATE_OUT)

target=pd.DataFrame({'PERIOD':pd.date_range(df.DATE_IN.min(), df.DATE_OUT.max(), freq='M'),
             'n':int(0)})

我下面的尝试是一个for循环。在循环中,我创建了一个包含一列的数据框,一个日期范围。然后我使用那个月在相应的月份访问 df_target 并添加 1。这有效。但在这种情况下,性能对我来说很重要。我想在具有 8-9 百万行的数据集上执行此操作或此操作的改进版本。那么也许for循环会很慢?

关于如何改进这个解决方案有什么想法吗?


for z in range(0, len(df)):
    
    tmp=pd.DataFrame({'PERIOD':pd.date_range(df.DATE_IN[z], df.DATE_OUT[z], freq='M')})
    
    for i in range(0, len(tmp)):
        
        info=target[target['PERIOD']==tmp.PERIOD[i]].n + 1
        target.loc[info.index[0], 'n'] = (target.loc[info.index[0], 'n'] + 1)

您可以使用 pd.date_range 展开 DATE_INDATE_OUT 列之间的日期,然后展开它。最后,对每个月使用计数唯一值。

target = (
    df.apply(lambda x: pd.date_range(x['DATE_IN'], x['DATE_OUT'], freq='M'), axis=1)
      .explode().value_counts().sort_index().rename('n').rename_axis('PERIOD').reset_index()
)
print(target)

# Output
       PERIOD  n
0  2013-01-31  1
1  2013-02-28  1
2  2013-03-31  1
3  2013-04-30  1
4  2013-05-31  1
5  2013-06-30  1
...
52 2017-05-31  1
53 2017-06-30  1
54 2017-07-31  1
55 2017-08-31  1
56 2017-09-30  1