从连续时间段(Python、datetime)计算天数
我想计算连续期间的天数 在下面的df中,我有四列:从连续时间段(Python、datetime)计算天数,python,pandas,datetime,Python,Pandas,Datetime,我想计算连续期间的天数 在下面的df中,我有四列: 身份证;代表一个人 期间;最低值为第一个周期,最高值为最近一个周期的数字 输入日期;期间开始的日期 过时;期间结束的日期 我想构建一个执行以下操作的通用函数: 计算连续期间的天数。如果两个期间之间的天数少于90天,则认为这两个期间是连续的 如果id的最后一个期间的过期日期为2013年,我只想计算天数。如果最后一个期间的“过期日期”为2014年或2012年,我想忽略该ID 我希望在结果变量中包含期间之间的天数 我的问题是,由于我对P
- 身份证;代表一个人
- 期间;最低值为第一个周期,最高值为最近一个周期的数字
- 输入日期;期间开始的日期
- 过时;期间结束的日期
- 计算连续期间的天数。如果两个期间之间的天数少于90天,则认为这两个期间是连续的
- 如果id的最后一个期间的过期日期为2013年,我只想计算天数。如果最后一个期间的“过期日期”为2014年或2012年,我想忽略该ID
- 我希望在结果变量中包含期间之间的天数
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
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
定义一个条件,说明间隔天数过高的情况:
days_cut = (df['days_since_last'] >= 90)
获取数据帧的一个子集,其中它是一个新的id
或一个有效的连续运行天数。为每个有效运行分配唯一的“运行”值(稍后用于分组):
将其合并回主数据框并向前填充run
,以便显示连续时段的有效运行位置:
df = pd.merge(df, tmp[['id','period','run']], on=['id','period'], how='left')
df['run'] = df['run'].fillna(method='ffill')
这就是当时的情况。您可以看到,对于每个id
都有连续运行的run
值:
id_changed = (df['id'].shift(1) != df['id'])
df.loc[id_changed, 'days_since_last'] = np.nan
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
我发布了一个答案,但我要指出的是,我在为它编写代码时,第一个id没有得到745,所以我要么误解了,要么上面的值不正确。@Rick M-这是我的失误。非常感谢你的努力!看起来不错!今天我将在我的真实世界数据集上尝试您的解决方案。一个简化方法是在开始时过滤掉那些没有out_date=2013的数据集。df['last_value']=df.groupby('id')['in_date']]。转换('last'),然后df=df[df['last_value]=2013]