我想计算连续期间的天数。
在下面的 df 中,我有四列:
- id;代表一个人。
- 期间;一个数字,其中最低的是第一个周期,最高的是最后一个周期。
- in_date;经期开始的日期。
- 过时;期间结束的日期。
我想构建一个执行以下操作的通用函数:
计算连续周期的天数。如果两个周期之间的天数小于 90,则认为它们是连续的。
如果某个 ID 的最后一个经期的 out_date 是 2013 年,我只想计算天数。如果最后一个经期的“out_date”是 2014 年或 2012 年,我想忽略该 ID。
我想在结果变量中包含周期之间的天数。
我的问题是,由于我对 Python 相当陌生,我无法想出一个好主意如何计算周期之间的天数并对连续周期进行分类。任何帮助将不胜感激。
import pandas as pd
import numpy as np
import datetime
data = {'id':[1, 1, 1, 2, 2, 2, 2, 3, 3, 3],
'period':[1, 2, 3, 1, 3, 5, 6, 2, 3, 4],
'in_date': ['2011-02-15','2011-11-10','2012-10-13',
'2010-04-03','2012-02-17','2012-08-15','2014-01-04','2010-06-01','2012-03-29','2012-09-12'],
'out_date': ['2011-05-21','2012-10-11','2013-10-25',
'2012-02-16','2012-02-19','2013-11-23','2014-12-18','2011-08-21','2012-09-11','2013-01-10']}
df = pd.DataFrame(data)
df['in_date'] = pd.to_datetime(df['in_date'])
df['out_date'] = pd.to_datetime(df['out_date'])
df['n_days'] = df['out_date'] - df['in_date']
预期输出:
最佳答案
首先,将n_days
转换为数值并确保df已排序:
df['n_days'] = (df['out_date'] - df['in_date']).dt.days
df = df.sort_values(['id','period'])
添加一列计算周期之间的天数:
df['days_since_last'] = (df['in_date'] - df['out_date'].shift(1)).dt.days
...并确保这些值不会在不同的 id
值之间交叉:
id_changed = (df['id'].shift(1) != df['id'])
df.loc[id_changed, 'days_since_last'] = np.nan
定义一个条件来记录间隔天数太长的情况:
days_cut = (df['days_since_last'] >= 90)
获取数据帧的子集,其中它是新的id
或有效的连续运行天数。为每个有效运行分配一个唯一的“run”值(稍后用于分组):
tmp = df[days_cut | id_changed ].copy()
tmp['run'] = range(len(tmp))
将其合并回主数据帧并向前填充run
,以便显示连续周期的有效运行位置:
df = pd.merge(df, tmp[['id','period','run']], on=['id','period'], how='left')
df['run'] = df['run'].fillna(method='ffill')
这就是当时的样子。您可以看到,对于每个 id
,都有连续的 run
值:
print(df)
id period in_date out_date n_days days_since_last run
0 1 1 2011-02-15 2011-05-21 95 NaN 0.0
1 1 2 2011-11-10 2012-10-11 336 173.0 1.0
2 1 3 2012-10-13 2013-10-25 377 2.0 1.0
3 2 1 2010-04-03 2012-02-16 684 NaN 2.0
4 2 3 2012-02-17 2012-02-19 2 1.0 2.0
5 2 5 2012-08-15 2013-11-23 465 178.0 3.0
6 2 6 2014-01-04 2014-12-18 348 42.0 3.0
7 3 2 2010-06-01 2011-08-21 446 NaN 4.0
8 3 3 2012-03-29 2012-09-11 166 221.0 5.0
9 3 4 2012-09-12 2013-01-10 120 1.0 5.0
通过对 n_days
列求和来提取每次运行
的连续天数。 .agg
还跟踪运行中的最大日期,因此我们只能保留 2013 年结束的运行:
consecutive_days = df.groupby(['id','run']).agg( {'n_days' : np.sum, 'out_date' : np.max } )
consecutive_days = consecutive_days[(consecutive_days['out_date'].dt.year == 2013)]
consecutive_days = consecutive_days.drop(columns=['out_date']).rename(columns={'n_days' : 'consecutive_days'})
最后,将其合并回原始数据框并删除多余的列:
df = pd.merge(df, consecutive_days, on='id', how='left')
df = df.drop(columns=['days_since_last','run'])
print(df)
id period in_date out_date n_days consecutive_days
0 1 1 2011-02-15 2011-05-21 95 713.0
1 1 2 2011-11-10 2012-10-11 336 713.0
2 1 3 2012-10-13 2013-10-25 377 713.0
3 2 1 2010-04-03 2012-02-16 684 NaN
4 2 3 2012-02-17 2012-02-19 2 NaN
5 2 5 2012-08-15 2013-11-23 465 NaN
6 2 6 2014-01-04 2014-12-18 348 NaN
7 3 2 2010-06-01 2011-08-21 446 286.0
8 3 3 2012-03-29 2012-09-11 166 286.0
9 3 4 2012-09-12 2013-01-10 120 286.0
关于python - 计算连续时间段的天数(Python,日期时间),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66030368/