Open JackieMium opened 6 years ago
哦对了,本篇全部代码(R 和 Python 混杂 hiahiahiahia):
# set up ------------------------------------------------------------------
library(RPostgreSQL)
library(tidyverse)
# being lazy
query <- function(query = query) {
con %>%
dbGetQuery(sql(query)) %>%
as_tibble()
}
# connect to DB -----------------------------------------------------------
drv <- dbDriver("PostgreSQL")
con <- dbConnect(
drv = drv,
dbname = "mimic",
user = "postgres",
.rs.askForPassword("Enter password for user postgres:")
)
# set the search path to the mimiciii schema
dbSendQuery(con, "SET search_path TO mimiciii, public;")
query()
去掉,一律使用 SQL。
(就算想要使用 R 的话直接复制代码每一段加上套壳也就行了)# Step 1: Identification of key terms -------------------------------------
SELECT itemid, label
, category, linksto
FROM d_items
WHERE dbsource = 'metavision'
AND LOWER(label) LIKE '%crrt%';
# Step 2: Extraction of ITEMIDs from tables -------------------------------
SELECT itemid, label
, category, linksto
FROM d_items di
WHERE dbsource = 'metavision'
AND (LOWER(label) LIKE '%dialy%'
OR category = 'Dialysis'
OR LOWER(label) LIKE '%crrt%')
ORDER BY linksto, category, label;
# table 1 of 3: itemid from CHARTEVENTS
SELECT ce.icustay_id
, di.label
, ce.charttime
, ce.value, ce.valueuom
FROM chartevents ce INNER JOIN d_items di ON
ce.itemid = di.itemid
WHERE ce.icustay_id = 246866
AND ce.itemid in
(
224404, -- | ART Lumen Volume
224406, -- | VEN Lumen Volume
228004, -- | Citrate (ACD-A)
224145, -- | Heparin Dose (per hour)
225183, -- | Current Goal
224149, -- | Access Pressure
224144, -- | Blood Flow (ml/min)
224154, -- | Dialysate Rate
224151, -- | Effluent Pressure
224150, -- | Filter Pressure
224191, -- | Hourly Patient Fluid Removal
228005, -- | PBP (Prefilter) Replacement Rate
228006, -- | Post Filter Replacement Rate
224153, -- | Replacement Rate
224152, -- | Return Pressure
226457 -- | Ultrafiltrate Output
)
ORDER BY ce.icustay_id, ce.charttime, di.label;
还是有一小段 R,哭了。
# a helper function which prints out the number of observations for a given itemid:
query_item <- function(item_id){
qur <- stringr::str_replace_all(paste("
SELECT value
, COUNT(distinct icustay_id) AS number_of_patients
, COUNT(icustay_id) AS number_of_observations
FROM chartevents
WHERE itemid = '",item_id,
"' GROUP BY value ORDER BY value;", sep = ""), "[\n]", "")
query(qur)
}
query_item(224146)
query_item(225956)
query_item(225958)
query_item(225976)
query_item(225977)
query_item(227290)
WITH t1 AS
(
SELECT icustay_id,
MAX(CASE WHEN
itemid = 227290 THEN 1
ELSE 0 END) AS HasMode
FROM chartevents ce
WHERE itemid IN
(
227290, -- CRRT mode
228004, -- Citrate (ACD-A)
225958, -- Heparin Concentration (units/mL)
224145, -- Heparin Dose (per hour)
225183, -- Current Goal -- always there
224149, -- Access Pressure
224144, -- Blood Flow (ml/min)
225977, -- Dialysate Fluid
224154, -- Dialysate Rate
224151, -- Effluent Pressure
224150, -- Filter Pressure
224191, -- Hourly Patient Fluid Removal
228005, -- PBP (Prefilter) Replacement Rate
228006, -- Post Filter Replacement Rate
225976, -- Replacement Fluid
224153, -- Replacement Rate
224152, -- Return Pressure
226457 -- Ultrafiltrate Output
)
GROUP BY icustay_id
)
SELECT COUNT(icustay_id) AS Num_ICUSTAY_ID
, SUM(hasmode) AS Num_With_Mode
FROM t1;
WITH t1 AS
(
SELECT icustay_id, charttime
, MAX(CASE WHEN
itemid = 227290 THEN 1
ELSE 0 END) AS HasCRRTMode
, MAX(CASE WHEN
itemid != 227290 THEN 1
ELSE 0 END) AS OtherITEMID
FROM chartevents ce
WHERE itemid in
(
227290, -- CRRT mode
228004, -- Citrate (ACD-A)
225958, -- Heparin Concentration (units/mL)
224145, -- Heparin Dose (per hour)
225183, -- Current Goal -- always there
224149, -- Access Pressure
224144, -- Blood Flow (ml/min)
225977, -- Dialysate Fluid
224154, -- Dialysate Rate
224151, -- Effluent Pressure
224150, -- Filter Pressure
224191, -- Hourly Patient Fluid Removal
228005, -- PBP (Prefilter) Replacement Rate
228006, -- Post Filter Replacement Rate
225976, -- Replacement Fluid
224153, -- Replacement Rate
224152, -- Return Pressure
226457 -- Ultrafiltrate Output
)
GROUP BY icustay_id, charttime
)
SELECT count(icustay_id) AS NumObs
, SUM(CASE WHEN
HasCRRTMode = 1 AND OtherITEMID = 1 THEN 1
ELSE 0 END) AS Both
, SUM(CASE WHEN
HasCRRTMode = 1 AND OtherITEMID = 0 THEN 1
ELSE 0 END) AS OnlyCRRTMode
, SUM(CASE WHEN
HasCRRTMode = 0 AND OtherITEMID = 1 THEN 1
ELSE 0 END) AS NoCRRTMode
FROM t1;
楼主,建议你用 dplyr::tbl(conn,"tbl") %>%
的 pipeline ,效率可以提升,而且后期如果从 Local/PG/MySQL 迁移到 spark 上非常容易
@harryprince 谢谢你的建议,我对 tidyverse
这一套还很生疏,PostgreSQL 也是初学的。不过迁移大概用不上,身边只有我在学这个,而且是在自己的笔记本上(╥_╥)。
花了几天时间把
mimic-code/notebooks/crrt-notebook.ipynb
从头到尾看了一遍。虽然消化得还不是很好,但是觉得这一篇教程真的是干货满满。决定还是花点时间仔细再整理一下。和前面一样,我还是尽量放到 R 里做,R 不好做的我再到 Juputer 里做。R 的设置在上一篇里写过,这里我就只写 Python 里的准备工作了。需要的东西有:这个记事本(因为教程以 Jupyter Notebook 的形式存在,所以一直称为记事本)总体讲述如何在 MIMIC 数据中定义 CRRT。CRRT,Continuous renal replacement therapy,中文作连续性肾脏替代治疗,也被称作连续血液净化治疗 (continuous blood purification, CBP)。
CRRT 是临床出现一种新的代替肾脏治疗方法, 即每天持续 24 小时或接近 24 小时的一种长时间、连续体外血液净化疗法。
【邓青志,余阶洋,彭佳华.连续性肾脏替代治疗对ICU脓毒症患者的临床研究进展[J]. 中国医学工程, 2018, 26(04): 30-32.】
以及
【马帅,丁峰.连续性肾脏替代治疗的过去、现在与未来[J].上海医药,2018,39(09):3-5+11.】
这个记事本主要目的是在 MIMIC-III v1.4 数据中定义病人 CRRT 的开始和结束时间;次要目的是展示如何从 MIMIC-III 数据中提取和整理临床数据。
框架
在 MIMIC-III 数据库中,定义一个临床概念包含一下几个关键步骤:
d_items
表格中搜索这些关键词(如果是实验室检查的话要看d_labitems
表格)。d_items
表格的linksto
这一列指定的表格中提取数据这整个过程是迭代进行的,也没有上面描述的那么清晰——验证时你可能又要回去修改数据提取的规则,等等。而且对于 MIMIC-III 数据,这整个过程还必须重复一次:一次是 MetaVision,一次是 CareVue。
MetaVision 和 CareVue
MIMIC-III 中的数据来自两个不同的 ICU 数据库系统。其结果就是,同一个临床概念的数据可能对应到多个不同的
itemid
。比如,病人心率数据算是一个比较容易提取的临床概念了,但是在d_items
表格中匹配“heart rate”却可以发现至少两个itemid
:得到:
可以看到两个
itemid
都对应心率——但是一个是 CareVue 数据库系统使用的(dbsource = 'carevue'
)而另一个是 MetaVision 系统使用的(dbsource = 'metavision'
)。这也就是上面提到的,数据提取过程必须重复一次。通常来说,推荐先提取 MetaVision 数据,因为其数据组织形式更好,并且可以为后续到底需要纳入哪些数据提供了一些十分有用的信息。比如,MetaVision 里的itemid
的每一个label
都有一个相应的缩写,而这些缩写可以在后面用来在 CareVue 中搜索用。Step 0: import libraries, connect to the database
由于是 Python 来做的,所以首先是载入包和一些设置。首先是所有要用到的包:
然后一些简单的设置和连接数据库:
我自己在连接数据库的时候每次都会出现报错:
Google 了一下就是这个文件放在不同的位置了,建立一个软链接就好:
Step 1: Identification of key terms
我们感兴趣的是 CRRT,那么首先我们直接在 MetaVision 数据中搜索”CRRT“看看:
可以得到:
然后我们就可以通过结果拓展我们开始的搜索方法了:
Step 2: Extraction of ITEMIDs from tables
Get list of itemid related to CRRT
(从这里开始为了贴结果方便我还是切到 R 里做了)
首先我们根据刚刚改进的搜索词来找到对应的
itemid
:Manually label above itemid
上面得到的是所有有可能会用来提取 CRRT 数据的数据元素。所以下一步就是鉴别哪些元素可以用来定义治疗的开始和结束的时间。这个工作得依靠专业知识进行(而不是简单地编程的问题)。
通过
linksto
列把表格分开,人工查看所有itemid
后我们得到下面这张表格,初步筛选后把所有itemid
标记为 "consider for further review"(待商榷) 或者 "not relevant"(无关)。Links to CHARTEVENTS
Links to DATETIMEEVENTS
Links to INPUTEVENTS_MV
Links to PROCEDUREEVENTS_MV
Reasons for inclusion/exclusion
筛选时的纳入和排除标准为:
itemid
被排除的原因是有 access line 并不一定保证病人正在接受 CRRT 治疗。虽然对于 CRRT 治疗 access line 确实必不可少,但是病人并未正在透析时也会有这些记录。(这一段不是很懂,原文:Access lines- no (excluded) - these ITEMIDs are not included as the presence of an access line does not guarantee that CRRT is being delivered. While having an access line is a requirement of performing CRRT, these lines are present even when a patient is not actively being hemodialysed. 主要问题在于 Access line 到底指的什么。是指数据中的记录呢?还是指做透析用的输液管留置管之类的什么东西?大概后者可能性更大)Step 3: Define rules based upon ITEMIDs
我们已经初步筛选得到应该纳入哪些数据了,现在就可以通过对应的
itemid
筛选到的数据来进一步定义 CRRT 的治疗时间了:这些数据表示 CRRT 开始、停止、继续还是其他什么呢?我们直接根据上面的表格按照 CHARTEVENTS, INPUTEVENTS_MV, 以及 PROCEDUREEVENTS_MV 的顺序再来看看这些数据到底代表着 CRRT 的什么过程。注意这些 _MV 后缀就是表示这些表格数据来自于 MetaVision,而 _CV 就代表来自 CareVue。所以就像之前说的,等我们把 MetaVision 数据提取完了,还必须针对 CareVue 再做一次。
table 1 of 3: itemid from CHARTEVENTS
从 CHARTEVENTS 表格里纳入的 CRRT 有关的数据元素有:
我们先看看这些数字型的数据。根据专业人士的意见,这些数据应该是 CRRT 的关键参数并且接受 CRRT 的病人会每小时都有记录。
得到:
从结果中可以看到
ART Lumen Volume
和VEN Lumen Volume
的记录时间和其它数据记录时间差别很大。和专业人员讨论后他们认为这是合理的,这些液体流速参数意味着输液管是开着的,但是这并不代表 CRRT 正在进行(这一句不知道翻译是否正确,原文:as these volumes indicate settings to keep open the line and are not directly relevant to the administration of CRRT)—— 最好的情况是这些数据是冗余的,最坏的情况是引起对判断 CRRT 开始和停止的误判。因此最后我们把这两项去掉了。剩下的itemid
有:再来看剩下的字符型数据:
我们一个一个
itemid
往下看。首先为了查看方便我们再来定义一个简单地函数:224146 - System Integrity
用上面定义的偷懒函数直接
query_item(224146)
得:和专业人员谈论后,我们得知这每一项都代表 CRRT 治疗的不同阶段。我们简单地分为三类:started,stopped 或者 active(即已开始,已停止和进行中)。既然 active 表明 CRRT 进行中,那么 active 首次出现也有可能指开始,因此我们直接归类为 ”active/started“。所以人工整理后得到:
后面我们再写代码来合并这些
itemid
。225956 - Reason for CRRT Filter Change
query_item(225956)
:这三项是 stop(即 CRRT 停止),因为这时候要更换滤器。随后的 CRRT 则为 restart(重新开始),而不是当前 CRRT 的延续。(这一段不是很懂是要表示什么,按理来说更换滤器之后开始应该是算作一次啊)
225958 - Heparin Concentration (units/mL)
query_item(225958)
:这些是 CRRT 的常规参数,可以和其他数字型字段放到一起。(这什么意思??)
225976 - Replacement Fluid
query_item(225976)
:CRRT 的常规参数,可以和其他数字型字段放到一起。
225977 - Dialysate Fluid
query_item(225977)
:CRRT 的常规参数,可以和其他数字型字段放到一起。
227290 - CRRT mode
query_item(227290)
:虽然看起来不错,但是有可能
CRRT mode
(CRRT 模式)和真正 CRRT 治疗不是同时记录的。我们来看看是不是所有有 CRRT 参数记录的病人都记录了CRRT mode
:结果:
或者现在进一步查询,有多少人没有记录其他 CRRT 参数而仅有
CRRT mode
呢?得到:
可以看到
CRRT mode
这个参数基本上很冗余(27446/81162 例既有CRRT mode
的记录也有其他,而只有个别人只有CRRT mode
记录而没有其他),并且也不能表示 CRRT 正在进行中,而且数据也不完全兼容(53778/81162 例接受 CRRT 治疗的病人其实并没有CRRT mode
的记录。不知道后面这句话指的具体是什么,但是我注意到在上面的表格里 81162 != 27446 + 1 + 53778),最终我们决定从item_id
里把它排除了。CHARTEVENTS wrap up
稍稍总结下,最后 CHARTEVENTS 里剩下的表示 CRRT 的 started/ongoing 的
itemid
是这些:还有下面这些表示 CRRT 的 started/stopped/ongoing 但是还需要特别处理的:
table 2 of 3: INPUTEVENTS_MV
INPUTEVENT_MV 里的
item_id
有:根据专业人士的意见,这些项目肯定是 CRRT 才会有的不需要特别去看了,我们直接把它们标记为 CRRT active/started。
table 3 of 3: PROCEDUREEVENTS_MV
PROCEDUREEVENTS_MV 里的
item_id
有:唯一有点争议的
item_id
是225436
(CRRT Filter Change)。这个item_id
代表 CRRT 中断,并且更换完成后 CRRT 再开始。原则上这可以作为结束时间,但是这一记录没有 100% 完整(不知道什么意思),专业人士的意见是相比把这个记录作为 CRRT 结束时间,可能直接忽略掉更好。因此最终纳入的是:
到这里第 3 步也是最繁琐的人工查看每个
item_id
并依据专业知识决定是否纳入以及纳入的元素如何分类就做完了。下面就是利用我们选好的item_id
来定义 CRRT 的时间了。下一篇继续。Cheers.