Python 如何使用groupby和filter数据框创建新列
假设我有一个数据集,其中包含了ICU患者心率的时间序列 我想补充一些纳入标准,例如,我只想考虑至少90小时的心跳率>0的患者的ICU停留时间。如果一小时后第一次测量的心率(从>=90开始)未知,我们假设它高于90,并包括ICU住院时间 从与“至少1小时”时间跨度对应的第一次测量开始,应包括ICU住院记录 请注意,一旦ICU住院包括在内,它就再也不会被排除在外,即使心率在某个时候回落到90以下 因此,下面的数据框中,“Icustay”对应于ICU住院的唯一ID,“Hours”表示进入ICU后在ICU的时间Python 如何使用groupby和filter数据框创建新列,python,python-3.x,pandas,numpy,Python,Python 3.x,Pandas,Numpy,假设我有一个数据集,其中包含了ICU患者心率的时间序列 我想补充一些纳入标准,例如,我只想考虑至少90小时的心跳率>0的患者的ICU停留时间。如果一小时后第一次测量的心率(从>=90开始)未知,我们假设它高于90,并包括ICU住院时间 从与“至少1小时”时间跨度对应的第一次测量开始,应包括ICU住院记录 请注意,一旦ICU住院包括在内,它就再也不会被排除在外,即使心率在某个时候回落到90以下 因此,下面的数据框中,“Icustay”对应于ICU住院的唯一ID,“Hours”表示进入ICU后在IC
Heart Rate Hours Icustay Inclusion Criteria
0 79 0.0 1001 0
1 91 1.5 1001 0
2 NaN 2.7 1001 0
3 85 3.4 1001 0
4 90 0.0 2010 0
5 94 29.4 2010 0
6 68 0.0 3005 0
应该成为
Heart Rate Hours Icustay Inclusion Criteria
0 79 0.0 1001 0
1 91 1.5 1001 1
2 NaN 2.7 1001 1
3 85 3.4 1001 1
4 90 0.0 2010 1
5 94 29.4 2010 1
6 68 0.0 3005 0
我已经为此编写了代码,并且可以正常工作。但是它非常慢,在处理我的整个数据集时,每个患者可能需要几秒钟的时间(实际上,我的数据集包含的数据比这6个字段多,但为了更好的可读性,我对其进行了简化)。因为有4万病人,我想加快速度
这是我目前正在使用的代码,以及我上面介绍的玩具数据集
import numpy as np
import pandas as pd
d = {'Icustay': [1001, 1001, 1001, 1001, 2010, 2010, 3005], 'Hours': [0, 1.5, 2.7, 3.4, 0, 29.4, 0],
'Heart Rate': [79, 91, np.NaN, 85, 90, 94, 68], 'Inclusion Criteria':[0, 0, 0, 0, 0, 0, 0]}
all_records = pd.DataFrame(data=d)
for curr in np.unique(all_records['Icustay']):
print(curr)
curr_stay = all_records[all_records['Icustay']==curr]
indexes = curr_stay['Hours'].index
heart_rate_flag = False
heart_rate_begin_time = 0
heart_rate_begin_index = 0
for i in indexes:
if(curr_stay['Heart Rate'][i] >= 90 and not heart_rate_flag):
heart_rate_flag = True
heart_rate_begin_time = curr_stay['Hours'][i]
heart_rate_begin_index = i
elif(curr_stay['Heart Rate'][i] < 90):
heart_rate_flag = False
elif(heart_rate_flag and curr_stay['Hours'][i]-heart_rate_begin_time >= 1.0):
all_records['Inclusion Criteria'].iloc[indexes[indexes>=heart_rate_begin_index]] = 1
break
将numpy导入为np
作为pd进口熊猫
d={'Icustay':[10011000100103005],'Hours':[0,1.5,2.7,3.4,0,29.4,0],
“心率”:[79,91,np.NaN,85,90,94,68],“纳入标准”:[0,0,0,0,0,0]}
所有_记录=pd.DataFrame(数据=d)
对于np.unique中的货币(所有_记录['Icustay']):
印刷(货币)
当前停留=所有记录[所有记录['Icustay']==当前]
索引=当前停留时间['Hours']。索引
心率标志=错误
心率开始时间=0
心率开始指数=0
对于索引中的i:
如果(curr_stay['heartrate'][i]>=90,而非心率标志):
心率标志=真
心率开始时间=当前停留时间[小时][i]
心率开始指数=i
elif(当前停留时间[心率][i]<90):
心率标志=错误
elif(心率标志和当前停留时间['Hours'][i]-心率开始时间>=1.0):
所有记录['Inclusion Criteria'].iloc[索引[索引>=心率\u开始\u索引]]=1
打破
请注意,数据集是按患者和小时排序的
有没有办法加快速度?我曾经考虑过像groupby这样的内置函数,但我不确定它们在这种特殊情况下是否有用。您可以在pandas中使用
groupby
和apply
函数。这也应该更快
## fill missing values
all_records['Heart Rate'].fillna(90, inplace=True)
## use apply
all_records['Inclusion Criteria'] = all_records.groupby('Icustay').apply(lambda x: (x['Heart Rate'].ge(90)) & (x['Hours'].ge(0))).values.astype(int)
print(all_records)
Heart Rate Hours Icustay Inclusion Criteria
0 79.0 0.0 1001 0
1 91.0 1.5 1001 1
2 97.0 2.7 1001 1
3 90.0 3.4 1001 1
4 90.0 0.0 2010 1
5 94.0 29.4 2010 1
6 68.0 0.0 3005 0
这看起来有点难看,但它避免了循环,并且
应用了(实际上只是引擎盖下的一个循环)。我还没有在大型数据集上进行测试,但我怀疑它将比您当前的代码快得多
首先,创建一些包含下一行/上一行详细信息的附加列,因为这可能与您的某些条件有关:
all_records['PrevHeartRate'] = all_records['Heart Rate'].shift()
all_records['NextHours'] = all_records['Hours'].shift(-1)
all_records['PrevICU'] = all_records['Icustay'].shift()
all_records['NextICU'] = all_records['Icustay'].shift(-1)
接下来,创建一个数据帧,其中包含每个id的第一条符合条件的记录(由于涉及大量逻辑,这现在非常混乱):
这给了我们:
first_index Icustay
0 1 1001
1 4 2010
现在可以从原始数据帧中删除所有新列:
all_records.drop(['PrevHeartRate', 'NextHours', 'PrevICU', 'NextICU'], axis=1, inplace=True)
然后,我们可以将其与原始数据帧合并:
new = pd.merge(all_records, first_per_id, how='left', on='Icustay')
给予:
Heart Rate Hours Icustay Inclusion Criteria first_index
0 79.0 0.0 1001 0 1.0
1 91.0 1.5 1001 0 1.0
2 97.0 2.7 1001 0 1.0
3 NaN 3.4 1001 0 1.0
4 90.0 0.0 2010 0 4.0
5 94.0 29.4 2010 0 4.0
6 68.0 0.0 3005 0 NaN
从这里,我们可以将“first_index”(该id的第一个合格索引)与实际索引进行比较:
new['Inclusion Criteria'] = new.index >= new['first_index']
这使得:
Heart Rate Hours Icustay Inclusion Criteria first_index
0 79.0 0.0 1001 False 1.0
1 91.0 1.5 1001 True 1.0
2 97.0 2.7 1001 True 1.0
3 NaN 3.4 1001 True 1.0
4 90.0 0.0 2010 True 4.0
5 94.0 29.4 2010 True 4.0
6 68.0 0.0 3005 False NaN
从这里开始,我们只需要整理(将结果列转换为整数,并删除第一个索引列):
给出最终预期结果:
Heart Rate Hours Icustay Inclusion Criteria
0 79.0 0.0 1001 0
1 91.0 1.5 1001 1
2 97.0 2.7 1001 1
3 NaN 3.4 1001 1
4 90.0 0.0 2010 1
5 94.0 29.4 2010 1
6 68.0 0.0 3005 0
谢谢你的提问。如果在同一ICU住院期间心率回到90以下,会发生什么情况?如果患者在某个时间点满足标准,即使心率回到90以下,在剩余的住院期间,纳入标准仍然为1。我将编辑我的问题,使之更清楚。我对时间跨度有点困惑。您说:该ICU住院的条目应从与“至少1小时”时间跨度相对应的第一次测量开始包含。
,但在您的示例中,您包括ICU住院的第一次观察2010
,即使是在小时=0.0
时观察到的。“至少1小时”是指纳入标准的时间跨度,而不是ICU总住院时间。因此,对于2010年ICU住院,心率在至少一个小时的时间内高于90,这段时间从0.0开始,这就是为什么它被包括在内。我一直在查看数据以寻找错误的情况,但它似乎总是按照预期工作,谢谢!谢谢,可悲的是,这并不能解决问题(我只是编辑了一下以澄清问题),因为它排除了ICU住院满足纳入标准后心率低于90的患者。对不起,在原来的问题中没有说得更清楚
new.drop('first_index', axis=1, inplace=True)
new['Inclusion Criteria'] = new['Inclusion Criteria'].astype(int)
Heart Rate Hours Icustay Inclusion Criteria
0 79.0 0.0 1001 0
1 91.0 1.5 1001 1
2 97.0 2.7 1001 1
3 NaN 3.4 1001 1
4 90.0 0.0 2010 1
5 94.0 29.4 2010 1
6 68.0 0.0 3005 0