python - 如何使用 groupby 和 filter 数据框创建新列
问题描述
假设我有一个数据集,其中包含留在 ICU 的患者心率的时间序列。
我想添加一些纳入标准,例如,我只想考虑心率 >= 90 至少一小时的患者在 ICU 停留。如果一小时后第一次测量的心率(从 >=90 值开始)未知,我们假设它高于 90 并包括入住 ICU。
应从对应于“至少 1 小时”时间跨度的第一次测量开始包括该 ICU 停留的条目。
请注意,一旦将 ICU 住院时间包括在内,即使心率在某个时候回落到 90 以下,它也不会再次被排除在外。
所以下面的数据框,其中“Icusay”对应于入住 ICU 的唯一 ID,“Hours”表示自进入 ICU 以来所花费的时间
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 个字段还多,但为了更好的可读性,我已经对其进行了简化)。由于有 40,000 名患者,我想加快速度。
这是我目前使用的代码,以及我上面介绍的玩具数据集。
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
请注意,数据集按患者和小时排序。
有没有办法加快这个速度?我曾考虑过像 group by 这样的内置函数,但我不确定它们在这种特殊情况下是否会有所帮助。
解决方案
这看起来有点难看,但它避免了循环,并且apply
(本质上只是一个循环)。我没有在大型数据集上进行过测试,但我怀疑它会比你当前的代码快很多。
首先,创建一些额外的列,其中包含下一行/上一行的详细信息,因为这可能与您的某些条件相关:
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 的第一个合格记录的 DataFrame(由于涉及的逻辑量,这现在非常混乱):
first_per_id = (all_records[((all_records['Heart Rate'] >= 90) |
((all_records['Heart Rate'].isnull()) &
(all_records['PrevHeartRate'] >= 90) &
(all_records['Icustay'] == all_records['PrevICU']))) &
((all_records['Hours'] >= 1) |
((all_records['NextHours'] >= 1) &
(all_records['NextICU'] == all_records['Icustay'])))]
.drop_duplicates(subset='Icustay', keep='first')[['Icustay']]
.reset_index()
.rename(columns={'index': 'first_index'}))
这给了我们:
first_index Icustay
0 1 1001
1 4 2010
您现在可以从原始 DataFrame 中删除所有新列:
all_records.drop(['PrevHeartRate', 'NextHours', 'PrevICU', 'NextICU'], axis=1, inplace=True)
然后我们可以将其与原始 DataFrame 合并:
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
从这里开始,我们只需要整理一下(将结果列转换为整数,并删除 first_index 列):
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
推荐阅读
- python - 尝试在 Ubuntu 20.04 上安装 python3.6 virtualenv
- mysql - 如何按sequelize中相关对象的数量排序?
- react-native - 世博会多个图像选择器为空
- python - 如何使用 Matplotlib 从虹膜数据中获取花瓣和萼片?
- java - 动态 JTable 大小(按行数)
- vue.js - 如何在Vue / October中获取上传图像的存储路径
- c# - 从嵌套集合中获取值 (LINQ)
- c# - 使用 c# 使用 openxml excel 将第一行设为只读
- r - 如何为 R 中的大型数据集生成词云?
- flutter - 我用 rxDart 写的代码不可读