wal*_*box 8 dataframe python-3.x pandas
我正在研究处方习惯并拥有大量已售产品的数据框。
我试图通过计算产品将持续多长时间并添加 5 天的依从性、开始延迟等因素来计算购买的结束日期,从而将药物的购买转化为药物的疗程。
然后我想将处方与重叠的日期窗口结合起来,但我正在努力寻找一种有效的方法来做到这一点。我希望 groupby 是可能的,但我不知道如何做到这一点。
我知道如何迭代数据帧以创建一个包含相关信息的新数据帧,但这是一个缓慢的操作,我希望我能找到一个更优雅的解决方案。
ID start end ingredient days dose end
1000 2018-10-03 2018-10-18 Metron... 10.0 125.00
1000 2018-10-13 2018-10-25 Metron... 7.0 125.00
1001 2018-03-08 2018-03-20 Cefalexin 7.0 150.00
1001 2018-09-17 2018-10-05 Cefalexin 13.0 150.00
1002 2018-05-18 2018-05-30 Amoxiclav 7.0 75.00
1002 2018-05-25 2018-06-06 Amoxiclav 7.0 100.00
1003 2018-07-01 2018-07-16 Amoxiclav 10.0 50.00
1003 2018-07-15 2018-07-30 Amoxiclav 10.0 50.00
1003 2018-07-25 2018-08-09 Amoxiclav 10.0 50.00
Run Code Online (Sandbox Code Playgroud)
我的预期结果如下:
ID start end ingredient days dose
1000 2018-10-03 2018-10-25 Metron... 17.0 125.00
1001 2018-03-08 2018-03-20 Cefalexin 7.0 150.00
1001 2018-09-17 2018-10-05 Cefalexin 13.0 150.00
1002 2018-05-18 2018-05-30 Amoxiclav 7.0 75.00
1002 2018-05-25 2018-06-06 Amoxiclav 7.0 100.00
1003 2018-07-01 2018-08-05 Amoxiclav 30.0 50.00
Run Code Online (Sandbox Code Playgroud)
1000的第二次购买正好是 10 天,因此结束日期与他们的第二次结束日期相同。
1001 没有重叠,所以保持原样。
1002 开始日期和结束日期重叠,但剂量有所变化,因此不应合并。
1003总共有 30 天。他们最终购买的开始日期晚于第一次购买的结束日期。他们的结束日期应该是他们第一次购买后的 35 天。这是一个可协商的标准,并且可以接受与最终购买的结束日期相匹配的结束日期。
我在这里吠错树了吗?这必须迭代完成吗?
我认为这里最大的问题是确定时间间隔何时重叠,其余的只是分组和相加。
首先,可以肯定的(如果尚未完成),以您的日期转换为datetime与天timedelta。这将有助于比较日期和持续时间并对其进行一些数学运算。
df['start'] = pd.to_datetime(df['start'])
df['end'] = pd.to_datetime(df['end'])
df['days'] = pd.to_timedelta(df['days'], unit='D')
Run Code Online (Sandbox Code Playgroud)
此代码产生您的预期结果:
def join_times(x):
startdf = pd.DataFrame({'time':x['start'], 'what':1})
enddf = pd.DataFrame({'time':x['end'], 'what':-1})
mergdf = pd.concat([startdf, enddf]).sort_values('time')
mergdf['running'] = mergdf['what'].cumsum()
mergdf['newwin'] = mergdf['running'].eq(1) & mergdf['what'].eq(1)
mergdf['group'] = mergdf['newwin'].cumsum()
x['group'] = mergdf['group'].loc[mergdf['what'].eq(1)]
res = x.groupby('group').agg({'days':'sum', 'start':'first'})
res['end'] = res.apply(lambda x : x['start'] + x['days'] + pd.to_timedelta(5, unit='D'), axis=1)
return res
ddf = df.groupby(['ID', 'ingredient', 'dose']).apply(join_times).reset_index().drop('group', axis=1)
Run Code Online (Sandbox Code Playgroud)
这是需要解释的。如您所见,我groupby用来识别子样本。然后工作由自定义join_times函数完成。
join_times函数在单个数据帧(列'time')开始和结束时间的同一列中连接在一起,按顺序排序。
第二列'what'用 +1 开始时间和 -1 结束时间标记。这些用于跟踪有多少间隔重叠(在列中'running'使用cumsum())。
然后建立一个布尔列'newwin'来标识一个新的非重叠时间间隔的开始,并建立一个列'group'来用相同的整数标记属于相同重叠时间间隔的行。
将 a'group'列添加到原始子样本中,复制先前构建的'group'列中的值。最后,我们可以为每个子样本确定哪些行有重叠。
所以我们可以groupby再次使用并对'days'列求和,保留列中的第一个日期'start'。
'end'列的计算方法是将'start'持续时间'days'加上 5 天。
上面的代码,使用您的数据样本,给出:
ID ingredient dose days start end
0 1000 Metron... 125.0 17 days 2018-10-03 2018-10-25
1 1001 Cefalexin 150.0 7 days 2018-03-08 2018-03-20
2 1001 Cefalexin 150.0 13 days 2018-09-17 2018-10-05
3 1002 Amoxiclav 75.0 7 days 2018-05-18 2018-05-30
4 1002 Amoxiclav 100.0 7 days 2018-05-25 2018-06-06
5 1003 Amoxiclav 50.0 30 days 2018-07-01 2018-08-05
Run Code Online (Sandbox Code Playgroud)
这是您的预期结果。由于groupby索引操作,列顺序不同。