从连续时间段(Python、datetime)计算天数

从连续时间段(Python、datetime)计算天数,python,pandas,datetime,Python,Pandas,Datetime,我想计算连续期间的天数 在下面的df中,我有四列: 身份证;代表一个人 期间;最低值为第一个周期,最高值为最近一个周期的数字 输入日期;期间开始的日期 过时;期间结束的日期 我想构建一个执行以下操作的通用函数: 计算连续期间的天数。如果两个期间之间的天数少于90天,则认为这两个期间是连续的 如果id的最后一个期间的过期日期为2013年,我只想计算天数。如果最后一个期间的“过期日期”为2014年或2012年,我想忽略该ID 我希望在结果变量中包含期间之间的天数 我的问题是,由于我对P

我想计算连续期间的天数

在下面的df中,我有四列:

  • 身份证;代表一个人
  • 期间;最低值为第一个周期,最高值为最近一个周期的数字
  • 输入日期;期间开始的日期
  • 过时;期间结束的日期
我想构建一个执行以下操作的通用函数:

  • 计算连续期间的天数。如果两个期间之间的天数少于90天,则认为这两个期间是连续的

  • 如果id的最后一个期间的过期日期为2013年,我只想计算天数。如果最后一个期间的“过期日期”为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
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]