使用 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_IN
和 DATE_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
我的数据框(下面的 df)有两个日期列,DATE_IN 和 DATE_OUT。我想按月 (df_target) 分发此数据。 df_target 有两列,PERIOD 由 df 中的所有包含日期组成,'n',设置为零。
这个想法很简单。如果一个 ID 的日期范围是 12 个月,比如 2015 年 7 月到 2016 年 7 月之间。那么我想在 df_target.
中每个对应的月份加 1import 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_IN
和 DATE_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