├── analysis ├── letter_response │ ├── README.md │ └── letter_response.Rmd ├── out-table-death.csv ├── out-table-bad_hfd.csv ├── out-table-good_hfd.csv ├── out-table-los.csv ├── out-table-ddelay.csv └── analysis_paper.Rmd ├── LICENSE ├── .gitignore ├── README.md ├── callout-create-tables.py ├── callout-create-tables.ipynb └── sql ├── 3-final-design-matrix.sql ├── 1-define-cohort.sql ├── 0-phi-info.sql └── 2-icu-variables.sql /analysis/letter_response/README.md: -------------------------------------------------------------------------------- 1 | # Response to Drs. Ofoma and Keithireddy Letter 2 | 3 | This analysis was done in response to a letter submitted by Drs. Ofoma and Keithireddy about our paper. 4 | 5 | The main concern was related to the use of the discharge delay > 24 h exposure. This work explores the sensitivity of the conclusions to this parametrization fitting natural cubic splines and a more granular binned approach. 6 | 7 | They had also request more information about the different groups with longer/shorter discharge delays, and this was actually looked at in our previous output document (../analysis_paper.html), but is repeated here for completeness. 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MIT Laboratory for Computational Physiology 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /analysis/out-table-death.csv: -------------------------------------------------------------------------------- 1 | "","","2.5 %","97.5 %","" 2 | "I(cut2(DISCHARGEDELAY_HOURS, c(24)) == ""[ 24.000,129.566]"")TRUE",0.991,0.74,1.309,0.948 3 | "cut2(oasis, g = 3)[27,34)",2.034,1.542,2.705,0 4 | "cut2(oasis, g = 3)[34,64]",4.199,3.23,5.52,0 5 | "request_tele",0.736,0.601,0.896,0.002 6 | "request_vre",1.848,1.335,2.514,0 7 | "request_cdiff",1.531,1.101,2.091,0.009 8 | "cut2(elixhauser_hospital, g = 3)[ 1, 7)",2.142,1.604,2.887,0 9 | "cut2(elixhauser_hospital, g = 3)[ 7,31]",3.759,2.888,4.956,0 10 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 1.000, 3.000)",0.922,0.709,1.206,0.55 11 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 3.000, 7.000)",1.646,1.252,2.17,0 12 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 7.000, 28.000)",2.314,1.739,3.087,0 13 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 28.000,130.762]",10.14,5.319,18.993,0 14 | "as.factor(callout_year)2006",0.79,0.502,1.272,0.32 15 | "as.factor(callout_year)2007",0.704,0.453,1.121,0.128 16 | "as.factor(callout_year)2008",0.547,0.349,0.877,0.01 17 | "as.factor(callout_year)2009",0.501,0.32,0.803,0.003 18 | "as.factor(callout_year)2010",0.638,0.412,1.013,0.049 19 | "as.factor(callout_year)2011",0.52,0.332,0.834,0.005 20 | "as.factor(callout_wardid == 1)TRUE",0.506,0.406,0.634,0 21 | -------------------------------------------------------------------------------- /analysis/out-table-bad_hfd.csv: -------------------------------------------------------------------------------- 1 | "","","2.5 %","97.5 %","" 2 | "I(cut2(DISCHARGEDELAY_HOURS, c(24)) == ""[ 24.000,129.566]"")TRUE",1.015,0.799,1.278,0.903 3 | "cut2(oasis, g = 3)[27,34)",1.347,1.104,1.646,0.003 4 | "cut2(oasis, g = 3)[34,64]",2.264,1.867,2.752,0 5 | "request_tele",0.79,0.672,0.927,0.004 6 | "request_vre",1.798,1.367,2.34,0 7 | "request_cdiff",1.507,1.138,1.972,0.003 8 | "cut2(elixhauser_hospital, g = 3)[ 1, 7)",1.674,1.348,2.083,0 9 | "cut2(elixhauser_hospital, g = 3)[ 7,31]",2.796,2.296,3.423,0 10 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 1.000, 3.000)",1.01,0.815,1.256,0.928 11 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 3.000, 7.000)",1.814,1.448,2.278,0 12 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 7.000, 28.000)",2.965,2.347,3.754,0 13 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 28.000,130.762]",11.258,6.231,20.367,0 14 | "as.factor(callout_year)2006",0.954,0.647,1.429,0.813 15 | "as.factor(callout_year)2007",0.805,0.551,1.196,0.27 16 | "as.factor(callout_year)2008",0.676,0.462,1.007,0.049 17 | "as.factor(callout_year)2009",0.653,0.446,0.972,0.031 18 | "as.factor(callout_year)2010",0.732,0.503,1.086,0.112 19 | "as.factor(callout_year)2011",0.544,0.369,0.815,0.003 20 | "as.factor(callout_wardid == 1)TRUE",0.501,0.419,0.601,0 21 | -------------------------------------------------------------------------------- /analysis/out-table-good_hfd.csv: -------------------------------------------------------------------------------- 1 | "","","2.5 %","97.5 %","" 2 | "I(cut2(DISCHARGEDELAY_HOURS, c(24)) == ""[ 24.000,129.566]"")TRUE",0.984,0.843,1.152,0.844 3 | "cut2(oasis, g = 3)[27,34)",0.822,0.731,0.925,0.001 4 | "cut2(oasis, g = 3)[34,64]",0.675,0.594,0.767,0 5 | "cut2(age, g = 3)[56.1,73.8)",0.971,0.862,1.094,0.631 6 | "cut2(age, g = 3)[73.8,91.4]",1.136,1,1.29,0.05 7 | "request_vre",0.549,0.451,0.67,0 8 | "request_cdiff",0.711,0.582,0.871,0.001 9 | "cut2(elixhauser_hospital, g = 3)[ 1, 7)",0.685,0.606,0.774,0 10 | "cut2(elixhauser_hospital, g = 3)[ 7,31]",0.496,0.44,0.559,0 11 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 1.000, 3.000)",0.843,0.744,0.956,0.008 12 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 3.000, 7.000)",0.532,0.461,0.613,0 13 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 7.000, 28.000)",0.27,0.23,0.316,0 14 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 28.000,130.762]",0.12,0.062,0.221,0 15 | "as.factor(callout_year)2006",1.011,0.771,1.319,0.938 16 | "as.factor(callout_year)2007",1.161,0.893,1.503,0.262 17 | "as.factor(callout_year)2008",1.369,1.054,1.771,0.018 18 | "as.factor(callout_year)2009",1.396,1.074,1.807,0.012 19 | "as.factor(callout_year)2010",1.261,0.972,1.627,0.077 20 | "as.factor(callout_year)2011",1.442,1.11,1.864,0.006 21 | "as.factor(callout_wardid == 1)TRUE",1.691,1.49,1.917,0 22 | -------------------------------------------------------------------------------- /analysis/out-table-los.csv: -------------------------------------------------------------------------------- 1 | "","","2.5 %","97.5 %","" 2 | "I(cut2(DISCHARGEDELAY_HOURS, c(24)) == ""[ 24.000,129.566]"")TRUE",-0.131,-0.179,-0.083,0 3 | "cut2(oasis, g = 3)[27,33)",0.054,0.018,0.09,0.003 4 | "cut2(oasis, g = 3)[33,62]",0.074,0.037,0.112,0 5 | "cut2(age, g = 3)[55.8,73.4)",0.08,0.044,0.116,0 6 | "cut2(age, g = 3)[73.4,91.4]",0.035,-0.004,0.074,0.076 7 | "female",0.042,0.013,0.071,0.004 8 | "request_tele",0.047,0.017,0.077,0.002 9 | "request_resp",0.127,0.015,0.239,0.027 10 | "request_mrsa",0.12,0.077,0.164,0 11 | "request_vre",0.148,0.079,0.217,0 12 | "request_cdiff",0.126,0.058,0.194,0 13 | "cut2(elixhauser_hospital, g = 3)[ 1, 7)",0.109,0.074,0.145,0 14 | "cut2(elixhauser_hospital, g = 3)[ 7,31]",0.226,0.19,0.262,0 15 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 1.000, 3.000)",0.094,0.058,0.129,0 16 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 3.000, 7.000)",0.29,0.247,0.334,0 17 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 7.000, 28.000)",0.549,0.495,0.602,0 18 | "cut2(los_pre_callout_days, c(1, 3, 7, 28))[ 28.000,110.022]",0.741,0.503,0.979,0 19 | "as.factor(callout_dayofweek)monday ",-0.115,-0.169,-0.061,0 20 | "as.factor(callout_dayofweek)saturday ",-0.028,-0.086,0.029,0.334 21 | "as.factor(callout_dayofweek)sunday ",-0.065,-0.123,-0.007,0.029 22 | "as.factor(callout_dayofweek)thursday ",-0.037,-0.091,0.017,0.177 23 | "as.factor(callout_dayofweek)tuesday ",-0.126,-0.18,-0.072,0 24 | "as.factor(callout_dayofweek)wednesday",-0.062,-0.115,-0.009,0.022 25 | "as.factor(callout_wardid == 1)TRUE",-0.185,-0.228,-0.142,0 26 | "cut2(PROPFULL_BEDS, c(0.9, 1))[0.900,1.000)",-0.001,-0.04,0.038,0.963 27 | "cut2(PROPFULL_BEDS, c(0.9, 1))[1.000,1.093]",-0.062,-0.113,-0.011,0.018 28 | "MED_SERVICETRUE",-0.068,-0.126,-0.01,0.022 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # callout [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1410655.svg)](https://doi.org/10.5281/zenodo.1410655) 2 | 3 | The research question of this study is: Do discharge delays from the ICU adversely affect patient outcomes? 4 | 5 | This research was published in the Journal of Intensive Care Medicine as: [Impact of Intensive Care Unit Discharge Delays on Patient Outcomes: A Retrospective Cohort Study](https://doi.org/10.1177/0885066618800276) 6 | 7 | ``` 8 | @article{bose2018impact, 9 | title={Impact of Intensive Care Unit Discharge Delays on Patient Outcomes: A Retrospective Cohort Study}, 10 | author={Bose, Somnath and Johnson, Alistair EW and Moskowitz, Ari and Celi, Leo Anthony and Raffa, Jesse D}, 11 | journal={Journal of intensive care medicine}, 12 | pages={0885066618800276}, 13 | year={2018}, 14 | publisher={SAGE Publications Sage CA: Los Angeles, CA}, 15 | doi={10.1177/0885066618800276} 16 | } 17 | ``` 18 | 19 | This research was generously supported by grants 1U01HL123022-01 (NIH/NHLBI), R01- 20 | EB017205 (NIH/NIBIB), R01-GM104987 (NIH/NIBIB), and 2T32HL007374-37 (NIH). 21 | 22 | # Data extraction 23 | 24 | Data extraction is done in a set of SQL scripts numbered according to the order in which they should be executed (0-4). Note that data extraction is done in two distinct systems: (i) month/year PHI data (along with hospital census information) is extracted in 0-phi-info.sql, (ii) covariates available in MIMIC-III are extracted in SQL scripts 1-4. Researchers in the LCP at MIT will be able to run script 0-phi-info.sql and extract a file with additional PHI info. All other researchers should ignore this script, and run scripts 1-3, to generate the callout data. 25 | 26 | Once scripts 1-3 have been executed, the data can be copied to file. To generate all the data, run the following in PostgreSQL: 27 | 28 | ```sql 29 | \cd /home/alistairewj/callout/ 30 | \i 1-define-cohort.sql 31 | \i 2-icu-variables.sql 32 | \i 3-final-design-matrix.sql 33 | copy (select * from dd_design_matrix order by icustay_id) to '/home/alistairewj/callout/callout.csv' CSV HEADER; 34 | ``` 35 | -------------------------------------------------------------------------------- /analysis/out-table-ddelay.csv: -------------------------------------------------------------------------------- 1 | "","","2.5 %","97.5 %","" 2 | "female",0.84,0.73,0.965,0.014 3 | "request_mrsa",1.615,1.332,1.949,0 4 | "request_vre",1.408,1.04,1.882,0.023 5 | "request_cdiff",1.695,1.267,2.241,0 6 | "as.factor(callout_month)2",0.989,0.711,1.377,0.949 7 | "as.factor(callout_month)3",0.5,0.34,0.727,0 8 | "as.factor(callout_month)4",0.672,0.462,0.971,0.036 9 | "as.factor(callout_month)5",0.7,0.49,0.996,0.048 10 | "as.factor(callout_month)6",0.623,0.438,0.882,0.008 11 | "as.factor(callout_month)7",0.88,0.621,1.245,0.472 12 | "as.factor(callout_month)8",1.137,0.825,1.569,0.433 13 | "as.factor(callout_month)9",1.272,0.929,1.746,0.135 14 | "as.factor(callout_month)10",1.084,0.789,1.495,0.619 15 | "as.factor(callout_month)11",1.076,0.779,1.489,0.658 16 | "as.factor(callout_month)12",0.58,0.4,0.836,0.004 17 | "as.factor(callout_year)2006",1.649,1.212,2.263,0.002 18 | "as.factor(callout_year)2007",0.91,0.666,1.252,0.555 19 | "as.factor(callout_year)2008",0.212,0.149,0.303,0 20 | "as.factor(callout_year)2009",0.249,0.172,0.36,0 21 | "as.factor(callout_year)2010",0.25,0.178,0.352,0 22 | "as.factor(callout_year)2011",0.188,0.131,0.271,0 23 | "as.factor(callout_dayofweek)monday ",0.635,0.481,0.834,0.001 24 | "as.factor(callout_dayofweek)saturday ",1.822,1.332,2.493,0 25 | "as.factor(callout_dayofweek)sunday ",1.554,1.126,2.143,0.007 26 | "as.factor(callout_dayofweek)thursday ",0.897,0.699,1.151,0.392 27 | "as.factor(callout_dayofweek)tuesday ",0.706,0.547,0.911,0.007 28 | "as.factor(callout_dayofweek)wednesday",0.616,0.477,0.796,0 29 | "as.factor(callout_wardid == 1)TRUE",0.513,0.358,0.751,0 30 | "cut2(PROPFULL_BEDS, c(0.9, 1))[0.900,1.000)",2.319,1.486,3.663,0 31 | "cut2(PROPFULL_BEDS, c(0.9, 1))[1.000,1.093]",3.349,1.908,5.851,0 32 | "relevel(cut2(hourofcallout2, c(7, 12, 19)), ""[ 7.000,12.000)"")[ 0.117, 7.000)",1.211,0.357,3.081,0.721 33 | "relevel(cut2(hourofcallout2, c(7, 12, 19)), ""[ 7.000,12.000)"")[12.000,19.000)",1.355,1.174,1.563,0 34 | "relevel(cut2(hourofcallout2, c(7, 12, 19)), ""[ 7.000,12.000)"")[19.000,23.867]",1.102,0.644,1.788,0.709 35 | "cut2(PROPFULL_BEDS, c(0.9, 1))[0.900,1.000)",3.505,2.713,1.122,0 36 | "cut2(PROPFULL_BEDS, c(0.9, 1))[1.000,1.093]",9.487,6.954,3.431,0 37 | -------------------------------------------------------------------------------- /callout-create-tables.py: -------------------------------------------------------------------------------- 1 | 2 | # coding: utf-8 3 | 4 | # In[ ]: 5 | 6 | # Import libraries 7 | import numpy as np 8 | import pandas as pd 9 | import matplotlib.pyplot as plt 10 | import re 11 | import psycopg2 12 | import sys 13 | 14 | from IPython.display import display, HTML # used to print out pretty pandas dataframes\n 15 | 16 | # querying oracle 17 | import cx_Oracle 18 | 19 | # prompt for password without echoing it 20 | import getpass 21 | 22 | def load_query(fn): 23 | # load SQL code which gets census info for patients 24 | with open(fn, 'r') as f: 25 | qry = f.read() 26 | 27 | 28 | # remove empty lines 29 | while '\n\n' in qry: 30 | qry = qry.replace('\n\n','\n') 31 | 32 | # remove semicolon at end 33 | qry = qry.rstrip('\n').rstrip(';') 34 | return qry 35 | 36 | 37 | # In[ ]: 38 | 39 | # configure the connections for both oracle and a local postgres database 40 | 41 | # PostgreSQL config which works on pc70 42 | sqluser = getpass.getuser() 43 | dbname = 'mimic' 44 | schema_name = 'mimiciii' 45 | 46 | 47 | print('PostgreSQL username: ' + sqluser) 48 | print('PostgreSQL database: ' + dbname) 49 | print('PostgreSQL schema: ' + schema_name) 50 | 51 | # Connect to local postgres version of mimic 52 | con = psycopg2.connect(dbname=dbname, user=sqluser) 53 | 54 | # Oracle which works on pc70 and connects to hera (need username/password) 55 | dbservice = 'MIMIC2' 56 | dbstring = 'localhost:3309/' + dbservice 57 | 58 | # connect to oracle **requires a tunnel in the background** - pc70 is configured to use port 3309 59 | # tunnel command: ssh -n -N -f -L 3309:localhost:1521 alistairewj@hera 60 | print('Oracle Username: ' + sqluser) 61 | db = cx_Oracle.connect(getpass.getuser() + '/' + getpass.getpass('Oracle Password: ') + '@' + dbstring) 62 | print('Oracle v' + db.version) 63 | 64 | 65 | # In[ ]: 66 | 67 | qry = load_query('sql/0-phi-info.sql') 68 | 69 | # execute query on database and get resultant rows 70 | cur = db.cursor() 71 | cur.execute(qry) 72 | data = cur.fetchall() 73 | 74 | # extract column names from description 75 | colNames = cur.description 76 | colNames = [x[0] for idx, x in enumerate(colNames)] 77 | cur.close() 78 | 79 | # save data in dataframe 80 | df_phi = pd.DataFrame.from_records(data, index=None, exclude=None, 81 | columns=colNames, 82 | coerce_float=False, nrows=None) 83 | 84 | # convert columns to lowercase - makes them consistent with postgresql 85 | df_phi.columns = [x.lower() for x in df_phi.columns] 86 | 87 | 88 | # In[ ]: 89 | 90 | # run all the SQL scripts for PostgreSQL - these generate materialized views 91 | cur = con.cursor() 92 | cur.execute('SET search_path to ' + schema_name) 93 | qry = load_query('sql/1-define-cohort.sql') 94 | cur.execute(qry) 95 | qry = load_query('sql/2-icu-variables.sql') 96 | cur.execute(qry) 97 | qry = load_query('sql/3-final-design-matrix.sql') 98 | cur.execute(qry) 99 | cur.execute('commit;') 100 | cur.close() 101 | 102 | # final query which actually pulls the data 103 | qry = """ 104 | select 105 | * 106 | from dd_design_matrix 107 | order by icustay_id 108 | """ 109 | df = pd.read_sql_query(qry,con) 110 | 111 | 112 | # We now have two dataframes: `df` with most of the callout data (extracted from a local PostgreSQL instance) and `df_phi` with the census related data (extracted from an Oracle instance which connects to hera). 113 | 114 | # In[ ]: 115 | 116 | # write the data to file 117 | df_phi.to_csv('callout-phi.csv',sep=',',na_rep='NA',header=True,index=False) 118 | df.to_csv('callout.csv',sep=',',na_rep='NA',header=True,index=False) 119 | 120 | 121 | # In[ ]: 122 | 123 | # print some stats 124 | print('Data size, normal: {}, PHI: {}'.format(df.shape[0],df_phi.shape[0])) 125 | if df.shape[0]==df_phi.shape[0]: 126 | print('Overlap in ICUSTAY_ID: {:2.2f}'.format( len(np.union1d(df['icustay_id'],df_phi['icustay_id'])) / df.shape[0] * 100.0 )) 127 | 128 | -------------------------------------------------------------------------------- /callout-create-tables.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "# Import libraries\n", 12 | "import numpy as np\n", 13 | "import pandas as pd\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import re\n", 16 | "import psycopg2\n", 17 | "import sys\n", 18 | "\n", 19 | "from IPython.display import display, HTML # used to print out pretty pandas dataframes\\n\n", 20 | "\n", 21 | "# querying oracle\n", 22 | "import cx_Oracle\n", 23 | "\n", 24 | "# prompt for password without echoing it\n", 25 | "import getpass\n", 26 | "\n", 27 | "def load_query(fn):\n", 28 | " # load SQL code which gets census info for patients\n", 29 | " with open(fn, 'r') as f:\n", 30 | " qry = f.read()\n", 31 | "\n", 32 | "\n", 33 | " # remove empty lines\n", 34 | " while '\\n\\n' in qry:\n", 35 | " qry = qry.replace('\\n\\n','\\n')\n", 36 | "\n", 37 | " # remove semicolon at end\n", 38 | " qry = qry.rstrip('\\n').rstrip(';')\n", 39 | " return qry\n" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": { 46 | "collapsed": false 47 | }, 48 | "outputs": [], 49 | "source": [ 50 | "# configure the connections for both oracle and a local postgres database\n", 51 | "\n", 52 | "# PostgreSQL config which works on pc70\n", 53 | "sqluser = getpass.getuser()\n", 54 | "dbname = 'mimic'\n", 55 | "schema_name = 'mimiciii'\n", 56 | "\n", 57 | "\n", 58 | "print('PostgreSQL username: ' + sqluser)\n", 59 | "print('PostgreSQL database: ' + dbname)\n", 60 | "print('PostgreSQL schema: ' + schema_name)\n", 61 | "\n", 62 | "# Connect to local postgres version of mimic\n", 63 | "con = psycopg2.connect(dbname=dbname, user=sqluser)\n", 64 | "\n", 65 | "# Oracle which works on pc70 and connects to hera (need username/password)\n", 66 | "dbservice = 'MIMIC2'\n", 67 | "dbstring = 'localhost:3309/' + dbservice\n", 68 | "\n", 69 | "# connect to oracle **requires a tunnel in the background** - pc70 is configured to use port 3309\n", 70 | "# tunnel command: ssh -n -N -f -L 3309:localhost:1521 alistairewj@hera\n", 71 | "print('Oracle Username: ' + sqluser)\n", 72 | "db = cx_Oracle.connect(getpass.getuser() + '/' + getpass.getpass('Oracle Password: ') + '@' + dbstring)\n", 73 | "print('Oracle v' + db.version)" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": { 80 | "collapsed": false 81 | }, 82 | "outputs": [], 83 | "source": [ 84 | "qry = load_query('sql/0-phi-info.sql')\n", 85 | "\n", 86 | "# execute query on database and get resultant rows\n", 87 | "cur = db.cursor()\n", 88 | "cur.execute(qry)\n", 89 | "data = cur.fetchall()\n", 90 | " \n", 91 | "# extract column names from description\n", 92 | "colNames = cur.description\n", 93 | "colNames = [x[0] for idx, x in enumerate(colNames)]\n", 94 | "cur.close()\n", 95 | "\n", 96 | "# save data in dataframe\n", 97 | "df_phi = pd.DataFrame.from_records(data, index=None, exclude=None,\n", 98 | " columns=colNames,\n", 99 | " coerce_float=False, nrows=None)\n", 100 | "\n", 101 | "# convert columns to lowercase - makes them consistent with postgresql\n", 102 | "df_phi.columns = [x.lower() for x in df_phi.columns]" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": { 109 | "collapsed": false 110 | }, 111 | "outputs": [], 112 | "source": [ 113 | "# run all the SQL scripts for PostgreSQL - these generate materialized views\n", 114 | "cur = con.cursor()\n", 115 | "cur.execute('SET search_path to ' + schema_name)\n", 116 | "qry = load_query('sql/1-define-cohort.sql')\n", 117 | "cur.execute(qry)\n", 118 | "qry = load_query('sql/2-icu-variables.sql')\n", 119 | "cur.execute(qry)\n", 120 | "qry = load_query('sql/3-final-design-matrix.sql')\n", 121 | "cur.execute(qry)\n", 122 | "cur.execute('commit;')\n", 123 | "cur.close()\n", 124 | "\n", 125 | "# final query which actually pulls the data\n", 126 | "qry = \\\n", 127 | "\"\"\"\n", 128 | "select \n", 129 | " *\n", 130 | "from dd_design_matrix\n", 131 | "order by icustay_id\n", 132 | "\"\"\"\n", 133 | "df = pd.read_sql_query(qry,con)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "We now have two dataframes: `df` with most of the callout data (extracted from a local PostgreSQL instance) and `df_phi` with the census related data (extracted from an Oracle instance which connects to hera)." 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": { 147 | "collapsed": false 148 | }, 149 | "outputs": [], 150 | "source": [ 151 | "# write the data to file\n", 152 | "df_phi.to_csv('callout-phi.csv',sep=',',na_rep='NA',header=True,index=False)\n", 153 | "df.to_csv('callout.csv',sep=',',na_rep='NA',header=True,index=False)" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "metadata": { 160 | "collapsed": false 161 | }, 162 | "outputs": [], 163 | "source": [ 164 | "# print some stats\n", 165 | "print('Data size, normal: {}, PHI: {}'.format(df.shape[0],df_phi.shape[0]))\n", 166 | "if df.shape[0]==df_phi.shape[0]:\n", 167 | " print('Overlap in ICUSTAY_ID: {:2.2f}'.format( len(np.union1d(df['icustay_id'],df_phi['icustay_id'])) / df.shape[0] * 100.0 ))" 168 | ] 169 | } 170 | ], 171 | "metadata": { 172 | "kernelspec": { 173 | "display_name": "Python 2", 174 | "language": "python", 175 | "name": "python2" 176 | }, 177 | "language_info": { 178 | "codemirror_mode": { 179 | "name": "ipython", 180 | "version": 2 181 | }, 182 | "file_extension": ".py", 183 | "mimetype": "text/x-python", 184 | "name": "python", 185 | "nbconvert_exporter": "python", 186 | "pygments_lexer": "ipython2", 187 | "version": "2.7.9" 188 | } 189 | }, 190 | "nbformat": 4, 191 | "nbformat_minor": 0 192 | } 193 | -------------------------------------------------------------------------------- /sql/3-final-design-matrix.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS dd_design_matrix; 2 | CREATE TABLE dd_design_matrix as 3 | select 4 | co.icustay_id 5 | 6 | -- lengths of stay 7 | , DISCHARGEDELAY_DAYS 8 | , LOS_HOSPITAL_DAYS -- admittime to dischtime 9 | 10 | , (LOS_HOSPITAL_DAYS - LOS_POST_ICU_DAYS - LOS_ICU_DAYS) as LOS_PREICU_DAYS 11 | , LOS_ICU_DAYS -- intime to outcometime 12 | , LOS_POST_ICU_DAYS -- outcometime to dischtime 13 | , LOS_PRE_CALLOUT_DAYS -- from hospital admit to callout (admittime to callouttime) 14 | , LOS_POST_CALLOUT_DAYS -- from callout to hospital discharge (callouttime to dischtime) 15 | 16 | -- censor delay 17 | , round(extract(EPOCH FROM ( 18 | case 19 | when co.deathtime is null and readm.intime is null then null 20 | when readm.intime < co.deathtime then readm.intime 21 | when co.deathtime <= readm.intime then co.deathtime 22 | else coalesce(co.deathtime, readm.intime) 23 | end 24 | - co.callouttime))::NUMERIC /60/60/24, 4) as LOS_POST_CALLOUT_CENSORED_DAYS 25 | 26 | -- things that censor outcome: time of death, time of readmission 27 | , case when co.deathtime is not null then 1 else 0 end as HospitalDeath 28 | , case when readm.icustay_id is not null then 1 else 0 end as HospitalReadmission 29 | 30 | -- -- censor time 31 | -- , case 32 | -- when co.deathtime is null and readm.intime is null then null 33 | -- when readm.intime < co.deathtime then readm.intime 34 | -- when co.deathtime <= readm.intime then co.deathtime 35 | -- else coalesce(co.deathtime, readm.intime) 36 | -- end as censortime 37 | 38 | 39 | -- various factors which could cause call out delays 40 | , request_tele, request_resp, request_cdiff, request_mrsa, request_vre 41 | , case when co.gender = 'F' then 1 else 0 end as female 42 | , oa.oasis 43 | , elixhauser_hospital 44 | 45 | -- tr.CURR_CAREUNIT is only null for 1 patient 46 | -- the patient was definitely in the MICU, and this code ensures no missing data ! 47 | , case 48 | when tr.CURR_CAREUNIT is not null 49 | and tr.CURR_CAREUNIT = 'CCU' then 1 50 | when tr.CURR_CAREUNIT is null 51 | and ie.LAST_CAREUNIT = 'CCU' then 1 52 | else 0 end as CCU 53 | , case 54 | when tr.CURR_CAREUNIT is not null 55 | and tr.CURR_CAREUNIT = 'CVICU' then 1 56 | when tr.CURR_CAREUNIT is null 57 | and ie.LAST_CAREUNIT = 'CVICU' then 1 58 | else 0 end as CVICU 59 | , case 60 | when tr.CURR_CAREUNIT is not null 61 | and tr.CURR_CAREUNIT = 'MICU' then 1 62 | when tr.CURR_CAREUNIT is null 63 | and ie.LAST_CAREUNIT = 'MICU' then 1 64 | else 0 end as MICU 65 | , case 66 | when tr.CURR_CAREUNIT is not null 67 | and tr.CURR_CAREUNIT = 'MSICU' then 1 68 | when tr.CURR_CAREUNIT is null 69 | and ie.LAST_CAREUNIT = 'MSICU' then 1 70 | else 0 end as MSICU 71 | , case 72 | when tr.CURR_CAREUNIT is not null 73 | and tr.CURR_CAREUNIT = 'SICU' then 1 74 | when tr.CURR_CAREUNIT is null 75 | and ie.LAST_CAREUNIT = 'SICU' then 1 76 | else 0 end as SICU 77 | , case 78 | when tr.CURR_CAREUNIT is not null 79 | and tr.CURR_CAREUNIT = 'TSICU' then 1 80 | when tr.CURR_CAREUNIT is null 81 | and ie.LAST_CAREUNIT = 'TSICU' then 1 82 | else 0 end as TSICU 83 | 84 | 85 | -- if no info given.. assume full code 86 | , case 87 | when cmo_last=0 88 | and dni_last=0 89 | and dncpr_last=0 90 | and dnr_last=0 91 | and fullcode_last=0 then 0 92 | else fullcode_last 93 | end as fullcode_last 94 | , dni_last, dncpr_last, dnr_last 95 | 96 | , HourOfCallout 97 | 98 | 99 | -- the rest of the info could be useful so we include it 100 | , age 101 | , ethnicity 102 | , elective 103 | , case when tr.CURR_CAREUNIT is not null then tr.CURR_CAREUNIT 104 | when ie.LAST_CAREUNIT is not null then ie.LAST_CAREUNIT 105 | else null end 106 | as careunit 107 | 108 | , s.service 109 | 110 | -- if no info given.. assume full code 111 | , case 112 | when cmo=0 113 | and dni=0 114 | and dncpr=0 115 | and dnr=0 116 | and fullcode=0 then 0 117 | else fullcode end 118 | as fullcode 119 | , dni, dncpr, dnr 120 | 121 | , co.callout_wardid -- used for binary flag FirstAvailWard "Yes/No" (callout_wardid=1 is a request for first available ward) 122 | , co.discharge_wardid 123 | , to_char(co.callouttime, 'day') as CALLOUT_DAYOFWEEK 124 | 125 | from dd_cohort co 126 | 127 | -- readmitted before discharged 128 | left join 129 | ( 130 | select co.icustay_id, tr.intime 131 | , ROW_NUMBER() OVER (partition by tr.hadm_id order by tr.intime) as rn 132 | from dd_cohort co 133 | left join mimiciii.transfers tr 134 | on co.hadm_id = tr.hadm_id 135 | -- only ICU stays which occurred *after* this ICU's discharge 136 | and tr.icustay_id is not null and tr.intime > co.outcometime 137 | where tr.intime is not null -- only readmitted patients 138 | ) readm 139 | on co.icustay_id = readm.icustay_id and readm.rn = 1 140 | 141 | -- severity of illness 142 | left join dd_oasis oa 143 | on co.icustay_id = oa.icustay_id 144 | 145 | -- care unit 146 | left join mimiciii.transfers tr 147 | on co.icustay_id = tr.icustay_id and co.callouttime between tr.intime and tr.outtime 148 | 149 | -- we join to this table to fix the care unit for literally 1 ICU patient 150 | -- they were called out when the transfer table had them sitting in a ward for an hour, between two stays in MICU 151 | left join mimiciii.icustays ie 152 | on co.icustay_id = ie.icustay_id 153 | 154 | -- service 155 | left join 156 | ( 157 | select co.icustay_id 158 | , coalesce(s1.only_service, s2.curr_service) as service 159 | , ROW_NUMBER() over (PARTITION BY CO.ICUSTAY_ID ORDER BY s2.transfertime DESC) as rn 160 | 161 | from dd_cohort co 162 | left join 163 | ( 164 | select hadm_id, max(curr_service) as only_service 165 | from mimiciii.services 166 | group by hadm_id 167 | having count(*)=1 168 | ) s1 169 | on co.hadm_id = s1.hadm_id 170 | left join mimiciii.services s2 171 | on co.hadm_id = s2.hadm_id and s2.transfertime < co.callouttime - interval '4' hour 172 | ) s 173 | on co.icustay_id = s.icustay_id and s.rn = 1 174 | left join 175 | ( 176 | select e.*, 177 | -- in-hospital mortality score 178 | CONGESTIVE_HEART_FAILURE*(4) + CARDIAC_ARRHYTHMIAS*(4) + VALVULAR_DISEASE*(-3) + PULMONARY_CIRCULATION*(0) + 179 | PERIPHERAL_VASCULAR *(0) + HYPERTENSION*(-1) + PARALYSIS*(0) + 180 | OTHER_NEUROLOGICAL *(7) + CHRONIC_PULMONARY*(0) + 181 | DIABETES_UNCOMPLICATED *(-1) + DIABETES_COMPLICATED*(-4) + 182 | HYPOTHYROIDISM *(0) + RENAL_FAILURE*(3) + LIVER_DISEASE*(4) + 183 | PEPTIC_ULCER *(-9) + AIDS*(0) + LYMPHOMA*(7) + 184 | METASTATIC_CANCER *(9) + SOLID_TUMOR*(0) + RHEUMATOID_ARTHRITIS*(0) + 185 | COAGULOPATHY*(3) + OBESITY*(-5) + WEIGHT_LOSS*(4) + FLUID_ELECTROLYTE*(6) + BLOOD_LOSS_ANEMIA*(0) + 186 | DEFICIENCY_ANEMIAS *(-4) + ALCOHOL_ABUSE*(0) + DRUG_ABUSE*(-6) + 187 | PSYCHOSES *(-5) + DEPRESSION*(-8) 188 | AS elixhauser_hospital 189 | from elixhauser_ahrq e 190 | ) eli 191 | on co.hadm_id = eli.hadm_id 192 | 193 | order by icustay_id; 194 | -------------------------------------------------------------------------------- /sql/1-define-cohort.sql: -------------------------------------------------------------------------------- 1 | -- This query creates the cohort used for the callout project ("Discharge Devils") 2 | -- The query requires the following materalized views/tables: 3 | -- ventdurations - created by ventilation-durations.sql 4 | 5 | drop materialized view IF EXISTS dd_cohort_all CASCADE; 6 | create materialized view dd_cohort_all as 7 | with ch as ( 8 | select ie.subject_id, ie.hadm_id, ie.icustay_id 9 | , case 10 | when adm.deathtime is not null then 'Y' 11 | else 'N' end 12 | as hospital_expire_flag 13 | 14 | -- ICU admission/discharge times 15 | , ie.intime, ie.outtime 16 | 17 | -- Hospital admission/discharge times 18 | , adm.admittime, adm.dischtime 19 | 20 | -- Patient death time 21 | 22 | -- fixes ~10 typos in death date which likely don't even show up in the cohort 23 | , case 24 | when adm.deathtime is not null 25 | then adm.dischtime 26 | else null 27 | end as deathtime 28 | 29 | -- Patient's age on ICU admission 30 | , round( (cast(ie.intime as date) - cast(pat.dob as date)) /365.242,4) as Age 31 | , pat.gender 32 | , adm.ethnicity 33 | 34 | , case 35 | when adm.ADMISSION_TYPE = 'ELECTIVE' then 1 36 | else 0 37 | end as Elective 38 | 39 | , adm.has_chartevents_data -- hospital admission with data 40 | 41 | -- not a child 42 | , case 43 | when round( (cast(ie.intime as date) - cast(pat.dob as date)) /365.242,4) > 15 44 | then 1 45 | else 0 46 | end as adult 47 | 48 | -- An integer which is 1 for the first ICU stay of the hospitalization 49 | -- Increases by 1 for each subsequent ICU admission 50 | , row_number() over (partition by ie.subject_id, ie.hadm_id order by ie.intime) as icustay_num 51 | from mimiciii.icustays ie 52 | inner join mimiciii.admissions adm 53 | on ie.hadm_id = adm.hadm_id 54 | inner join mimiciii.patients pat 55 | on ie.subject_id = pat.subject_id 56 | ) 57 | , ca as ( 58 | select ch.* 59 | -- callout variables we are interested in 60 | , ca.curr_careunit 61 | , ca.request_tele, ca.request_resp, ca.request_cdiff, ca.request_mrsa, ca.request_vre 62 | , ca.createtime as callouttime 63 | , ca.outcometime 64 | , ca.firstreservationtime 65 | , ca.currentreservationtime 66 | , ca.callout_wardid 67 | , ca.discharge_wardid 68 | , ca.callout_outcome 69 | , ca.callout_status 70 | , coalesce(vent.mechvent,0) as MechVent 71 | , case when ca.subject_id is not null then 1 else 0 end as has_callout_data 72 | -- If a single ICUSTAY_ID has multiple successful callouts, we can consider these as readmissions within 24 hours 73 | -- ICUSTAY_ID will not capture a readmission within 24 hours, so we use the call out discharge time instead 74 | , row_number() over (partition by ch.icustay_id order by ca.createtime) as callout_num 75 | from ch 76 | left join mimiciii.callout ca 77 | on ch.subject_id = ca.subject_id and ch.hadm_id = ca.hadm_id 78 | and ca.createtime between ch.intime and ch.outtime 79 | and callout_outcome = 'Discharged' 80 | and callout_status = 'Inactive' 81 | left join 82 | ( 83 | select ie.icustay_id 84 | , max(case when vent.icustay_id is not null then 1 else 0 end) as MechVent 85 | from icustays ie 86 | left join ventdurations vent 87 | on ie.icustay_id = vent.icustay_id 88 | and vent.starttime between ie.intime - interval '1' day and ie.intime + interval '1' day 89 | group by ie.icustay_id 90 | ) vent 91 | on ch.icustay_id = vent.icustay_id 92 | ) 93 | -- extract code status at that time 94 | , codestatus1 as 95 | ( 96 | select 97 | ca.icustay_id 98 | , case when value = 'Comfort Measures' then 1 else 0 end as CMO 99 | , case when value = 'Do Not Intubate' then 1 else 0 end as DNI 100 | , case when substr(value,1,16) = 'CPR Not Indicate' then 1 else 0 end as DNCPR 101 | , case when substr(value,1,16) = 'Do Not Resuscita' then 1 else 0 end as DNR 102 | , case when value = 'Full Code' then 1 else 0 end as FULLCODE 103 | 104 | , ROW_NUMBER() OVER (PARTITION BY ca.icustay_id ORDER BY ce.charttime DESC) as rn 105 | 106 | from ca 107 | left join mimiciii.chartevents ce 108 | on ca.icustay_id = ce.icustay_id 109 | and ce.charttime between ca.intime and ca.callouttime 110 | and ce.itemid in (128,223758) 111 | ) 112 | , codestatus2 as 113 | ( 114 | select icustay_id 115 | , MAX(CMO) as CMO 116 | , MAX(DNI) as DNI 117 | , MAX(DNCPR) as DNCPR 118 | , MAX(DNR) as DNR 119 | , MAX(FULLCODE) as FULLCODE 120 | 121 | , MAX(case when rn = 1 then CMO else null end) as CMO_LAST 122 | , MAX(case when rn = 1 then DNI else null end) as DNI_LAST 123 | , MAX(case when rn = 1 then DNCPR else null end) as DNCPR_LAST 124 | , MAX(case when rn = 1 then DNR else null end) as DNR_LAST 125 | , MAX(case when rn = 1 then FULLCODE else null end) as FULLCODE_LAST 126 | FROM codestatus1 127 | group by icustay_id 128 | ) 129 | select ca.* 130 | -- code status variables 131 | , cs.cmo, cs.cmo_last 132 | , cs.dni, cs.dni_last 133 | , cs.dncpr, cs.dncpr_last 134 | , cs.dnr, cs.dnr_last 135 | , cs.fullcode, cs.fullcode_last 136 | 137 | -- actual HHMM of the ICU discharge time 138 | , EXTRACT(HOUR FROM cast(ca.outcometime as timestamp))*100 + EXTRACT(MINUTE FROM cast(ca.outcometime as timestamp)) as HourOfDischarge 139 | , EXTRACT(HOUR FROM cast(ca.callouttime as timestamp))*100 + EXTRACT(MINUTE FROM cast(ca.callouttime as timestamp)) as HourOfCallout 140 | , round(extract(epoch from (ca.outcometime - ca.callouttime))::NUMERIC/60.0/60.0/24.0,4) as DISCHARGEDELAY_DAYS 141 | , round(extract(epoch from (ca.outcometime - ca.intime))::NUMERIC/60.0/60.0/24.0,4) as LOS_ICU_DAYS 142 | , round(extract(epoch from (ca.intime - ca.admittime))::NUMERIC/60.0/60.0/24.0,4) as LOS_PREICU_DAYS 143 | , round(extract(epoch from (ca.dischtime - ca.admittime))::NUMERIC/60.0/60.0/24.0,4) as LOS_HOSPITAL_DAYS 144 | , round(extract(epoch from (ca.dischtime - ca.callouttime))::NUMERIC/60.0/60.0/24.0,4) as LOS_POST_CALLOUT_DAYS 145 | , round(extract(epoch from (ca.dischtime - ca.outcometime))::NUMERIC/60.0/60.0/24.0,4) as LOS_POST_ICU_DAYS 146 | , round(extract(epoch from (ca.callouttime - ca.admittime))::NUMERIC/60.0/60.0/24.0,4) as LOS_PRE_CALLOUT_DAYS 147 | from ca 148 | left join codestatus2 cs 149 | on ca.icustay_id = cs.icustay_id 150 | where callout_num = 1; 151 | 152 | 153 | 154 | DROP MATERIALIZED VIEW IF EXISTS DD_COHORT CASCADE; 155 | CREATE MATERIALIZED VIEW DD_COHORT AS 156 | select 157 | dd.* 158 | from dd_cohort_all dd 159 | where 160 | icustay_num = 1 161 | and adult = 1 162 | and has_chartevents_data = 1 163 | and has_callout_data = 1 164 | and coalesce(cmo,0) != 1 165 | and coalesce(cmo_last,0) != 1; 166 | 167 | -- print counts 168 | select 169 | count(*) as num_pat 170 | , sum(case when adult = 1 171 | then 1 else 0 end) as adults 172 | , sum(case when adult = 1 173 | and has_callout_data = 1 174 | then 1 else 0 end) as has_callout 175 | , sum(case when adult = 1 176 | and has_callout_data = 1 177 | and icustay_num = 1 178 | then 1 else 0 end) as first_stay 179 | , sum(case when adult = 1 180 | and has_callout_data = 1 181 | and icustay_num = 1 182 | and coalesce(cmo,0) != 1 and coalesce(cmo_last,0) != 1 183 | then 1 else 0 end) as never_cmo 184 | , sum(case when adult = 1 185 | and has_callout_data = 1 186 | and icustay_num = 1 187 | and coalesce(cmo,0) != 1 and coalesce(cmo_last,0) != 1 188 | and has_chartevents_data = 1 189 | then 1 else 0 end) as no_missing_data 190 | from dd_cohort_all dd; 191 | -------------------------------------------------------------------------------- /sql/0-phi-info.sql: -------------------------------------------------------------------------------- 1 | -- Information that contains PHI (i.e. dates, census info) 2 | -- These queries must be run in Oracle connected to the PHI database 3 | 4 | -- step 1) define dd_cohort exactly as it is done in 1-define-cohort.sql 5 | with ch as ( 6 | select ie.subject_id, ie.hadm_id, ie.icustay_id 7 | -- ICU admission/discharge times 8 | , ie.intime, ie.outtime 9 | 10 | -- exclusions 11 | , case -- not a child 12 | when round( (cast(ie.intime as date) - cast(pat.dob as date)) /365.242,4) > 15 13 | then 1 14 | else 0 15 | end as adult 16 | , adm.has_chartevents_data -- hospital admission with data 17 | 18 | -- Age at ICU admission 19 | , case when round( (cast(ie.intime as date) - cast(pat.dob as date))/365.242,4 ) > 200 then 91.4 20 | else round( (cast(ie.intime as date) - cast(pat.dob as date))/365.242,4 ) end as age 21 | 22 | -- An integer which is 1 for the first ICU stay of the hospitalization 23 | -- Increases by 1 for each subsequent ICU admission 24 | , row_number() over (partition by ie.subject_id, ie.hadm_id order by ie.intime) as icustay_num 25 | 26 | from mimiciii_phi.icustays ie 27 | inner join mimiciii_phi.admissions adm 28 | on ie.hadm_id = adm.hadm_id 29 | inner join mimiciii_phi.patients pat 30 | on ie.subject_id = pat.subject_id 31 | ) 32 | , ca as ( 33 | select ch.subject_id, ch.hadm_id, ch.icustay_id 34 | , ch.intime, ch.outtime 35 | , ch.age 36 | 37 | -- callout variables we are interested in 38 | , ca.createtime as callouttime 39 | , ca.outcometime 40 | 41 | , ca.callout_wardid 42 | , ca.discharge_wardid 43 | 44 | -- exclusions 45 | , ch.adult 46 | , ch.has_chartevents_data 47 | , ch.icustay_num 48 | , case when ca.subject_id is not null then 1 else 0 end as has_callout_data 49 | 50 | -- If a single ICUSTAY_ID has multiple successful callouts, we can consider these as readmissions within 24 hours 51 | -- ICUSTAY_ID will not capture a readmission within 24 hours, so we use the call out discharge time instead 52 | , row_number() over (partition by ch.icustay_id order by ca.createtime) as callout_num 53 | from ch 54 | left join mimiciii_phi.callout ca 55 | on ch.subject_id = ca.subject_id and ch.hadm_id = ca.hadm_id 56 | and ca.createtime between ch.intime and ch.outtime 57 | and callout_outcome = 'Discharged' 58 | and callout_status = 'Inactive' 59 | ) 60 | -- extract code status at that time 61 | , codestatus1 as 62 | ( 63 | select 64 | ca.icustay_id 65 | , case when value = 'Comfort Measures' then 1 else 0 end as CMO 66 | , case when value = 'Do Not Intubate' then 1 else 0 end as DNI 67 | , case when substr(value,1,16) = 'CPR Not Indicate' then 1 else 0 end as DNCPR 68 | , case when substr(value,1,16) = 'Do Not Resuscita' then 1 else 0 end as DNR 69 | , case when value = 'Full Code' then 1 else 0 end as FULLCODE 70 | 71 | , ROW_NUMBER() OVER (PARTITION BY ca.icustay_id ORDER BY ce.charttime DESC) as rn 72 | 73 | from ca 74 | left join mimiciii_phi.chartevents ce 75 | on ca.icustay_id = ce.icustay_id 76 | and ce.charttime between ca.intime and ca.callouttime 77 | and ce.itemid in (128,223758) 78 | ) 79 | , codestatus2 as 80 | ( 81 | select icustay_id 82 | , MAX(CMO) as CMO 83 | , MAX(DNI) as DNI 84 | , MAX(DNCPR) as DNCPR 85 | , MAX(DNR) as DNR 86 | , MAX(FULLCODE) as FULLCODE 87 | 88 | , MAX(case when rn = 1 then CMO else null end) as CMO_LAST 89 | , MAX(case when rn = 1 then DNI else null end) as DNI_LAST 90 | , MAX(case when rn = 1 then DNCPR else null end) as DNCPR_LAST 91 | , MAX(case when rn = 1 then DNR else null end) as DNR_LAST 92 | , MAX(case when rn = 1 then FULLCODE else null end) as FULLCODE_LAST 93 | FROM codestatus1 94 | group by icustay_id 95 | ) 96 | -- the below should be identical to the cohort defined in 1-define-cohort.sql 97 | -- .. except, of course, it has fewer columns 98 | , dd as 99 | ( 100 | select ca.* 101 | from ca 102 | left join codestatus2 cs 103 | on ca.icustay_id = cs.icustay_id 104 | where icustay_num = 1 105 | and adult = 1 106 | and has_chartevents_data = 1 107 | and has_callout_data = 1 108 | and coalesce(cmo,0) != 1 109 | and coalesce(cmo_last,0) != 1 110 | and callout_num = 1 111 | ) 112 | ------------------------ 113 | -- ADD IN CENSUS DATA -- 114 | ------------------------ 115 | , t1 as 116 | ( 117 | select 118 | census_date 119 | , sum(case when cost_center_desc in ('CC6A - Med/Surg/Trauma', 'Acute Surgical/Trauma') then CENSUS else null end) as MedSurgTrauma_CENSUS 120 | , sum(case when cost_center_desc in ('CC6A - Med/Surg/Trauma', 'Acute Surgical/Trauma') then BEDS else null end) as MedSurgTrauma_BEDS 121 | , sum(case when cost_center_desc in ('RS12 - Med/Surg/GYN ', 'Medical/ Surgical (Gynecology) Inpatient Unit') then CENSUS else null end) as MedSurgGynecology_CENSUS 122 | , sum(case when cost_center_desc in ('RS12 - Med/Surg/GYN ', 'Medical/ Surgical (Gynecology) Inpatient Unit') then BEDS else null end) as MedSurgGynecology_BEDS 123 | , sum(case when cost_center_desc in ('RS11 - Hem/Onc', 'FD7 - Hem/Onc', 'Hematology /Oncology Inpatient Unit' ) then CENSUS else null end) as HemOnc_CENSUS 124 | , sum(case when cost_center_desc in ('RS11 - Hem/Onc', 'FD7 - Hem/Onc', 'Hematology /Oncology Inpatient Unit' ) then BEDS else null end) as HemOnc_BEDS 125 | , sum(case when cost_center_desc in ('FA10 - Transplant', 'Transplant Unit') then CENSUS else null end) as TransplantUnit_CENSUS 126 | , sum(case when cost_center_desc in ('FA10 - Transplant', 'Transplant Unit') then BEDS else null end) as TransplantUnit_BEDS 127 | , sum(case when cost_center_desc in ('FA11 - Neuro', 'Neurology/ Neurosurgery') then CENSUS else null end) as Neuro_CENSUS 128 | , sum(case when cost_center_desc in ('FA11 - Neuro', 'Neurology/ Neurosurgery') then BEDS else null end) as Neuro_BEDS 129 | , sum(case when cost_center_desc in ('FA3 - Cardiology/Medicine', 'Cardiology/ Medical Inpatient Unit') then CENSUS else null end) as CardiologyMed_CENSUS 130 | , sum(case when cost_center_desc in ('FA3 - Cardiology/Medicine', 'Cardiology/ Medical Inpatient Unit') then BEDS else null end) as CardiologyMed_BEDS 131 | , sum(case when cost_center_desc in ('FA5 - Vascular', 'Vascular') then CENSUS else null end) as Vascular_CENSUS 132 | , sum(case when cost_center_desc in ('FA5 - Vascular', 'Vascular') then BEDS else null end) as Vascular_BEDS 133 | , sum(case when cost_center_desc in ('FA6A - Cardiac Surgery ', 'Cardiac Surgery') then CENSUS else null end) as CardiacSurgery_CENSUS 134 | , sum(case when cost_center_desc in ('FA6A - Cardiac Surgery ', 'Cardiac Surgery') then BEDS else null end) as CardiacSurgery_BEDS 135 | , sum(case when cost_center_desc in ('CC7A - Medicine ', 'FA2 - Medicine', 'Medicine Inpatient Unit', 'Medicine' , 'ST5 - Medicine ') then CENSUS else null end) as Medicine_CENSUS 136 | , sum(case when cost_center_desc in ('CC7A - Medicine ', 'FA2 - Medicine', 'Medicine Inpatient Unit', 'Medicine' , 'ST5 - Medicine ') then BEDS else null end) as Medicine_BEDS 137 | , sum(case when cost_center_desc in ('FA7 - Med/Surg', 'ST7 - Med/Surg ', 'Med/ Surg') then CENSUS else null end) as MedSurg_CENSUS 138 | , sum(case when cost_center_desc in ('FA7 - Med/Surg', 'ST7 - Med/Surg ', 'Med/ Surg') then BEDS else null end) as MedSurg_BEDS 139 | , sum(case when cost_center_desc in ('Surgery/ PancreaticBiliary/Bariatric', 'FA9 - Med/Sug') then CENSUS else null end) as MedSurgPancrBiliary_CENSUS 140 | , sum(case when cost_center_desc in ('Surgery/ PancreaticBiliary/Bariatric', 'FA9 - Med/Sug') then BEDS else null end) as MedSurgPancrBiliary_BEDS 141 | 142 | , sum(case when cost_center_desc = 'Emergency Department' then CENSUS else null end) as EmergencyDepartment_CENSUS 143 | , sum(case when cost_center_desc = 'Emergency Department' then BEDS else null end) as EmergencyDepartment_BEDS 144 | , sum(case when cost_center_desc = 'Labor & Delivery' then CENSUS else null end) as LaborDelivery_CENSUS 145 | , sum(case when cost_center_desc = 'Labor & Delivery' then BEDS else null end) as LaborDelivery_BEDS 146 | , sum(case when cost_center_desc = 'Medical/ Cardiology' then CENSUS else null end) as MedicalCardiology_CENSUS 147 | , sum(case when cost_center_desc = 'Medical/ Cardiology' then BEDS else null end) as MedicalCardiology_BEDS 148 | , sum(case when cost_center_desc = 'Obstetrics (Postpartum & Antepartum) Inpatient Unit' then CENSUS else null end) as Obstetrics_CENSUS 149 | , sum(case when cost_center_desc = 'Obstetrics (Postpartum & Antepartum) Inpatient Unit' then BEDS else null end) as Obstetrics_BEDS 150 | , sum(case when cost_center_desc = 'PACU - West' then CENSUS else null end) as PACUWest_CENSUS 151 | , sum(case when cost_center_desc = 'PACU - West' then BEDS else null end) as PACUWest_BEDS 152 | , sum(case when cost_center_desc = 'Thoracic Surgery' then CENSUS else null end) as ThoracicSurgery_CENSUS 153 | , sum(case when cost_center_desc = 'Thoracic Surgery' then BEDS else null end) as ThoracicSurgery_BEDS 154 | , sum(case when cost_center_desc = 'Vascular stepdown' then CENSUS else null end) as VascularStepdown_CENSUS 155 | , sum(case when cost_center_desc = 'Vascular stepdown' then BEDS else null end) as VascularStepdown_BEDS 156 | 157 | from 158 | ( 159 | select census_date, floor_name 160 | , census, beds, other 161 | , cc.floor, cc.type 162 | , cc.cost_center_desc 163 | from mimiciii_phi.a_census_unpivot cen 164 | left join MIMICIII_PHI.A_COST_CENTER cc 165 | on cen.census_date = cc.activity_dt 166 | and upper(trim(cen.floor_name)) = upper(trim(cc.floor)) 167 | ) cen 168 | group by census_date 169 | ) 170 | -- create table 2, which defines whether a ward is a member of a particular cost center on a given day 171 | , t2 as 172 | ( 173 | select 174 | activity_dt 175 | , upper(trim(floor)) as FLOOR 176 | , type 177 | , case 178 | -- below cost centers are merged together as the name changes are mostly cosmetic 179 | -- i.e., the ward name was added to the cost center, etc 180 | when cost_center_desc in ('CC6A - Med/Surg/Trauma', 'Acute Surgical/Trauma') then 'MedSurgTrauma' 181 | when cost_center_desc in ('RS12 - Med/Surg/GYN ', 'Medical/ Surgical (Gynecology) Inpatient Unit') then 'MedSurgGynecology' 182 | when cost_center_desc in ('RS11 - Hem/Onc', 'FD7 - Hem/Onc', 'Hematology /Oncology Inpatient Unit' ) then 'HemOnc' 183 | when cost_center_desc in ('FA10 - Transplant', 'Transplant Unit') then 'TransplantUnit' 184 | when cost_center_desc in ('FA11 - Neuro', 'Neurology/ Neurosurgery') then 'Neuro' 185 | when cost_center_desc in ('FA3 - Cardiology/Medicine', 'Cardiology/ Medical Inpatient Unit') then 'CardiologyMed' 186 | when cost_center_desc in ('FA5 - Vascular', 'Vascular') then 'Vascular' 187 | when cost_center_desc in ('FA6A - Cardiac Surgery ', 'Cardiac Surgery') then 'CardiacSurgery' 188 | when cost_center_desc in ('CC7A - Medicine ', 'FA2 - Medicine', 'Medicine Inpatient Unit', 'Medicine' , 'ST5 - Medicine ') then 'Medicine' 189 | when cost_center_desc in ('FA7 - Med/Surg', 'ST7 - Med/Surg ', 'Med/ Surg') then 'MedSurg' 190 | when cost_center_desc in ('Surgery/ PancreaticBiliary/Bariatric', 'FA9 - Med/Sug') then 'MedSurgPancrBiliary' 191 | 192 | -- below cost centers aren't merged, but their names are changed to match the above column names 193 | when cost_center_desc = 'Emergency Department' then 'EmergencyDepartment' 194 | when cost_center_desc = 'Labor & Delivery' then 'LaborDelivery' 195 | when cost_center_desc = 'Medical/ Cardiology' then 'MedicalCardiology' 196 | when cost_center_desc = 'Obstetrics (Postpartum & Antepartum) Inpatient Unit' then 'Obstetrics' 197 | when cost_center_desc = 'PACU - West' then 'PACUWest' 198 | when cost_center_desc = 'Thoracic Surgery' then 'ThoracicSurgery' 199 | when cost_center_desc = 'Vascular stepdown' then 'VascularStepdown' 200 | 201 | when cost_center_desc is null then 'MISSING_COST_CENTER' 202 | else 203 | cost_center_desc 204 | end as cost_center 205 | from MIMICIII_PHI.A_COST_CENTER 206 | ) 207 | 208 | 209 | -- final table - join callout data to above two tables, adding: 210 | -- 1) the census of the hospital (table 1) 211 | -- 2) the cost center type of the callout ward (table 2) 212 | select 213 | -- variables from dd_design_matrix 214 | dd.icustay_id 215 | , dd.age 216 | , to_char( dd.callouttime, 'mm') as CALLOUT_MONTH 217 | , to_char( dd.callouttime, 'yyyy') as CALLOUT_YEAR 218 | 219 | -- cost center info for the callout ward 220 | , case when t2.activity_dt is not null then 1 else 0 end as HAS_WARD_INFO 221 | , t2.cost_center 222 | , t2.type as ward_type 223 | 224 | -- census variables 225 | , case when t1.CENSUS_DATE is not null then 1 else 0 end as HAS_CENSUS 226 | , t1.MedSurgTrauma_CENSUS 227 | , t1.MedSurgTrauma_BEDS 228 | , t1.MedSurgGynecology_CENSUS 229 | , t1.MedSurgGynecology_BEDS 230 | , t1.HemOnc_CENSUS 231 | , t1.HemOnc_BEDS 232 | , t1.TransplantUnit_CENSUS 233 | , t1.TransplantUnit_BEDS 234 | , t1.Neuro_CENSUS 235 | , t1.Neuro_BEDS 236 | , t1.CardiologyMed_CENSUS 237 | , t1.CardiologyMed_BEDS 238 | , t1.Vascular_CENSUS 239 | , t1.Vascular_BEDS 240 | , t1.CardiacSurgery_CENSUS 241 | , t1.CardiacSurgery_BEDS 242 | , t1.Medicine_CENSUS 243 | , t1.Medicine_BEDS 244 | , t1.MedSurg_CENSUS 245 | , t1.MedSurg_BEDS 246 | , t1.MedSurgPancrBiliary_CENSUS 247 | , t1.MedSurgPancrBiliary_BEDS 248 | 249 | , t1.EmergencyDepartment_CENSUS 250 | , t1.EmergencyDepartment_BEDS 251 | , t1.LaborDelivery_CENSUS 252 | , t1.LaborDelivery_BEDS 253 | , t1.MedicalCardiology_CENSUS 254 | , t1.MedicalCardiology_BEDS 255 | , t1.Obstetrics_CENSUS 256 | , t1.Obstetrics_BEDS 257 | , t1.PACUWest_CENSUS 258 | , t1.PACUWest_BEDS 259 | , t1.ThoracicSurgery_CENSUS 260 | , t1.ThoracicSurgery_BEDS 261 | , t1.VascularStepdown_CENSUS 262 | , t1.VascularStepdown_BEDS 263 | 264 | from dd 265 | inner join mimiciii_phi.D_WARD dw 266 | on dd.callout_wardid = dw.wardid 267 | -- join to get the census for the day 268 | left join t1 269 | on trunc(dd.callouttime,'dd') = t1.census_date 270 | left join t2 271 | on trunc(dd.callouttime,'dd') = t2.activity_dt 272 | and upper(trim(dw.ward)) = t2.floor 273 | order by dd.icustay_id; 274 | -------------------------------------------------------------------------------- /analysis/letter_response/letter_response.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Callout: Response" 3 | author: "J.D. Raffa" 4 | date: "Nov. 1, 2018" 5 | output: 6 | html_document: 7 | fig_caption: yes 8 | fig_height: 8 9 | fig_width: 8 10 | --- 11 | 12 | This analysis was done in response to a letter submitted by Drs. Ofoma and Keithireddy about our paper. 13 | 14 | The main concern was related to the use of the discharge delay > 24 h exposure. This work explores the sensitivity of the conclusions to this parametrization fitting natural cubic splines and a more granular binned approach. 15 | 16 | They had also request more information about the different groups with longer/shorter discharge delays, and this was actually looked at in our previous output document (analysis_paper.html), but is repeated here for completeness. 17 | 18 | 19 | 20 | ```{r setup, include=FALSE} 21 | knitr::opts_chunk$set(echo = FALSE,message=FALSE,warning =FALSE,fig.keep=TRUE,dev=c("png","postscript","pdf")) 22 | setwd("~/data2/MIMIC-Callout/callout-final/") 23 | d.nonphi <- read.csv("callout.csv") 24 | nrow(d.nonphi) 25 | d.phi <- read.csv("callout-phi.csv") 26 | nrow(d.phi) 27 | setwd("~/db/callout/final") 28 | names(d.phi) <- tolower(names(d.phi)) 29 | 30 | library(dplyr);library(Hmisc); library(car) 31 | library(ggplot2) 32 | ``` 33 | 34 | ```{r} 35 | d.phi %>% full_join(d.nonphi,by="icustay_id") %>% nrow() 36 | d.phi %>% inner_join(d.nonphi,by="icustay_id") %>% nrow() 37 | d.phi %>% full_join(d.nonphi,by="icustay_id") %>% filter(micu==1 | msicu==1) %>% nrow() 38 | d.phi %>% inner_join(d.nonphi,by="icustay_id") %>% filter(micu==1 | msicu==1) %>% nrow() 39 | 40 | d <- d.phi %>% full_join(d.nonphi,by="icustay_id") %>% filter(micu==1 | msicu==1) 41 | d$HOSP_FREE_DAYS <- (28 - d$los_post_icu_days)*(d$los_post_icu_days<28)*(1-d$hospitaldeath) 42 | d$CALLOUT_DURING_ROUNDS <- d$hourofcallout>700 & d$hourofcallout<1200; 43 | d$CALLOUT_DURING_NIGHT <- d$hourofcallout>2300 | d$hourofcallout<700; 44 | d$DISCHARGEDELAY_HOURS <- d$dischargedelay_days*24; 45 | d$hourofcallout2 <- floor(d$hourofcallout/100) + ((d$hourofcallout/100) - floor(d$hourofcallout/100))*100/60 46 | d <- d %>% filter(callout_year<2012 & callout_year>2001) 47 | nrow(d) 48 | d$PROPFULL_BEDS <- rowSums(d[,grep("census",names(d))],na.rm=TRUE)/rowSums(d[,grep("beds",names(d))],na.rm=TRUE) 49 | #d <- d %>% mutate(ethnicity=stringr::str_sub(ethnicity,0,20)) 50 | d <- d %>% mutate(ethnicity=recode_factor(ethnicity,`WHITE`="White", 51 | `WHITE - EASTERN EUROPEAN`="White", 52 | `WHITE - RUSSIAN` = "White", 53 | `WHITE - BRAZILIAN` = "White", 54 | `WHITE - OTHER EUROPEAN` = "White", 55 | `BLACK/AFRICAN AMERICAN` = "African American/Black", 56 | `BLACK/CAPE VERDEAN`= "African American/Black", 57 | `BLACK/HAITIAN`= "African American/Black", 58 | `BLACK/AFRICAN` = "African American/Black", 59 | .default="Other"),MED_SERVICE=service=="MED") 60 | ``` 61 | 62 | 63 | Below are the Overall patient characteristics. 64 | 65 | ```{r} 66 | library(MASS);library(splines);library(mgcv);library(plotly) 67 | d$discharge_time <- d$hourofcallout2 + d$DISCHARGEDELAY_HOURS 68 | d$discharge_time <- ifelse(d$discharge_time<24, d$discharge_time,d$discharge_time - floor(d$discharge_time/24)*24) 69 | d$daypostcall <- floor((d$hourofcallout2 + d$DISCHARGEDELAY_HOURS)/24) 70 | d$postcalldaycat2 <- cut2(d$daypostcall,c(1)) 71 | 72 | library(tableone) 73 | vars1 <- c("micu","age_nohipaa", "callout_month","female","request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","oasis","elixhauser_hospital","ethnicity","MED_SERVICE","HOSP_FREE_DAYS","callout_dayofweek","CALLOUT_DURING_NIGHT","CALLOUT_DURING_ROUNDS","DISCHARGEDELAY_HOURS","hourofcallout2","PROPFULL_BEDS","postcalldaycat2","hospitaldeath","los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "callout_year") 74 | factorVars1 = c("micu", "female","ethnicity","MED_SERVICE", "callout_month", "request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","service","callout_dayofweek","CALLOUT_DURING_ROUNDS","CALLOUT_DURING_NIGHT","postcalldaycat2","hospitaldeath","callout_year") 75 | nonnormal.vars = c("los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "hourofcallout2","HOSP_FREE_DAYS") 76 | 77 | 78 | CreateTableOne(data=d,vars=vars1,factorVars = factorVars1) %>% print( 79 | printToggle = FALSE, 80 | showAllLevels = TRUE, 81 | cramVars = "kon",nonnormal = nonnormal.vars 82 | ) %>% 83 | {data.frame( 84 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 85 | row.names = NULL, 86 | check.names = FALSE, 87 | stringsAsFactors = FALSE)} %>% 88 | knitr::kable(caption="Patient Characteristics Overall") 89 | ``` 90 | 91 | 92 | # Question 1: Who has long discharge delays? 93 | 94 | 95 | ```{r} 96 | CreateTableOne(data=d %>% mutate(DisDelay=cut2(DISCHARGEDELAY_HOURS,c(0,4,8,24))),vars=vars1,factorVars = factorVars1,strata="DisDelay",test=TRUE) %>% print( 97 | printToggle = FALSE, 98 | showAllLevels = TRUE, 99 | cramVars = "kon",nonnormal = nonnormal.vars 100 | ) %>% 101 | {data.frame( 102 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 103 | row.names = NULL, 104 | check.names = FALSE, 105 | stringsAsFactors = FALSE)} %>% 106 | knitr::kable(caption="Patient Characteristics By Discharge Delay Categories (Hours)") 107 | 108 | 109 | ``` 110 | 111 | 112 | 113 | 114 | ```{r} 115 | CreateTableOne(data=d %>% mutate(DisDelay=cut2(DISCHARGEDELAY_HOURS,c(24))),vars=vars1,factorVars = factorVars1,strata="DisDelay",test=TRUE) %>% print( 116 | printToggle = FALSE, 117 | showAllLevels = TRUE, 118 | cramVars = "kon",nonnormal = nonnormal.vars 119 | ) %>% 120 | {data.frame( 121 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 122 | row.names = NULL, 123 | check.names = FALSE, 124 | stringsAsFactors = FALSE)} %>% 125 | knitr::kable(caption="Patient Characteristics By Discharge Delay Categories (Hours)") 126 | 127 | 128 | ``` 129 | 130 | We fit a logistic regression model with: 131 | 132 | 1. Demographics: age, sex, 133 | 2. Requests: tele, resp, mrsa, cdiff, vre 134 | 3. Adjustment for severity/comoribidity/icu los: oasis, exlixhauser, los_pre_callout_days 135 | 4. Possible structural/admin variables: DOW, month, year. 136 | 5. Census variables: type of bed requested, proportion of hospital beds is use. 137 | 138 | 139 | ```{r} 140 | 141 | ``` 142 | 143 | ```{r} 144 | vars1 <- c("micu","age_nohipaa", "callout_month","female","request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","oasis","elixhauser_hospital","ethnicity","MED_SERVICE","HOSP_FREE_DAYS","callout_dayofweek","CALLOUT_DURING_NIGHT","CALLOUT_DURING_ROUNDS","DISCHARGEDELAY_HOURS","hourofcallout2","PROPFULL_BEDS","postcalldaycat2","los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "callout_year") 145 | factorVars1 = c("micu", "female","ethnicity","MED_SERVICE", "callout_month", "request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","service","callout_dayofweek","CALLOUT_DURING_ROUNDS","CALLOUT_DURING_NIGHT","postcalldaycat2","callout_year") 146 | nonnormal.vars = c("los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "hourofcallout2","HOSP_FREE_DAYS") 147 | 148 | 149 | CreateTableOne(data=d,vars=vars1,factorVars = factorVars1,strata="hospitaldeath") %>% print( 150 | printToggle = FALSE, 151 | showAllLevels = TRUE, 152 | cramVars = "kon",nonnormal = nonnormal.vars 153 | ) %>% 154 | {data.frame( 155 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 156 | row.names = NULL, 157 | check.names = FALSE, 158 | stringsAsFactors = FALSE)} %>% 159 | knitr::kable(caption="Patient Characteristics Overall") 160 | 161 | ``` 162 | 163 | ```{r} 164 | library(splines) 165 | full.model.glm <- glm(hospitaldeath ~ ns(DISCHARGEDELAY_HOURS,knots=c(6,12,24,36,48)) + +cut2(oasis,g=3) + cut2(age_nohipaa,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 166 | 167 | drop1(full.model.glm,test="Chisq") 168 | sjPlot::plot_model(full.model.glm,Title="Full Model, mortality") 169 | 170 | library(MASS) 171 | 172 | 173 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0,scope=list(lower=~ns(DISCHARGEDELAY_HOURS,knots=c(6,12,24,36,48)))) 174 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, mortality") 175 | drop1(final.model.glm.aic,test="Chisq") 176 | final.model.glm.aicp <- final.model.glm.aic 177 | 178 | drop.res <- drop1(full.model.glm ,test="Chisq")[-2,] 179 | print(drop.res) 180 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 181 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 182 | 183 | 184 | jdr.mort.glm <- update( full.model.glm, .~. -request_resp) 185 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 186 | print(jdr.mort.glm) 187 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 188 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 189 | 190 | 191 | jdr.mort.glm <- update( jdr.mort.glm, .~. -request_mrsa) 192 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 193 | print(jdr.mort.glm) 194 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 195 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 196 | 197 | jdr.mort.glm <- update( jdr.mort.glm, .~. -cut2(age_nohipaa, g = 3)) 198 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 199 | print(jdr.mort.glm) 200 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 201 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 202 | 203 | jdr.mort.glm <- update( jdr.mort.glm, .~. -as.factor(callout_wardid == 1):cut2(PROPFULL_BEDS, c(0.9, 1))) 204 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 205 | print(jdr.mort.glm) 206 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 207 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 208 | 209 | jdr.mort.glm <- update( jdr.mort.glm, .~. -cut2(PROPFULL_BEDS, c(0.9, 1))) 210 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 211 | print(jdr.mort.glm) 212 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 213 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 214 | 215 | jdr.mort.glm <- update( jdr.mort.glm, .~. -as.factor(callout_dayofweek)) 216 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 217 | print(jdr.mort.glm) 218 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 219 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 220 | 221 | jdr.mort.glm <- update( jdr.mort.glm, .~. -as.factor(callout_month)) 222 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 223 | print(jdr.mort.glm) 224 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 225 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 226 | 227 | jdr.mort.glm <- update( jdr.mort.glm, .~. -relevel(cut2(hourofcallout2, c(7, 12, 19)), "[ 7.000,12.000)")) 228 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 229 | print(jdr.mort.glm) 230 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 231 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 232 | 233 | jdr.mort.glm <- update( jdr.mort.glm, .~. -MED_SERVICE) 234 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 235 | print(jdr.mort.glm) 236 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 237 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 238 | 239 | jdr.mort.glm <- update( jdr.mort.glm, .~. -female) 240 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 241 | print(jdr.mort.glm) 242 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 243 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 244 | jdr.mort.glm.splines <- jdr.mort.glm 245 | drop1(jdr.mort.glm.splines,test="Chisq") 246 | 247 | ``` 248 | 249 | 250 | ```{r} 251 | library(splines) 252 | full.model.glm <- glm(hospitaldeath ~ cut2(DISCHARGEDELAY_HOURS,c(6,12,24,36,48)) +cut2(oasis,g=3) + cut2(age_nohipaa,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 253 | 254 | drop1(full.model.glm,test="Chisq") 255 | sjPlot::plot_model(full.model.glm,Title="Full Model, mortality") 256 | sjPlot::sjt.glm(full.model.glm) 257 | 258 | library(MASS) 259 | 260 | 261 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0,scope=list(lower=~cut2(DISCHARGEDELAY_HOURS,c(6,12,24,36,48)))) 262 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, mortality") 263 | sjPlot::sjt.glm(final.model.glm.aic) 264 | drop1(final.model.glm.aic,test="Chisq") 265 | 266 | drop.res <- drop1(full.model.glm ,test="Chisq")[-2,] 267 | print(drop.res) 268 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 269 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 270 | 271 | jdr.mort.glm <- update( full.model.glm, .~. -request_resp) 272 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 273 | print(jdr.mort.glm) 274 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 275 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 276 | 277 | 278 | jdr.mort.glm <- update( jdr.mort.glm, .~. -request_mrsa) 279 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 280 | print(jdr.mort.glm) 281 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 282 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 283 | 284 | jdr.mort.glm <- update( jdr.mort.glm, .~. -cut2(age_nohipaa, g = 3)) 285 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 286 | print(jdr.mort.glm) 287 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 288 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 289 | 290 | jdr.mort.glm <- update( jdr.mort.glm, .~. -as.factor(callout_wardid == 1):cut2(PROPFULL_BEDS, c(0.9, 1))) 291 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 292 | print(jdr.mort.glm) 293 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 294 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 295 | 296 | jdr.mort.glm <- update( jdr.mort.glm, .~. -cut2(PROPFULL_BEDS, c(0.9, 1))) 297 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 298 | print(jdr.mort.glm) 299 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 300 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 301 | 302 | jdr.mort.glm <- update( jdr.mort.glm, .~. -as.factor(callout_dayofweek)) 303 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 304 | print(jdr.mort.glm) 305 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 306 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 307 | 308 | jdr.mort.glm <- update( jdr.mort.glm, .~. -as.factor(callout_month)) 309 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 310 | print(jdr.mort.glm) 311 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 312 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 313 | 314 | jdr.mort.glm <- update( jdr.mort.glm, .~. -MED_SERVICE) 315 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 316 | print(jdr.mort.glm) 317 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 318 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 319 | 320 | jdr.mort.glm <- update( jdr.mort.glm, .~. -relevel(cut2(hourofcallout2, c(7, 12, 19)), "[ 7.000,12.000)")) 321 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 322 | print(jdr.mort.glm) 323 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 324 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 325 | 326 | jdr.mort.glm <- update( jdr.mort.glm, .~. -female) 327 | drop.res <- drop1(jdr.mort.glm ,test="Chisq")[-2,] 328 | print(jdr.mort.glm) 329 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 330 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 331 | jdr.mort.glm.bins <- jdr.mort.glm 332 | drop1(jdr.mort.glm.bins,test="Chisq") 333 | ``` 334 | 335 | 336 | 337 | ```{r,fig.cap="a"} 338 | outv <- (predict(ns(d$DISCHARGEDELAY_HOURS,knots=c(6,12,24,36,48)),newx=seq(0,72,0.5)))%*%coef(jdr.mort.glm.splines)[2:7] 339 | 340 | covoutv <- predict(ns(d$DISCHARGEDELAY_HOURS,knots=c(6,12,24,36,48)),newx=seq(0,72,0.5))%*% summary(jdr.mort.glm.splines)$cov.unscaled[2:7,2:7]%*%t(predict(ns(d$DISCHARGEDELAY_HOURS,knots=c(6,12,24,36,48)),newx=seq(0,72,0.5))) 341 | 342 | Z <- matrix(0,nr=nrow(covoutv),ncol=nrow(outv)) 343 | diag(Z) <- 1 344 | Z[,1] <- Z[,1] - 1 345 | se <- diag(Z%*%covoutv%*%t(Z)) 346 | LB <- outv - qnorm(0.975)*se - outv[1,] 347 | UB <- outv + qnorm(0.975)*se - outv[1,] 348 | outv <- exp(outv - outv[1,]) 349 | plot(seq(0,72,0.5),outv,xlim=c(0,60),type="l",ylim=c(0.4,1.75),xlab="Discharge Delay (Hours)",ylab="Odds Ratio for In-Hospital Mortality",cex.axis=1.3,cex.lab=1.3) 350 | title(main="a) Spline Analysis",adj=0) 351 | lines(seq(0,72,0.5),exp(LB),lty=3) 352 | lines(seq(0,72,0.5),exp(UB),lty=3) 353 | lines(c(0,100),c(1,1),lty=2,lwd=0.3) 354 | rug(d$DISCHARGEDELAY_HOURS) 355 | p_a <- recordPlot() 356 | #as ggplot 357 | out <- data.frame(vals=seq(0,72,0.5),outv=outv,UB=UB,LB=LB) 358 | p_a <- ggplot(out,aes(vals,outv)) + geom_line() + geom_line(aes(vals,exp(LB)),lty=3) + geom_line(aes(vals,exp(UB)),lty=3) + geom_hline(yintercept = 1,lty=2) + xlab("Discharge Delay (Hours)") + ylab("Odds Ratio for In-Hospital Mortality") + ggtitle("a) Spline Analysis") + theme_bw() + geom_rug(data=d,aes(x=DISCHARGEDELAY_HOURS,y=NULL)) + xlim(c(0,60)) + theme(axis.text = element_text(size=16),axis.title = element_text(size=16)) + ylim(c(0.55,1.65)) 359 | p_a 360 | ``` 361 | 362 | Below is a similar approach to the splines, but using gam (penalized splines chosen) just for a last look at this. Indeed, the wiggleness goes away. 363 | 364 | ```{r} 365 | 366 | library(mgcv) 367 | 368 | full.model.gam <- gam(hospitaldeath ~ s(DISCHARGEDELAY_HOURS,k=10,bs="ts") + +cut2(oasis,g=3) + cut2(age_nohipaa,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 369 | 370 | plot(full.model.gam) 371 | summary(full.model.gam) 372 | anova(full.model.gam) 373 | ``` 374 | 375 | ```{r,fig.cap="b"} 376 | OR.out <- confint(jdr.mort.glm.bins) 377 | #out.reg <- data.frame(group=c("<6","6-12","12-24","24-36","36-48",">48"),OR=c(1,1.02,1.18,0.95,1.76,1.22),ORLB=c(1,0.86,0.84,0.65,0.71,0.66),ORUB=c(1,1.32,1.64,1.36,3.86,2.13),stringsAsFactors = FALSE) 378 | out.reg <- data.frame(group=c("<6","6-12","12-24","24-36","36-48",">48"),OR=exp(c(0,coef(jdr.mort.glm.bins)[2:6])),ORLB=exp(c(0,OR.out[2:6,1])),ORUB=exp(c(0,OR.out[2:6,2]))) 379 | out.reg$group <- factor(out.reg$group,levels=out.reg$group) 380 | p_b <- ggplot(out.reg,aes(group,OR)) + geom_point() + geom_errorbar(aes(ymax=ORUB,ymin=ORLB),width=0.3) + xlab("Discharge Delay (Hours)") + ylab("Odds Ratio for In-Hospital Death") + ggtitle("b) Binned Analysis") + theme_bw() + geom_hline(yintercept = 1,lty=2) + theme(axis.text = element_text(size=16),axis.title = element_text(size=16)) 381 | 382 | ``` 383 | 384 | ```{r} 385 | library(gridExtra); library(grid) 386 | setEPS() 387 | postscript("combined.eps",width=16,height=8) 388 | ml <- marrangeGrob(list(p_a,p_b),nrow=1,ncol=2,top="") 389 | ml 390 | dev.off() 391 | postscript("combined_vert.eps",width=8,height=16) 392 | ml <- marrangeGrob(list(p_a,p_b),nrow=2,ncol=1,top="") 393 | ml 394 | dev.off() 395 | ``` 396 | -------------------------------------------------------------------------------- /sql/2-icu-variables.sql: -------------------------------------------------------------------------------- 1 | -- Subqueries: 2 | -- 1) GCS 3 | -- 2) VENT 4 | -- 3) Vitals 5 | -- 4) Urine output 6 | 7 | -- Required tables: 8 | -- dd_cohort 9 | 10 | -- Created tables: 11 | -- dd_gcs 12 | -- dd_vitals 13 | -- dd_vent 14 | -- dd_oasis 15 | 16 | drop materialized view IF EXISTS dd_gcs; 17 | CREATE MATERIALIZED VIEW dd_gcs as 18 | with base as 19 | ( 20 | SELECT pvt.ICUSTAY_ID, pvt.charttime 21 | 22 | -- Easier names - note we coalesced Metavision and CareVue IDs below 23 | , max(case when GCSTYPE = 'Motor' then pvt.valuenum else null end) as GCSMotor 24 | , max(case when GCSTYPE = 'Verbal' then pvt.valuenum else null end) as GCSVerbal 25 | , max(case when GCSTYPE = 'Eyes' then pvt.valuenum else null end) as GCSEyes 26 | 27 | -- If verbal was set to 0 in the below select, then this is an intubated patient 28 | , case 29 | when max(case when GCSTYPE = 'Verbal' then pvt.valuenum else null end) = 0 30 | then 1 31 | else 0 32 | end as EndoTrachFlag 33 | 34 | , ROW_NUMBER () 35 | OVER (PARTITION BY pvt.ICUSTAY_ID ORDER BY pvt.charttime ASC) as rn 36 | 37 | FROM 38 | ( 39 | select l.ICUSTAY_ID 40 | -- merge the ITEMIDs so that the pivot applies to both metavision/carevue data 41 | , case 42 | when l.ITEMID in (723,223900) then 'Verbal' 43 | when l.ITEMID in (454,223901) then 'Motor' 44 | when l.ITEMID in (184,220739) then 'Eyes' 45 | else null end 46 | as GCSTYPE 47 | 48 | -- convert the data into a number, reserving a value of 0 for ET/Trach 49 | , case 50 | -- endotrach/vent is assigned a value of 0, later parsed specially 51 | when l.ITEMID = 723 and l.VALUE = '1.0 ET/Trach' then 0 -- carevue 52 | when l.ITEMID = 223900 and l.VALUE = 'No Response-ETT' then 0 -- metavision 53 | 54 | else VALUENUM 55 | end 56 | as VALUENUM 57 | , l.CHARTTIME 58 | from mimiciii.CHARTEVENTS l 59 | 60 | -- get callout time for charttime subselection 61 | inner join dd_cohort b 62 | on l.icustay_id = b.icustay_id 63 | 64 | -- Isolate the desired GCS variables 65 | where l.ITEMID in 66 | ( 67 | -- 198 -- GCS 68 | -- GCS components, CareVue 69 | 184, 454, 723 70 | -- GCS components, Metavision 71 | , 223900, 223901, 220739 72 | ) 73 | -- Only get data for the 24 hours before call out 74 | and l.charttime between b.callouttime - interval '1' day and b.callouttime 75 | ) pvt 76 | group by pvt.ICUSTAY_ID, pvt.charttime 77 | ) 78 | , gcs as ( 79 | select b.* 80 | , b2.GCSVerbal as GCSVerbalPrev 81 | , b2.GCSMotor as GCSMotorPrev 82 | , b2.GCSEyes as GCSEyesPrev 83 | -- Calculate GCS, factoring in special case when they are intubated and prev vals 84 | -- note that the coalesce are used to implement the following if: 85 | -- if current value exists, use it 86 | -- if previous value exists, use it 87 | -- otherwise, default to normal 88 | , case 89 | -- replace GCS during sedation with 15 90 | when b.GCSVerbal = 0 91 | then 15 92 | when b.GCSVerbal is null and b2.GCSVerbal = 0 93 | then 15 94 | -- if previously they were intub, but they aren't now, do not use previous GCS values 95 | when b2.GCSVerbal = 0 96 | then 97 | coalesce(b.GCSMotor,6) 98 | + coalesce(b.GCSVerbal,5) 99 | + coalesce(b.GCSEyes,4) 100 | -- otherwise, add up score normally, imputing previous value if none available at current time 101 | else 102 | coalesce(b.GCSMotor,coalesce(b2.GCSMotor,6)) 103 | + coalesce(b.GCSVerbal,coalesce(b2.GCSVerbal,5)) 104 | + coalesce(b.GCSEyes,coalesce(b2.GCSEyes,4)) 105 | end as GCS 106 | 107 | from base b 108 | -- join to itself within 6 hours to get previous value 109 | left join base b2 110 | on b.ICUSTAY_ID = b2.ICUSTAY_ID and b.rn = b2.rn+1 and b2.charttime > b.charttime - interval '6' hour 111 | ) 112 | , gcs_final as ( 113 | select gcs.* 114 | -- This sorts the data by GCS, so rn=1 is the the lowest GCS values to keep 115 | , ROW_NUMBER () 116 | OVER (PARTITION BY gcs.ICUSTAY_ID 117 | ORDER BY gcs.GCS 118 | ) as IsMinGCS 119 | from gcs 120 | ) 121 | select ie.ICUSTAY_ID 122 | -- The minimum GCS is determined by the above row partition, we only join if IsMinGCS=1 123 | , GCS as MinGCS 124 | , coalesce(GCSMotor,GCSMotorPrev) as GCSMotor 125 | , coalesce(GCSVerbal,GCSVerbalPrev) as GCSVerbal 126 | , coalesce(GCSEyes,GCSEyesPrev) as GCSEyes 127 | , EndoTrachFlag as EndoTrachFlag 128 | 129 | -- subselect down to the cohort of eligible patients 130 | from dd_cohort ie 131 | left join gcs_final gs 132 | on ie.ICUSTAY_ID = gs.ICUSTAY_ID 133 | where IsMinGCS = 1; 134 | 135 | 136 | drop materialized view if exists dd_vitals; 137 | create materialized view dd_vitals as 138 | SELECT pvt.subject_id, pvt.hadm_id, pvt.icustay_id 139 | 140 | -- Easier names 141 | , min(case when VitalID = 1 then valuenum else null end) as HeartRate_Min 142 | , max(case when VitalID = 1 then valuenum else null end) as HeartRate_Max 143 | , min(case when VitalID = 2 then valuenum else null end) as SysBP_Min 144 | , max(case when VitalID = 2 then valuenum else null end) as SysBP_Max 145 | , min(case when VitalID = 3 then valuenum else null end) as DiasBP_Min 146 | , max(case when VitalID = 3 then valuenum else null end) as DiasBP_Max 147 | , min(case when VitalID = 4 then valuenum else null end) as MeanBP_Min 148 | , max(case when VitalID = 4 then valuenum else null end) as MeanBP_Max 149 | , min(case when VitalID = 5 then valuenum else null end) as RespRate_Min 150 | , max(case when VitalID = 5 then valuenum else null end) as RespRate_Max 151 | , min(case when VitalID = 6 then valuenum else null end) as TempC_Min 152 | , max(case when VitalID = 6 then valuenum else null end) as TempC_Max 153 | , min(case when VitalID = 7 then valuenum else null end) as SpO2_Min 154 | , max(case when VitalID = 7 then valuenum else null end) as SpO2_Max 155 | , min(case when VitalID = 8 then valuenum else null end) as Glucose_Min 156 | , max(case when VitalID = 8 then valuenum else null end) as Glucose_Max 157 | 158 | FROM ( 159 | select ie.subject_id, ie.hadm_id, ie.icustay_id 160 | , case 161 | when itemid in (211,220045) and valuenum > 0 and valuenum < 300 then 1 -- HeartRate 162 | when itemid in (51,442,455,6701,220179,220050) and valuenum > 0 and valuenum < 400 then 2 -- SysBP 163 | when itemid in (8368,8440,8441,8555,220180,220051) and valuenum > 0 and valuenum < 300 then 3 -- DiasBP 164 | when itemid in (456,52,6702,443,220052,220181,225312) and valuenum > 0 and valuenum < 300 then 4 -- MeanBP 165 | when itemid in (615,618,220210,224690) and valuenum > 0 and valuenum < 70 then 5 -- RespRate 166 | when itemid in (223761,678) and valuenum > 70 and valuenum < 120 then 6 -- TempF, converted to degC in valuenum call 167 | when itemid in (223762,676) and valuenum > 10 and valuenum < 50 then 6 -- TempC 168 | when itemid in (646,220277) and valuenum > 0 and valuenum <= 100 then 7 -- SpO2 169 | when itemid in (807,811,1529,3745,3744,225664,220621,226537) and valuenum > 0 then 8 -- Glucose 170 | 171 | else null end as VitalID 172 | -- convert F to C 173 | , case when itemid in (223761,678) then (valuenum-32)/1.8 else valuenum end as valuenum 174 | 175 | from mimiciii.icustays ie 176 | left join mimiciii.chartevents ce 177 | on ie.subject_id = ce.subject_id and ie.hadm_id = ce.hadm_id and ie.icustay_id = ce.icustay_id 178 | and ce.charttime between ie.intime and ie.intime + interval '1' day 179 | where ce.itemid in 180 | ( 181 | -- HEART RATE 182 | 211, --"Heart Rate" 183 | 220045, --"Heart Rate" 184 | 185 | -- Systolic/diastolic 186 | 187 | 51, -- Arterial BP [Systolic] 188 | 442, -- Manual BP [Systolic] 189 | 455, -- NBP [Systolic] 190 | 6701, -- Arterial BP #2 [Systolic] 191 | 220179, -- Non Invasive Blood Pressure systolic 192 | 220050, -- Arterial Blood Pressure systolic 193 | 194 | 8368, -- Arterial BP [Diastolic] 195 | 8440, -- Manual BP [Diastolic] 196 | 8441, -- NBP [Diastolic] 197 | 8555, -- Arterial BP #2 [Diastolic] 198 | 220180, -- Non Invasive Blood Pressure diastolic 199 | 220051, -- Arterial Blood Pressure diastolic 200 | 201 | 202 | -- MEAN ARTERIAL PRESSURE 203 | 456, --"NBP Mean" 204 | 52, --"Arterial BP Mean" 205 | 6702, -- Arterial BP Mean #2 206 | 443, -- Manual BP Mean(calc) 207 | 220052, --"Arterial Blood Pressure mean" 208 | 220181, --"Non Invasive Blood Pressure mean" 209 | 225312, --"ART BP mean" 210 | 211 | -- RESPIRATORY RATE 212 | 618,-- Respiratory Rate 213 | 615,-- Resp Rate (Total) 214 | 220210,-- Respiratory Rate 215 | 224690, -- Respiratory Rate (Total) 216 | 217 | 218 | -- SPO2, peripheral 219 | 646, 220277, 220 | 221 | -- GLUCOSE, both lab and fingerstick 222 | 807,-- Fingerstick Glucose 223 | 811,-- Glucose (70-105) 224 | 1529,-- Glucose 225 | 3745,-- BloodGlucose 226 | 3744,-- Blood Glucose 227 | 225664,-- Glucose finger stick 228 | 220621,-- Glucose (serum) 229 | 226537,-- Glucose (whole blood) 230 | 231 | -- TEMPERATURE 232 | 223762, -- "Temperature Celsius" 233 | 676, -- "Temperature C" 234 | 223761, -- "Temperature Fahrenheit" 235 | 678 -- "Temperature F" 236 | 237 | 238 | ) 239 | ) pvt 240 | group by pvt.subject_id, pvt.hadm_id, pvt.icustay_id 241 | order by pvt.subject_id, pvt.hadm_id, pvt.icustay_id; 242 | 243 | drop materialized view if exists dd_uo; 244 | create materialized view dd_uo AS 245 | select 246 | -- patient identifiers 247 | co.subject_id, co.hadm_id, co.icustay_id 248 | 249 | -- volumes associated with urine output ITEMIDs 250 | , sum(oe.VALUE) as UrineOutput 251 | 252 | from dd_cohort co 253 | -- Join to the outputevents table to get urine output 254 | left join mimiciii.outputevents oe 255 | -- join on all patient identifiers 256 | on co.subject_id = oe.subject_id and co.hadm_id = oe.hadm_id and co.icustay_id = oe.icustay_id 257 | -- and ensure the data occurs during the first day 258 | and oe.charttime between co.callouttime - interval '1' day and co.callouttime -- just before callout 259 | and itemid in 260 | ( 261 | -- these are the most frequently occurring urine output observations in CareVue 262 | 40055, -- "Urine Out Foley" 263 | 43175, -- "Urine ." 264 | 40069, -- "Urine Out Void" 265 | 40094, -- "Urine Out Condom Cath" 266 | 40715, -- "Urine Out Suprapubic" 267 | 40473, -- "Urine Out IleoConduit" 268 | 40085, -- "Urine Out Incontinent" 269 | 40057, -- "Urine Out Rt Nephrostomy" 270 | 40056, -- "Urine Out Lt Nephrostomy" 271 | 40405, -- "Urine Out Other" 272 | 40428, -- "Urine Out Straight Cath" 273 | 40086,-- Urine Out Incontinent 274 | 40096, -- "Urine Out Ureteral Stent #1" 275 | 40651, -- "Urine Out Ureteral Stent #2" 276 | 277 | -- these are the most frequently occurring urine output observations in CareVue 278 | 226559, -- "Foley" 279 | 226560, -- "Void" 280 | 227510, -- "TF Residual" 281 | 226561, -- "Condom Cath" 282 | 226584, -- "Ileoconduit" 283 | 226563, -- "Suprapubic" 284 | 226564, -- "R Nephrostomy" 285 | 226565, -- "L Nephrostomy" 286 | 226567, -- Straight Cath 287 | 226557, -- "R Ureteral Stent" 288 | 226558 -- "L Ureteral Stent" 289 | ) 290 | group by co.subject_id, co.hadm_id, co.icustay_id, co.callouttime 291 | order by co.icustay_id; 292 | 293 | 294 | drop materialized view if exists dd_vent; 295 | create materialized view dd_vent as 296 | 297 | select 298 | co.subject_id, co.hadm_id, co.icustay_id 299 | -- use ventilator settings to determine existence of mechanical ventilation 300 | -- case statement determining whether it is an instance of mech vent 301 | , max( 302 | case 303 | when itemid is null or value is null then 0 -- can't have null values 304 | when itemid = 720 and value != 'Other/Remarks' THEN 1 -- VentTypeRecorded 305 | when itemid = 467 and value = 'Ventilator' THEN 1 -- O2 delivery device == ventilator 306 | when itemid = 648 and value = 'Intubated/trach' THEN 1 -- Speech = intubated 307 | when itemid in 308 | ( 309 | 445, 448, 449, 450, 1340, 1486, 1600, 224687 -- minute volume 310 | , 639, 654, 681, 682, 683, 684,224685,224684,224686 -- tidal volume 311 | , 218,436,535,444,459,224697,224695,224696,224746,224747 -- High/Low/Peak/Mean/Neg insp force ("RespPressure") 312 | , 221,1,1211,1655,2000,226873,224738,224419,224750,227187 -- Insp pressure 313 | , 543 -- PlateauPressure 314 | , 5865,5866,224707,224709,224705,224706 -- APRV pressure 315 | , 60,437,505,506,686,220339,224700 -- PEEP 316 | , 3459 -- high pressure relief 317 | , 501,502,503,224702 -- PCV 318 | , 223,667,668,669,670,671,672 -- TCPCV 319 | , 157,158,1852,3398,3399,3400,3401,3402,3403,3404,8382,227809,227810 -- ETT 320 | , 224701 -- PSVlevel 321 | ) 322 | THEN 1 323 | else 0 324 | end 325 | ) as MechVent 326 | from dd_cohort co 327 | left join mimiciii.chartevents tt 328 | on co.subject_id = tt.subject_id and co.hadm_id = tt.hadm_id and co.icustay_id = tt.icustay_id 329 | and tt.charttime between co.callouttime - interval '1' day and co.callouttime 330 | group by co.subject_id, co.hadm_id, co.icustay_id 331 | order by co.icustay_id; 332 | 333 | 334 | 335 | 336 | -- now, using the above data, extract a severity score for the 24 hours before discharge 337 | DROP MATERIALIZED VIEW IF EXISTS DD_OASIS; 338 | CREATE MATERIALIZED VIEW DD_OASIS as 339 | 340 | with surgflag as 341 | ( 342 | select ie.icustay_id 343 | , max(case 344 | when lower(curr_service) like '%surg%' then 1 345 | when curr_service = 'ORTHO' then 1 346 | else 0 end) as surgical 347 | from mimiciii.icustays ie 348 | left join mimiciii.services se 349 | on ie.hadm_id = se.hadm_id 350 | and se.transfertime < ie.intime + interval '1' day 351 | group by ie.icustay_id 352 | ) 353 | , cohort as 354 | ( 355 | select ie.subject_id, ie.hadm_id, ie.icustay_id 356 | , ie.intime 357 | , ie.outtime 358 | , adm.deathtime 359 | , cast(ie.intime as timestamp) - cast(adm.admittime as timestamp) as PreICULOS 360 | , floor( ( cast(ie.intime as date) - cast(pat.dob as date) ) / 365.242 ) as age 361 | , gcs.mingcs 362 | , vital.heartrate_max 363 | , vital.heartrate_min 364 | , vital.meanbp_max 365 | , vital.meanbp_min 366 | , vital.resprate_max 367 | , vital.resprate_min 368 | , vital.tempc_max 369 | , vital.tempc_min 370 | , vent.mechvent 371 | , uo.urineoutput 372 | 373 | , case 374 | when adm.ADMISSION_TYPE = 'ELECTIVE' and sf.surgical = 1 375 | then 1 376 | when adm.ADMISSION_TYPE is null or sf.surgical is null 377 | then null 378 | else 0 379 | end as ElectiveSurgery 380 | 381 | -- age group 382 | , case 383 | when ( ( cast(ie.intime as date) - cast(pat.dob as date) ) / 365.242 ) <= (60*60*24*12) then 'neonate' 384 | when ( ( cast(ie.intime as date) - cast(pat.dob as date) ) / 365.242 ) <= (60*60*24*12*15) then 'middle' 385 | else 'adult' end as ICUSTAY_AGE_GROUP 386 | 387 | -- mortality flags 388 | , case 389 | when adm.deathtime between ie.intime and ie.outtime 390 | then 1 391 | when adm.deathtime <= ie.intime -- sometimes there are typographical errors in the death date 392 | then 1 393 | when adm.dischtime <= ie.outtime and adm.discharge_location = 'DEAD/EXPIRED' 394 | then 1 395 | else 0 end 396 | as ICUSTAY_EXPIRE_FLAG 397 | , adm.hospital_expire_flag 398 | from mimiciii.icustays ie 399 | inner join mimiciii.admissions adm 400 | on ie.hadm_id = adm.hadm_id 401 | inner join mimiciii.patients pat 402 | on ie.subject_id = pat.subject_id 403 | left join surgflag sf 404 | on ie.icustay_id = sf.icustay_id 405 | -- join to custom tables to get more data.... 406 | left join dd_gcs gcs 407 | on ie.icustay_id = gcs.icustay_id 408 | left join dd_vitals vital 409 | on ie.icustay_id = vital.icustay_id 410 | left join dd_uo uo 411 | on ie.icustay_id = uo.icustay_id 412 | left join dd_vent vent 413 | on ie.icustay_id = vent.icustay_id 414 | ) 415 | , scorecomp as 416 | ( 417 | select co.subject_id, co.hadm_id, co.icustay_id 418 | , co.ICUSTAY_AGE_GROUP 419 | , co.icustay_expire_flag 420 | , co.hospital_expire_flag 421 | 422 | -- Below code calculates the component scores needed for OASIS 423 | , case when preiculos is null then null 424 | when preiculos < '0 0:10:12' then 5 425 | when preiculos < '0 4:57:00' then 3 426 | when preiculos < '1 0:00:00' then 0 427 | when preiculos < '12 23:48:00' then 1 428 | else 2 end as preiculos_score 429 | , case when age is null then null 430 | when age < 24 then 0 431 | when age <= 53 then 3 432 | when age <= 77 then 6 433 | when age <= 89 then 9 434 | when age >= 90 then 7 435 | else 0 end as age_score 436 | , case when mingcs is null then null 437 | when mingcs <= 7 then 10 438 | when mingcs < 14 then 4 439 | when mingcs = 14 then 3 440 | else 0 end as gcs_score 441 | , case when heartrate_max is null then null 442 | when heartrate_max > 125 then 6 443 | when heartrate_min < 33 then 4 444 | when heartrate_max >= 107 and heartrate_max <= 125 then 3 445 | when heartrate_max >= 89 and heartrate_max <= 106 then 1 446 | else 0 end as heartrate_score 447 | , case when meanbp_min is null then null 448 | when meanbp_min < 20.65 then 4 449 | when meanbp_min < 51 then 3 450 | when meanbp_max > 143.44 then 3 451 | when meanbp_min >= 51 and meanbp_min < 61.33 then 2 452 | else 0 end as meanbp_score 453 | , case when resprate_min is null then null 454 | when resprate_min < 6 then 10 455 | when resprate_max > 44 then 9 456 | when resprate_max > 30 then 6 457 | when resprate_max > 22 then 1 458 | when resprate_min < 13 then 1 else 0 459 | end as resprate_score 460 | , case when tempc_max is null then null 461 | when tempc_max > 39.88 then 6 462 | when tempc_min >= 33.22 and tempc_min <= 35.93 then 4 463 | when tempc_max >= 33.22 and tempc_max <= 35.93 then 4 464 | when tempc_min < 33.22 then 3 465 | when tempc_min > 35.93 and tempc_min <= 36.39 then 2 466 | when tempc_max >= 36.89 and tempc_max <= 39.88 then 2 467 | else 0 end as temp_score 468 | , case when UrineOutput is null then null 469 | when UrineOutput < 671.09 then 10 470 | when UrineOutput > 6896.80 then 8 471 | when UrineOutput >= 671.09 472 | and UrineOutput <= 1426.99 then 5 473 | when UrineOutput >= 1427.00 474 | and UrineOutput <= 2544.14 then 1 475 | else 0 end as UrineOutput_score 476 | , case when mechvent is null then null 477 | when mechvent = 1 then 9 478 | else 0 end as mechvent_score 479 | , case when ElectiveSurgery is null then null 480 | when ElectiveSurgery = 1 then 0 481 | else 6 end as electivesurgery_score 482 | 483 | -- The below code gives the component associated with each score 484 | -- This is not needed to calculate OASIS, but provided for user convenience. 485 | -- If both the min/max are in the normal range (score of 0), then the average value is stored. 486 | , preiculos 487 | , age 488 | , mingcs as gcs 489 | , case when heartrate_max is null then null 490 | when heartrate_max > 125 then heartrate_max 491 | when heartrate_min < 33 then heartrate_min 492 | when heartrate_max >= 107 and heartrate_max <= 125 then heartrate_max 493 | when heartrate_max >= 89 and heartrate_max <= 106 then heartrate_max 494 | else (heartrate_min+heartrate_max)/2 end as heartrate 495 | , case when meanbp_min is null then null 496 | when meanbp_min < 20.65 then meanbp_min 497 | when meanbp_min < 51 then meanbp_min 498 | when meanbp_max > 143.44 then meanbp_max 499 | when meanbp_min >= 51 and meanbp_min < 61.33 then meanbp_min 500 | else (meanbp_min+meanbp_max)/2 end as meanbp 501 | , case when resprate_min is null then null 502 | when resprate_min < 6 then resprate_min 503 | when resprate_max > 44 then resprate_max 504 | when resprate_max > 30 then resprate_max 505 | when resprate_max > 22 then resprate_max 506 | when resprate_min < 13 then resprate_min 507 | else (resprate_min+resprate_max)/2 end as resprate 508 | , case when tempc_max is null then null 509 | when tempc_max > 39.88 then tempc_max 510 | when tempc_min >= 33.22 and tempc_min <= 35.93 then tempc_min 511 | when tempc_max >= 33.22 and tempc_max <= 35.93 then tempc_max 512 | when tempc_min < 33.22 then tempc_min 513 | when tempc_min > 35.93 and tempc_min <= 36.39 then tempc_min 514 | when tempc_max >= 36.89 and tempc_max <= 39.88 then tempc_max 515 | else (tempc_min+tempc_max)/2 end as temp 516 | , UrineOutput 517 | , mechvent 518 | , ElectiveSurgery 519 | from cohort co 520 | ) 521 | , score as 522 | ( 523 | select s.* 524 | , coalesce(age_score,0) 525 | + coalesce(preiculos_score,0) 526 | + coalesce(gcs_score,0) 527 | + coalesce(heartrate_score,0) 528 | + coalesce(meanbp_score,0) 529 | + coalesce(resprate_score,0) 530 | + coalesce(temp_score,0) 531 | + coalesce(urineoutput_score,0) 532 | + coalesce(mechvent_score,0) 533 | + coalesce(electivesurgery_score,0) 534 | as OASIS 535 | from scorecomp s 536 | ) 537 | select 538 | subject_id, hadm_id, icustay_id 539 | -- , ICUSTAY_AGE_GROUP 540 | -- , hospital_expire_flag 541 | -- , icustay_expire_flag 542 | , OASIS 543 | -- Calculate the probability of in-hospital mortality 544 | , 1 / (1 + exp(- (-6.1746 + 0.1275*(OASIS) ))) as OASIS_PROB 545 | -- , age, age_score 546 | -- , preiculos, preiculos_score 547 | -- , gcs, gcs_score 548 | -- , heartrate, heartrate_score 549 | -- , meanbp, meanbp_score 550 | -- , resprate, resprate_score 551 | -- , temp, temp_score 552 | -- , urineoutput, UrineOutput_score 553 | -- , mechvent, mechvent_score 554 | -- , electivesurgery, electivesurgery_score 555 | from score 556 | order by icustay_id; 557 | -------------------------------------------------------------------------------- /analysis/analysis_paper.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Callout Paper Output" 3 | author: "J.D. Raffa" 4 | date: "Sept 5, 2018" 5 | output: 6 | html_document: 7 | fig_caption: yes 8 | fig_height: 8 9 | fig_width: 10 10 | --- 11 | 12 | 13 | Just to review: 14 | 15 | - We decided to only use MICU patients. 16 | - Patients had to link to Census data, which eliminated some of the older ICU stays (pre 2005, and others). 17 | - Excluded 2012 patients, as the quality is suspect. 18 | - Hour of Callout, Hour of Discharge, and Discharge Delay are all related in a somewhat complex fashion. 19 | 20 | 21 | ```{r setup, include=FALSE} 22 | knitr::opts_chunk$set(echo = FALSE,message=FALSE,warning=FALSE) 23 | d.nonphi <- read.csv("../callout.csv") 24 | nrow(d.nonphi) 25 | # Please note, this file is not available for extraction from the "regular"-MIMIC III, hence the "phi" 26 | d.phi <- read.csv("../callout-phi.csv") 27 | nrow(d.phi) 28 | names(d.phi) <- tolower(names(d.phi)) 29 | 30 | library(dplyr);library(Hmisc); library(car) 31 | library(ggplot2) 32 | ``` 33 | 34 | 35 | ```{r} 36 | d.phi %>% full_join(d.nonphi,by="icustay_id") %>% nrow() 37 | d.phi %>% inner_join(d.nonphi,by="icustay_id") %>% nrow() 38 | d.phi %>% full_join(d.nonphi,by="icustay_id") %>% filter(micu==1 | msicu==1) %>% nrow() 39 | d.phi %>% inner_join(d.nonphi,by="icustay_id") %>% filter(micu==1 | msicu==1) %>% nrow() 40 | 41 | d <- d.phi %>% full_join(d.nonphi,by="icustay_id") %>% filter(micu==1 | msicu==1) 42 | d$HOSP_FREE_DAYS <- (28 - d$los_post_icu_days)*(d$los_post_icu_days<28)*(1-d$hospitaldeath) 43 | d$CALLOUT_DURING_ROUNDS <- d$hourofcallout>700 & d$hourofcallout<1200; 44 | d$CALLOUT_DURING_NIGHT <- d$hourofcallout>2300 | d$hourofcallout<700; 45 | d$DISCHARGEDELAY_HOURS <- d$dischargedelay_days*24; 46 | d$hourofcallout2 <- floor(d$hourofcallout/100) + ((d$hourofcallout/100) - floor(d$hourofcallout/100))*100/60 47 | d <- d %>% filter(callout_year<2012 & callout_year>2001) 48 | nrow(d) 49 | d$PROPFULL_BEDS <- rowSums(d[,grep("census",names(d))],na.rm=TRUE)/rowSums(d[,grep("beds",names(d))],na.rm=TRUE) 50 | #d <- d %>% mutate(ethnicity=stringr::str_sub(ethnicity,0,20)) 51 | d <- d %>% mutate(ethnicity=recode_factor(ethnicity,`WHITE`="White", 52 | `WHITE - EASTERN EUROPEAN`="White", 53 | `WHITE - RUSSIAN` = "White", 54 | `WHITE - BRAZILIAN` = "White", 55 | `WHITE - OTHER EUROPEAN` = "White", 56 | `BLACK/AFRICAN AMERICAN` = "African American/Black", 57 | `BLACK/CAPE VERDEAN`= "African American/Black", 58 | `BLACK/HAITIAN`= "African American/Black", 59 | `BLACK/AFRICAN` = "African American/Black", 60 | .default="Other"),MED_SERVICE=service=="MED") 61 | ``` 62 | 63 | 64 | Below are the Overall patient characteristics. 65 | 66 | ```{r} 67 | library(MASS);library(splines);library(mgcv);library(plotly) 68 | d$discharge_time <- d$hourofcallout2 + d$DISCHARGEDELAY_HOURS 69 | d$discharge_time <- ifelse(d$discharge_time<24, d$discharge_time,d$discharge_time - floor(d$discharge_time/24)*24) 70 | d$daypostcall <- floor((d$hourofcallout2 + d$DISCHARGEDELAY_HOURS)/24) 71 | d$postcalldaycat2 <- cut2(d$daypostcall,c(1)) 72 | 73 | library(tableone) 74 | vars1 <- c("micu","age", "callout_month","female","request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","oasis","elixhauser_hospital","ethnicity","MED_SERVICE","HOSP_FREE_DAYS","callout_dayofweek","CALLOUT_DURING_NIGHT","CALLOUT_DURING_ROUNDS","DISCHARGEDELAY_HOURS","hourofcallout2","PROPFULL_BEDS","postcalldaycat2","hospitaldeath","los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "callout_year") 75 | factorVars1 = c("micu", "female","ethnicity","MED_SERVICE", "callout_month", "request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","service","callout_dayofweek","CALLOUT_DURING_ROUNDS","CALLOUT_DURING_NIGHT","postcalldaycat2","hospitaldeath","callout_year") 76 | nonnormal.vars = c("los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "hourofcallout2","HOSP_FREE_DAYS") 77 | 78 | 79 | CreateTableOne(data=d,vars=vars1,factorVars = factorVars1) %>% print( 80 | printToggle = FALSE, 81 | showAllLevels = TRUE, 82 | cramVars = "kon",nonnormal = nonnormal.vars 83 | ) %>% 84 | {data.frame( 85 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 86 | row.names = NULL, 87 | check.names = FALSE, 88 | stringsAsFactors = FALSE)} %>% 89 | knitr::kable(caption="Patient Characteristics Overall") 90 | ``` 91 | 92 | 93 | # Question 1: Who has long discharge delays? 94 | 95 | 96 | ```{r} 97 | CreateTableOne(data=d %>% mutate(DisDelay=cut2(DISCHARGEDELAY_HOURS,c(0,4,8,24))),vars=vars1,factorVars = factorVars1,strata="DisDelay",test=TRUE) %>% print( 98 | printToggle = FALSE, 99 | showAllLevels = TRUE, 100 | cramVars = "kon",nonnormal = nonnormal.vars 101 | ) %>% 102 | {data.frame( 103 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 104 | row.names = NULL, 105 | check.names = FALSE, 106 | stringsAsFactors = FALSE)} %>% 107 | knitr::kable(caption="Patient Characteristics By Discharge Delay Categories (Hours)") 108 | 109 | 110 | ``` 111 | 112 | Determinants of the DD are quite complex, depending on many factors. We instead focus on breaking down DD to >=24 vs <24h 113 | 114 | 115 | 116 | ```{r} 117 | CreateTableOne(data=d %>% mutate(DisDelay=cut2(DISCHARGEDELAY_HOURS,c(24))),vars=vars1,factorVars = factorVars1,strata="DisDelay",test=TRUE) %>% print( 118 | printToggle = FALSE, 119 | showAllLevels = TRUE, 120 | cramVars = "kon",nonnormal = nonnormal.vars 121 | ) %>% 122 | {data.frame( 123 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 124 | row.names = NULL, 125 | check.names = FALSE, 126 | stringsAsFactors = FALSE)} %>% 127 | knitr::kable(caption="Patient Characteristics By Discharge Delay Categories (Hours)") 128 | 129 | 130 | ``` 131 | 132 | We fit a logistic regression model for DD>24 as the "outcome" with: 133 | 134 | 1. Demographics: age, sex, 135 | 2. Requests: tele, resp, mrsa, cdiff, vre 136 | 3. Adjustment for severity/comoribidity/icu los: oasis, exlixhauser, los_pre_callout_days 137 | 4. Possible structural/admin variables: DOW, month, year. 138 | 5. Census variables: type of bed requested, proportion of hospital beds is use. 139 | 140 | as covariates. 141 | 142 | Model selection: fit full model, reduce backwards stepwise, until all variables are significant by LRT. 143 | 144 | AIC/BIC model selection was done first, but used only as a comparison to the manually worked selection (`jdr.ddelay.glm`). 145 | 146 | ```{r} 147 | 148 | library(sjPlot) 149 | 150 | 151 | full.model.glm <- glm(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]" ~ cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + cut2(hourofcallout2,c(7,12,19)),data=d,family="binomial" ) 152 | 153 | drop1(full.model.glm,test="Chisq") 154 | sjPlot::plot_model(full.model.glm,Title="Full Model, ddelay") 155 | 156 | library(MASS) 157 | final.model.glm.bic <- stepAIC(full.model.glm,k=log(nobs(full.model.glm)),trace=0) 158 | sjPlot::plot_model(final.model.glm.bic,Title="BIC Model, ddelay") 159 | drop1(final.model.glm.bic,test="Chisq") 160 | 161 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0) 162 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, ddelay") 163 | drop1(final.model.glm.aic,test="Chisq") 164 | 165 | 166 | 167 | full.model.glm <- glm(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]" ~ cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 168 | 169 | #1 170 | 171 | drop.res <- drop1(full.model.glm ,test="Chisq") 172 | print(drop.res) 173 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 174 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 175 | 176 | 177 | #2 178 | jdr.ddelay.glm <- update( full.model.glm, .~. - cut2(age, g = 3)) 179 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 180 | print(drop.res) 181 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 182 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 183 | 184 | #3 185 | 186 | jdr.ddelay.glm <- update( jdr.ddelay.glm, .~. - cut2(elixhauser_hospital, g = 3)) 187 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 188 | print(drop.res) 189 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 190 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 191 | 192 | #4 193 | 194 | jdr.ddelay.glm <- update( jdr.ddelay.glm, .~. - MED_SERVICE) 195 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 196 | print(drop.res) 197 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 198 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 199 | 200 | #5 201 | 202 | jdr.ddelay.glm <- update( jdr.ddelay.glm, .~. - request_resp) 203 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 204 | print(drop.res) 205 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 206 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 207 | 208 | #6 209 | 210 | jdr.ddelay.glm <- update( jdr.ddelay.glm, .~. - request_tele) 211 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 212 | print(drop.res) 213 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 214 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 215 | 216 | #7 217 | 218 | jdr.ddelay.glm <- update( jdr.ddelay.glm, .~. - cut2(oasis, g = 3)) 219 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 220 | print(drop.res) 221 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 222 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 223 | 224 | #8 225 | 226 | jdr.ddelay.glm <- update( jdr.ddelay.glm, .~. - cut2(los_pre_callout_days, c(1, 3, 7, 28))) 227 | drop.res <- drop1(jdr.ddelay.glm ,test="Chisq") 228 | print(drop.res) 229 | rownames(drop.res)[which.max(drop.res$`Pr(>Chi)`)] 230 | drop.res$`Pr(>Chi)`[which.max(drop.res$`Pr(>Chi)`)] 231 | 232 | #stop 233 | 234 | summary(jdr.ddelay.glm) 235 | sjt.glm(jdr.ddelay.glm) 236 | plot_model(jdr.ddelay.glm) 237 | 238 | # leave outs: 239 | leave.out <- c(1,35,36) 240 | out <- round(cbind(exp(jdr.ddelay.glm$coef)[-leave.out],exp(confint(jdr.ddelay.glm)[-leave.out,]),summary(jdr.ddelay.glm)$coef[-leave.out,4]),3) 241 | 242 | #interactions: using Wald intervals/tests 243 | covM <- summary(jdr.ddelay.glm)$cov.unscale[c(30:31,35:36),c(30:31,35:36)] 244 | Z <- matrix(c(1,0,0,1,1,0,0,1),nr=2); 245 | se <- sqrt(diag(Z%*%covM%*%t(Z))) 246 | out <- rbind(out,round(cbind(exp(jdr.ddelay.glm$coef[30:31] +jdr.ddelay.glm$coef[35:36]),exp(jdr.ddelay.glm$coef[30:31] +jdr.ddelay.glm$coef[35:36] - qnorm(0.975)*se), exp(jdr.ddelay.glm$coef[30:31] +jdr.ddelay.glm$coef[35:36] + qnorm(0.975*se)),pnorm(-(jdr.ddelay.glm$coef[30:31] +jdr.ddelay.glm$coef[35:36])/se)*2),3)) 247 | write.csv(file="out-table-ddelay.csv",out) # CSV was used in the paper, after formatting, rounding, etc. 248 | 249 | ``` 250 | 251 | ## Answer 1: 252 | 253 | **Solid evidence**: When Hospital is near or over capacity, when mrsa/cdiff have to be taken into account (more likely); When the callout is made during rounds or in more recent calendar year (less likely). 254 | 255 | **Less Solid Evidence**: All of the above **PLUS**: On certain days of the week, sicker patients (OASIS), certain months, vre (more likely); Female (less likely). Effect modified of "first available bed" and propfull beds. E.g., when hospital is <90% used: "first available bed" => less likely to have a long delay, but effect is negated or reversed when hospitals are full. 256 | 257 | 258 | # Question 2: Do people who have long discharge delays (>24 hours) die more often? 259 | 260 | 261 | - Marginal evidence in above table: 5.4% vs 6.8% (p=0.06), but we know these patients are generally sicker, more likely to have concerns about cdiff, vre, mrsa, etc. 262 | 263 | 264 | We will build a model in a similar way as before, but add the long discharge delay (>24hrs) into a model for hospital mortality, retaining our exposure of interested (DD) throughout. 265 | ```{r} 266 | vars1 <- c("micu","age", "callout_month","female","request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","oasis","elixhauser_hospital","ethnicity","MED_SERVICE","HOSP_FREE_DAYS","callout_dayofweek","CALLOUT_DURING_NIGHT","CALLOUT_DURING_ROUNDS","DISCHARGEDELAY_HOURS","hourofcallout2","PROPFULL_BEDS","postcalldaycat2","los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "callout_year") 267 | factorVars1 = c("micu", "female","ethnicity","MED_SERVICE", "callout_month", "request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","service","callout_dayofweek","CALLOUT_DURING_ROUNDS","CALLOUT_DURING_NIGHT","postcalldaycat2","callout_year") 268 | nonnormal.vars = c("los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "hourofcallout2","HOSP_FREE_DAYS") 269 | 270 | 271 | CreateTableOne(data=d,vars=vars1,factorVars = factorVars1,strata="hospitaldeath") %>% print( 272 | printToggle = FALSE, 273 | showAllLevels = TRUE, 274 | cramVars = "kon",nonnormal = nonnormal.vars 275 | ) %>% 276 | {data.frame( 277 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 278 | row.names = NULL, 279 | check.names = FALSE, 280 | stringsAsFactors = FALSE)} %>% 281 | knitr::kable(caption="Patient Characteristics Overall") 282 | 283 | ``` 284 | 285 | ```{r} 286 | full.model.glm <- glm(hospitaldeath ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 287 | 288 | drop1(full.model.glm,test="Chisq") 289 | sjPlot::plot_model(full.model.glm,Title="Full Model, mortality") 290 | 291 | library(MASS) 292 | final.model.glm.bic <- stepAIC(full.model.glm,k=log(nobs(full.model.glm)),trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 293 | sjPlot::plot_model(final.model.glm.bic,Title="BIC Model, mortality") 294 | drop1(final.model.glm.bic,test="Chisq") 295 | 296 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 297 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, mortality") 298 | drop1(final.model.glm.aic,test="Chisq") 299 | 300 | 301 | #jdr 302 | full.model.glm <- glm(hospitaldeath ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 303 | summary(full.model.glm) 304 | drop1(full.model.glm) 305 | 306 | #1 307 | 308 | 309 | 310 | drop.res <- drop1(full.model.glm ,test="Chisq") 311 | print(drop.res) 312 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 313 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 314 | 315 | 316 | #2 317 | jdr.death.glm <- update( full.model.glm, .~. - request_resp) 318 | drop.res <- drop1(jdr.death.glm,test="Chisq") 319 | print(drop.res) 320 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 321 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 322 | 323 | #3 324 | jdr.death.glm <- update( jdr.death.glm, .~. - request_mrsa) 325 | drop.res <- drop1(jdr.death.glm,test="Chisq") 326 | print(drop.res) 327 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 328 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 329 | 330 | #interaction almost same as age! 331 | #4 332 | jdr.death.glm <- update( jdr.death.glm, .~. - as.factor(callout_wardid == 1):cut2(PROPFULL_BEDS, c(0.9, 1))) 333 | drop.res <- drop1(jdr.death.glm,test="Chisq") 334 | print(drop.res) 335 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 336 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 337 | 338 | #5 339 | 340 | jdr.death.glm <- update( jdr.death.glm, .~. - cut2(PROPFULL_BEDS, c(0.9, 1))) 341 | drop.res <- drop1(jdr.death.glm,test="Chisq") 342 | print(drop.res) 343 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 344 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 345 | 346 | #6 347 | 348 | jdr.death.glm <- update( jdr.death.glm, .~. - cut2(age, g = 3)) 349 | drop.res <- drop1(jdr.death.glm,test="Chisq") 350 | print(drop.res) 351 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 352 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 353 | 354 | #7 355 | 356 | jdr.death.glm <- update( jdr.death.glm, .~. - as.factor(callout_dayofweek)) 357 | drop.res <- drop1(jdr.death.glm,test="Chisq") 358 | print(drop.res) 359 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 360 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 361 | 362 | #8 363 | 364 | jdr.death.glm <- update( jdr.death.glm, .~. - as.factor(callout_month)) 365 | drop.res <- drop1(jdr.death.glm,test="Chisq") 366 | print(drop.res) 367 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 368 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 369 | 370 | #9 371 | 372 | jdr.death.glm <- update( jdr.death.glm, .~. - MED_SERVICE) 373 | drop.res <- drop1(jdr.death.glm,test="Chisq") 374 | print(drop.res) 375 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 376 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 377 | 378 | 379 | #10 380 | 381 | jdr.death.glm <- update( jdr.death.glm, .~. - relevel(cut2(hourofcallout2, c(7, 12, 19)), "[ 7.000,12.000)")) 382 | drop.res <- drop1(jdr.death.glm,test="Chisq") 383 | print(drop.res) 384 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 385 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 386 | 387 | #11 388 | 389 | jdr.death.glm <- update( jdr.death.glm, .~. - female) 390 | drop.res <- drop1(jdr.death.glm,test="Chisq") 391 | print(drop.res) 392 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 393 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 394 | 395 | 396 | plot_model(jdr.death.glm) 397 | sjt.glm(jdr.death.glm) 398 | 399 | leave.out <- 1 400 | out2 <- round(cbind(exp(jdr.death.glm$coef)[-leave.out],exp(confint(jdr.death.glm)[-leave.out,]),summary(jdr.death.glm)$coef[-leave.out,4]),3) 401 | print(out2) 402 | write.csv(file="out-table-death.csv",out2) 403 | 404 | 405 | ``` 406 | 407 | ## Answer 2: After adjusting for potential confounders, there is no statistically significant evidence that a long delay produces a better hospital mortality outcome. 408 | 409 | 410 | # Question 3: Do individuals with a long discharge delay have smaller numbers of hospital free days? 411 | 412 | HFDs are technically ordinal, so we tried Proportional Odds Logistic Regression. This didn't make it into the paper. 413 | 414 | There is not very much evidence in the above table that hospital free days is impacted by long discharge delays (21.36 vs 21.09, p=0.265) 415 | 416 | We can look at it using an empirical cumulative distribution function: 417 | ```{r} 418 | 419 | spl <- split(d,cut2(d$DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") 420 | plot(ecdf(floor(spl[[1]]$HOSP_FREE_DAYS)),main="ecdf red=short delay",lwd=2) 421 | lines(ecdf(floor(spl[[2]]$HOSP_FREE_DAYS)),col="red") 422 | 423 | ``` 424 | 425 | 426 | 427 | # Question 4: Do people who have long discharge delays (>24 hours) have a "good outcome" defined as a short post discharge LOS (<1 week) and survive? 428 | 429 | ```{r} 430 | with(d,table(HOSP_FREE_DAYS>21)) 431 | with(d,table(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]")) 432 | with(d,table(HOSP_FREE_DAYS>21,cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]")) 433 | 434 | prop.table(with(d,table(HOSP_FREE_DAYS>21,cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]")),2) 435 | chisq.test(with(d,table(HOSP_FREE_DAYS>21,cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 436 | 437 | vars1 <- c("micu","age", "callout_month","female","request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","oasis","elixhauser_hospital","ethnicity","MED_SERVICE","HOSP_FREE_DAYS","callout_dayofweek","CALLOUT_DURING_NIGHT","CALLOUT_DURING_ROUNDS","DISCHARGEDELAY_HOURS","hourofcallout2","PROPFULL_BEDS","postcalldaycat2","los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "callout_year","hospitaldeath") 438 | factorVars1 = c("micu", "female","ethnicity","MED_SERVICE", "callout_month", "request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","service","callout_dayofweek","CALLOUT_DURING_ROUNDS","CALLOUT_DURING_NIGHT","postcalldaycat2","callout_year") 439 | nonnormal.vars = c("los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "hourofcallout2") 440 | 441 | d$GOOD_HFDs <- d$HOSP_FREE_DAYS>21; 442 | CreateTableOne(data=d,vars=vars1,factorVars = factorVars1,strata="GOOD_HFDs") %>% print( 443 | printToggle = FALSE, 444 | showAllLevels = TRUE, 445 | cramVars = "kon",nonnormal = nonnormal.vars 446 | ) %>% 447 | {data.frame( 448 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 449 | row.names = NULL, 450 | check.names = FALSE, 451 | stringsAsFactors = FALSE)} %>% 452 | knitr::kable(caption="Patient Characteristics Overall") 453 | 454 | ``` 455 | 456 | 457 | 458 | ```{r} 459 | full.model.glm <- glm(I(HOSP_FREE_DAYS>21) ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 460 | 461 | drop1(full.model.glm,test="Chisq") 462 | sjPlot::plot_model(full.model.glm,Title="Full Model, mortality") 463 | 464 | library(MASS) 465 | final.model.glm.bic <- stepAIC(full.model.glm,k=log(nobs(full.model.glm)),trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 466 | sjPlot::plot_model(final.model.glm.bic,Title="BIC Model, mortality") 467 | drop1(final.model.glm.bic,test="Chisq") 468 | 469 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 470 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, mortality") 471 | drop1(final.model.glm.aic,test="Chisq") 472 | 473 | #jdr 474 | full.model.glm <- glm(I(HOSP_FREE_DAYS>21) ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 475 | summary(full.model.glm) 476 | drop1(full.model.glm) 477 | 478 | #1 479 | 480 | 481 | 482 | drop.res <- drop1(full.model.glm ,test="Chisq") 483 | print(drop.res) 484 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 485 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 486 | 487 | #2 488 | jdr.goodhfd.glm <- update( full.model.glm, .~. - as.factor(callout_wardid == 1):cut2(PROPFULL_BEDS, c(0.9, 1))) 489 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 490 | print(drop.res) 491 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 492 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 493 | 494 | 495 | #3 496 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. - MED_SERVICE) 497 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 498 | print(drop.res) 499 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 500 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 501 | 502 | #4 503 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. - request_tele) 504 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 505 | print(drop.res) 506 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 507 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 508 | 509 | #5 510 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. - as.factor(callout_dayofweek)) 511 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 512 | print(drop.res) 513 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 514 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 515 | 516 | #6 517 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. -cut2(PROPFULL_BEDS, c(0.9, 1))) 518 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 519 | print(drop.res) 520 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 521 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 522 | 523 | 524 | #7 525 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. -female) 526 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 527 | print(drop.res) 528 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 529 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 530 | 531 | 532 | #8 533 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. -as.factor(callout_month)) 534 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 535 | print(drop.res) 536 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 537 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 538 | 539 | #9 540 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. -relevel(cut2(hourofcallout2, c(7, 12, 19)), "[ 7.000,12.000)")) 541 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 542 | print(drop.res) 543 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 544 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 545 | 546 | 547 | #10 548 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. -request_mrsa) 549 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 550 | print(drop.res) 551 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 552 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 553 | 554 | 555 | #11 556 | jdr.goodhfd.glm <- update( jdr.goodhfd.glm, .~. -request_resp) 557 | drop.res <- drop1(jdr.goodhfd.glm,test="Chisq") 558 | print(drop.res) 559 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 560 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 561 | 562 | plot_model(jdr.goodhfd.glm) 563 | sjt.glm(jdr.goodhfd.glm) 564 | 565 | leave.out <- 1 566 | out3 <- round(cbind(exp(jdr.goodhfd.glm$coef)[-leave.out],exp(confint(jdr.goodhfd.glm)[-leave.out,]),summary(jdr.goodhfd.glm)$coef[-leave.out,4]),3) 567 | print(out3) 568 | write.csv(file="out-table-good_hfd.csv",out3) 569 | 570 | 571 | ``` 572 | 573 | ## Answer 4: After adjusting for potential confounders, there is no statistically significant evidence that a long delay produces a better HFD outcome (>21 days). 574 | 575 | 576 | # Question 5: Do people who have long discharge delays (>24 hours) have a "bad outcome" defined as a long post discharge LOS (>3 week) or death? 577 | 578 | ```{r} 579 | with(d,table(HOSP_FREE_DAYS<=7)) 580 | with(d,table(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]")) 581 | with(d,table(HOSP_FREE_DAYS<=7,cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]")) 582 | 583 | prop.table(with(d,table(HOSP_FREE_DAYS<=7,cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]")),2) 584 | chisq.test(with(d,table(HOSP_FREE_DAYS<=7,cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 585 | ``` 586 | 587 | 588 | ```{r} 589 | vars1 <- c("micu","age", "callout_month","female","request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","oasis","elixhauser_hospital","ethnicity","MED_SERVICE","HOSP_FREE_DAYS","callout_dayofweek","CALLOUT_DURING_NIGHT","CALLOUT_DURING_ROUNDS","DISCHARGEDELAY_HOURS","hourofcallout2","PROPFULL_BEDS","postcalldaycat2","los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "callout_year","hospitaldeath") 590 | factorVars1 = c("micu", "female","ethnicity","MED_SERVICE", "callout_month", "request_tele","request_resp","request_cdiff","request_mrsa", "request_vre","service","callout_dayofweek","CALLOUT_DURING_ROUNDS","CALLOUT_DURING_NIGHT","postcalldaycat2","callout_year") 591 | nonnormal.vars = c("los_preicu_days","los_post_callout_days","los_post_icu_days","los_pre_callout_days", "hourofcallout2") 592 | 593 | d$BAD_HFDs <- d$HOSP_FREE_DAYS<=7; 594 | CreateTableOne(data=d,vars=vars1,factorVars = factorVars1,strata="BAD_HFDs") %>% print( 595 | printToggle = FALSE, 596 | showAllLevels = TRUE, 597 | cramVars = "kon",nonnormal = nonnormal.vars 598 | ) %>% 599 | {data.frame( 600 | variable_name = gsub(" ", " ", rownames(.), fixed = TRUE), ., 601 | row.names = NULL, 602 | check.names = FALSE, 603 | stringsAsFactors = FALSE)} %>% 604 | knitr::kable(caption="Patient Characteristics Overall") 605 | 606 | ``` 607 | 608 | 609 | ```{r} 610 | full.model.glm <- glm(I(HOSP_FREE_DAYS<=7) ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 611 | 612 | drop1(full.model.glm,test="Chisq") 613 | sjPlot::plot_model(full.model.glm,Title="Full Model, mortality") 614 | 615 | library(MASS) 616 | final.model.glm.bic <- stepAIC(full.model.glm,k=log(nobs(full.model.glm)),trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 617 | sjPlot::plot_model(final.model.glm.bic,Title="BIC Model, mortality") 618 | drop1(final.model.glm.bic,test="Chisq") 619 | 620 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 621 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, mortality") 622 | drop1(final.model.glm.aic,test="Chisq") 623 | 624 | 625 | #jdr 626 | full.model.glm <- glm(I(HOSP_FREE_DAYS<=7) ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.000,12.000)"),data=d,family="binomial" ) 627 | summary(full.model.glm) 628 | drop1(full.model.glm) 629 | 630 | #1 631 | 632 | 633 | 634 | drop.res <- drop1(full.model.glm ,test="Chisq") 635 | print(drop.res) 636 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 637 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 638 | 639 | #2 640 | jdr.badhfd.glm <- update( full.model.glm, .~. - request_mrsa) 641 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 642 | print(drop.res) 643 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 644 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 645 | 646 | 647 | #3 648 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - as.factor(callout_dayofweek)) 649 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 650 | print(drop.res) 651 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 652 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 653 | 654 | 655 | #4 656 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - as.factor(callout_wardid == 1):cut2(PROPFULL_BEDS, c(0.9, 1))) 657 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 658 | print(drop.res) 659 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 660 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 661 | 662 | 663 | #5 664 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - cut2(PROPFULL_BEDS, c(0.9, 1))) 665 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 666 | print(drop.res) 667 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 668 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 669 | 670 | 671 | #6 672 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - as.factor(callout_month)) 673 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 674 | print(drop.res) 675 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 676 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 677 | 678 | #7 679 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - request_resp) 680 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 681 | print(drop.res) 682 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 683 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 684 | 685 | #8 686 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - MED_SERVICE) 687 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 688 | print(drop.res) 689 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 690 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 691 | 692 | 693 | #9 694 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - female) 695 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 696 | print(drop.res) 697 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 698 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 699 | 700 | 701 | 702 | #10 703 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - relevel(cut2(hourofcallout2, c(7, 12, 19)), "[ 7.000,12.000)")) 704 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 705 | print(drop.res) 706 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 707 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 708 | 709 | #11 710 | jdr.badhfd.glm <- update( jdr.badhfd.glm, .~. - cut2(age, g = 3)) 711 | drop.res <- drop1(jdr.badhfd.glm,test="Chisq") 712 | print(drop.res) 713 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 714 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 715 | 716 | 717 | plot_model(jdr.badhfd.glm) 718 | sjt.glm(jdr.badhfd.glm) 719 | 720 | leave.out <- 1 721 | out4 <- round(cbind(exp(jdr.badhfd.glm$coef)[-leave.out],exp(confint(jdr.badhfd.glm)[-leave.out,]),summary(jdr.badhfd.glm)$coef[-leave.out,4]),3) 722 | print(out4) 723 | write.csv(file="out-table-bad_hfd.csv",out4) 724 | 725 | ``` 726 | 727 | ## Answer 5: After adjusting for potential confounders, there is no statistically significant evidence that a long delay yield lower rates of poor HFD outcome (<7 days). 728 | 729 | 730 | # Question 6: Do survivors of the hospital stay who have long discharge delays have longer log(LOS_post_icu)? 731 | 732 | Post ICU days were similar in the above table 5.95 vs 6.19 (p=0.35) 733 | 734 | 735 | 736 | 737 | 738 | ```{r} 739 | full.model.glm <- lm(log(los_post_icu_days + 1) ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.00,12.00)"),data=(d %>% filter(hospitaldeath==0)) ) 740 | 741 | drop1(full.model.glm,test="F") 742 | sjPlot::plot_model(full.model.glm,Title="Full Model, LOS") 743 | 744 | library(MASS) 745 | final.model.glm.bic <- stepAIC(full.model.glm,k=log(nobs(full.model.glm)),trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 746 | sjPlot::plot_model(final.model.glm.bic,Title="BIC Model, LOS") 747 | drop1(final.model.glm.bic,test="F") 748 | 749 | final.model.glm.aic <- stepAIC(full.model.glm,trace=0,scope=list(lower=~I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]"))) 750 | sjPlot::plot_model(final.model.glm.aic,Title="AIC Model, LOS") 751 | drop1(final.model.glm.aic,test="F") 752 | 753 | 754 | #jdr 755 | full.model.glm <- lm(log(los_post_icu_days + 1) ~ I(cut2(DISCHARGEDELAY_HOURS,c(24))=="[ 24.000,129.566]") +cut2(oasis,g=3) + cut2(age,g=3) + female + request_tele + request_resp + request_mrsa + request_vre + request_cdiff + cut2(elixhauser_hospital,g=3) + cut2(los_pre_callout_days,c(1,3,7,28)) + as.factor(callout_month) + as.factor(callout_year) + as.factor(callout_dayofweek) + as.factor(callout_wardid==1)*cut2(PROPFULL_BEDS,c(0.9,1)) + MED_SERVICE + relevel(cut2(hourofcallout2,c(7,12,19)),"[ 7.00,12.00)"),data=(d %>% filter(hospitaldeath==0)) ) 756 | summary(full.model.glm) 757 | drop1(full.model.glm) 758 | 759 | #1 760 | 761 | 762 | 763 | drop.res <- drop1(full.model.glm ,test="Chisq") 764 | print(drop.res) 765 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 766 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 767 | 768 | #2 769 | jdr.los.glm <- update( full.model.glm, .~. - as.factor(callout_wardid == 1):cut2(PROPFULL_BEDS, c(0.9, 1))) 770 | drop.res <- drop1(jdr.los.glm,test="Chisq") 771 | print(drop.res) 772 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 773 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 774 | 775 | 776 | #3 777 | jdr.los.glm <- update( jdr.los.glm, .~. -as.factor(callout_month)) 778 | drop.res <- drop1(jdr.los.glm,test="Chisq") 779 | print(drop.res) 780 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 781 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 782 | 783 | 784 | #4 785 | 786 | 787 | jdr.los.glm <- update( jdr.los.glm, .~. -as.factor(callout_year)) 788 | drop.res <- drop1(jdr.los.glm,test="Chisq") 789 | print(drop.res) 790 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 791 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 792 | 793 | #5 794 | 795 | jdr.los.glm <- update( jdr.los.glm, .~. -relevel(cut2(hourofcallout2, c(7, 12, 19)), "[ 7.00,12.00)")) 796 | drop.res <- drop1(jdr.los.glm,test="Chisq") 797 | print(drop.res) 798 | rownames(drop.res)[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 799 | drop.res$`Pr(>Chi)`[-2][which.max(drop.res$`Pr(>Chi)`[-2])] 800 | 801 | 802 | plot_model(jdr.los.glm) 803 | sjt.glm(jdr.los.glm) 804 | 805 | leave.out <- 1 806 | out5 <- round(cbind((jdr.los.glm$coef)[-leave.out],(confint(jdr.los.glm)[-leave.out,]),summary(jdr.los.glm)$coef[-leave.out,4]),3) 807 | print(out5) 808 | write.csv(file="out-table-los.csv",out5) 809 | 810 | 811 | #plot(jdr.los.glm) 812 | 813 | 814 | ``` 815 | 816 | ## Answer 6: After adjusting for potential confounders, There appears to be some evidence that those with long delays have about 12\% shorter post ICU LOS. 817 | 818 | 819 | --------------------------------------------------------------------------------