Adult Decompensation OR To ICU Sankey Diagram

Author: Ziyuan Shen

Last Revision: 11/05/2019

The notebook refers sankey diagram of CTICU project. The author thanks Shujin Zhong for coding support.

Visualization Goal

Identify which units send transfers to the ORs and which ICUs receive transfers out of the ORs.

Visualization Specifics

  • subcohort: all encounters which have OR -> ICU transfers (within adult decompensation cohort)
  • number of encounters: 13541 (number of encounters of the entire cohort: 177213)
  • each encounter flow is generated from hospital Admssion to Discharge
  • lower boundary of stay in each department: 30 minutes
  • lower boundary of the number of each distinct type of transfer: 150, 120
    the smaller this threshold is, the more types of transfers get visualized on the Sankey diagram;
    the larger this threshold is, the more discernible the Sankey diagram is.
  • hospital departments (including Admission and Discharge here) are colored as:
          Admission
          ED (Emergency Department)
          floor
          stepdown
          surgery (stepdown)
          ICU (Intensive Care Unit)
          Discharge
  • each node in sankey diagram is named by the department name with a prefix i- denoting it's the ith transfer within each encounter, and a suffix _j denoting it's the jth time the patient enters the same department during the encounter
  • department names are truncated to make visualization discernible, please refer the raw code for raw department names
In [3]:
import pandas as pd
import numpy as np
import plotly
plotly.offline.init_notebook_mode()

hospital_units = pd.read_csv('../../../../Data/metadata/hospital_unit/ad_hospital_units.csv')

def simplify_name(x):
    if '-' not in x or 'PERIOP' in x:
        return x
    prefix = x.split('-')[0] + '-'
    dep_name = x.split('-')[1].split('_')[0]
    suffix = '_' + x.split('_')[1]
    return prefix + dep_name.split(' ')[0] + ' ' + dep_name.split(' ')[1] + suffix

def categorize_dep(level_of_care, specialty):
    if specialty=='emergency':
        return 'ED'
    if level_of_care=='intensive care':
        return 'ICU'
    if specialty=='surgery':
        return 'surgery'
    if level_of_care=='stepdown':
        return 'stepdown'
    if level_of_care=='regular':
        return 'floor'

hospital_units['dep_category'] = hospital_units.apply(lambda x: categorize_dep(x.level_of_care, x.specialty), axis=1)
hospital_units = hospital_units[['DEPARTMENT_NAME', 'dep_category']]
hospital_units = hospital_units.drop_duplicates(subset=['DEPARTMENT_NAME'])

def get_dep_category(dep_name):
    return hospital_units.loc[hospital_units['DEPARTMENT_NAME']==dep_name]['dep_category'].values[0]

color_dic = {'Admission': 'rgba(239, 239, 129, 1)', # yellow
             'ED': 'rgba(117, 164, 250, 1)', # blue
             'floor': 'rgba(123, 201, 108, 1)', # green
             'stepdown': 'rgba(247, 163, 80, 1)', # orange
             'surgery': 'rgba(219, 102, 255, 1)', # purple
             'ICU': 'rgba(252, 64, 64, 1)', # red
             'Discharge': 'rgba(69, 56, 253, 1)' # dark blue
            }

def find_unit_color(unit):
    if 'DUH N9000-S SHORT STAY' in unit:
        unit = get_dep_category('DUH N9000-S SHORT STAY')
    elif '-' in unit:
        unit = unit.split('-')[1].split('_')[0]
        unit = get_dep_category(unit)
    for key in color_dic.keys():
        if key == unit:
            return color_dic[key]
In [4]:
Source = [46, 13, 25, 43, 39, 39, 30, 19, 49, 2, 48, 14, 14, 14, 20, 20, 23, 44, 44, 44, 26, 26, 26, 36, 36, 36, 
          45, 33, 37, 37, 37, 10, 38, 8, 40, 40, 40, 40, 40, 0, 0, 0, 17, 3, 32, 12, 11, 1, 50, 27, 22, 22, 4, 
          24, 29, 41, 15, 47, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7]

Target = [37, 37, 36, 21, 23, 37, 37, 37, 37, 37, 37, 23, 44, 26, 23, 26, 11, 8,  1, 50, 17, 27,  9,  3, 32,  9, 
          28,  5, 38, 40,  0, 38, 35,  9, 4, 41, 15, 47,  6, 24, 29,  9,  9,  9,  9, 18,  9,  9,  9,  9, 34, 31, 
          9,  9,  9,  9,  9,  9,  9, 46, 16, 13, 42, 25, 51, 43, 39, 30, 19, 49,  2, 48, 14, 20]

Value = [419,  720,  742,  245,  188,  379, 1095, 1186,  182,  272,  212, 346, 1279,  713,  599,  204, 290, 458, 
         459,  442,  391,  152, 347,  277,  166,  225,  211,  251,  417, 3277, 1243,  214,  176, 393,  633,  733, 
         447,  708,  487,  190,  251,  704,  380,  237, 152,  166,  241,  359,  370,  265,  434,  196,  588, 
         238,  241, 678,  404,  649,  439,  455,  179,  742,  343,  758,  437,  255, 1819, 1170, 1260,  206, 352, 
         305, 2404, 1100]

name_numb = {'3-DMP 8E NEURO ICU_1': 0,
 '3-DUH N3100 CARDIOTHORACIC SURGERY_1': 1,
 '1-DUH N7100 CARDIOLOGY_1': 2,
 '3-DRAH 5TH FLOOR NURSING UNIT_1': 3,
 '4-DMP 7E CARDIOTHORACIC SURGERY_1': 4,
 '3-DRH CRITICAL CARE_1': 5,
 '4-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_2': 6,
 'Admission': 7,
 '3-DMP 7E CARDIOTHORACIC SURGERY_1': 8,
 'Discharge': 9,
 '2-DUKE NORTH PERIOP_1': 10,
 '3-DUH N2300 GENERAL SURGERY_1': 11,
 '3-DRH PERIOP_1': 12,
 '1-DMP 8W NEUROLOGY/NEUROSURGERY_1': 13,
 '1-DUKE MED PAV PERIOP_1': 14,
 '4-DUH N3100 CARDIOTHORACIC SURGERY_2': 15,
 '1-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 16,
 '3-DMP 8W NEUROLOGY/NEUROSURGERY_1': 17,
 '4-DRH CRITICAL CARE_1': 18,
 '1-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 19,
 '1-DUKE NORTH PERIOP_1': 20,
 '2-DRH CRITICAL CARE_1': 21,
 '3-DUKE MED PAV PERIOP_1': 22,
 '2-DMP 6W TRAUMA SICU_1': 23,
 '4-DMP 8W NEUROLOGY/NEUROSURGERY_1': 24,
 '1-DRAH PERIOP_1': 25,
 '2-DMP 8E NEURO ICU_1': 26,
 '3-DUH N4100 NEUROSURGERY_1': 27,
 '3-DRAH INTENSIVE  CARE UNIT_1': 28,
 '4-DMP 8W NEUROLOGY/NEUROSURGERY_2': 29,
 '1-DUH N3100 CARDIOTHORACIC SURGERY_1': 30,
 '4-DMP 8E NEURO ICU_1': 31,
 '3-DRAH NEUROSCIENCE UNIT_1': 32,
 '2-DRH PERIOP_1': 33,
 '4-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 34,
 '4-DUH N2300 GENERAL SURGERY_1': 35,
 '2-DRAH INTENSIVE  CARE UNIT_1': 36,
 '2-DUKE MED PAV PERIOP_1': 37,
 '3-DMP 6W TRAUMA SICU_1': 38,
 '1-DUH EMERGENCY DEPT_1': 39,
 '3-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 40,
 '4-DUH N3100 CARDIOTHORACIC SURGERY_1': 41,
 '1-DRAH EMERGENCY DEPARTMENT_1': 42,
 '1-DRH PERIOP_1': 43,
 '2-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 44,
 '2-DRAH PERIOP_1': 45,
 '1-DMP 7E CARDIOTHORACIC SURGERY_1': 46,
 '4-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 47,
 '1-DUH N7300 CARDIOLOGY_1': 48,
 '1-DUH N4100 NEUROSURGERY_1': 49,
 '3-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 50,
 '1-DRH EMERGENCY DEPARTMENT_1': 51}

name_numb_simplified = {simplify_name(key): value for key, value in name_numb.items()}
test = pd.DataFrame({'From': Source, 'To': Target, 'Values': Value})
cn = list(map(find_unit_color, name_numb.keys()))
cl = list(map(lambda x: x.replace('1)', '0.5)'), cn))
cn = np.array(cn)
cl = np.array(cl)

test['Colors'] = cl[test['From'].values.astype(int)]

data_trace = dict(
    type='sankey',
    arrangement = "freeform",
    domain = dict(
      x =  [0,1],
      y =  [0,1]
    ),
    orientation = "h",
    valueformat = ".0f",
    
    node = dict(
      pad = 5,
      thickness = 5,
      label =  list(name_numb_simplified),
      color =  cn
    ),
    link = dict(
      source = test['From'],
      target = test['To'],
      value = test['Values'],
      color = test['Colors'],
  ))
layout =  dict(
    title = "Adult Decompensation OR To ICU Subcohort Patient Flow",
    titlefont = dict(size = 20),
    font = dict(size = 10)
)

Number of distinct type of transfers threshold: 150

In [5]:
fig = dict(data=[data_trace], layout=layout)
plotly.offline.iplot(fig, validate=False)
In [6]:
Source = [58, 18, 15, 29, 35, 63, 53, 47, 47, 47, 34, 21, 61,  2, 60, 56, 16, 16, 16, 22, 22, 27, 27, 27, 
          27, 54, 54, 54, 30, 30, 30, 43, 43, 43, 55, 38, 45, 45, 45, 45, 10, 46,  8, 48, 48, 48, 48, 48, 
          48,  0,  0, 0, 19,  3, 37,  5, 13, 12,  1, 62, 31, 39, 26, 26, 26,  4, 57, 41, 41, 28, 33, 23, 
          42, 50, 17, 59,  6, 24, 44, 64,  7,  7,  7,  7,  7, 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7, 
          7, 7]

Target = [45, 45, 45, 43, 38, 25, 25, 27, 30, 45, 45, 45, 45, 45, 45, 45, 27, 54, 30, 27, 30, 11, 12, 31, 
          39,  8,  1, 62, 19, 31,  9,  3, 37,  9, 32,  5, 46, 48, 40,  0, 46, 42,  9,  4, 57, 50, 17, 59, 
          6, 28, 33, 9,  9,  9,  9, 23, 20,  9,  9,  9,  9,  9, 41, 36, 14,  9,  9, 44, 64,  9,  9,  9, 
          9,  9,  9,  9,  9, 51,  9,  9, 58, 18, 49, 15, 52, 29, 35, 63, 53, 47, 34, 21, 61,  2, 60, 56, 
          16, 22]

Value = [419,  131,  720,  742,  139,  123,  245,  188,  122,  379, 1095, 1186,  182,  272,  212,  128, 
         346, 1279,  713,  599,  204,  123, 290,  121,  146,  458,  459,  442,  391,  152,  347,  277, 
         166, 225,  211,  251,  417, 3277,  130, 1243,  214,  176,  393,  633, 135,  733,  447,  708, 
         487,  190,  251,  704,  380,  237,  152, 136,  166,  241,  359,  370,  265,  137,  434,  196, 
         125,  588, 122,  130,  121,  238,  241,  131,  146,  678,  404,  649,  439, 148,  134,  140, 
         455,  179,  134,  742,  343,  758,  143,  437, 255, 1819, 1170, 1260,  206,  352,  305,  138, 
         2404, 1100]

name_numb = {'3-DMP 8E NEURO ICU_1': 0,
 '3-DUH N3100 CARDIOTHORACIC SURGERY_1': 1,
 '1-DUH N7100 CARDIOLOGY_1': 2,
 '3-DRAH 5TH FLOOR NURSING UNIT_1': 3,
 '4-DMP 7E CARDIOTHORACIC SURGERY_1': 4,
 '3-DRH CRITICAL CARE_1': 5,
 '4-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_2': 6,
 'Admission': 7,
 '3-DMP 7E CARDIOTHORACIC SURGERY_1': 8,
 'Discharge': 9,
 '2-DUKE NORTH PERIOP_1': 10,
 '3-DUH N2100 GENERAL SURGERY_1': 11,
 '3-DUH N2300 GENERAL SURGERY_1': 12,
 '3-DRH PERIOP_1': 13,
 '4-DMP 8E NEURO ICU_2': 14,
 '1-DMP 8W NEUROLOGY/NEUROSURGERY_1': 15,
 '1-DUKE MED PAV PERIOP_1': 16,
 '4-DUH N3100 CARDIOTHORACIC SURGERY_2': 17,
 '1-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 18,
 '3-DMP 8W NEUROLOGY/NEUROSURGERY_1': 19,
 '4-DRH CRITICAL CARE_1': 20,
 '1-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 21,
 '1-DUKE NORTH PERIOP_1': 22,
 '4-DRH 52 TELEMETRY_2': 23,
 '4-DUKE MED PAV PERIOP_1': 24,
 '2-DRH CRITICAL CARE_1': 25,
 '3-DUKE MED PAV PERIOP_1': 26,
 '2-DMP 6W TRAUMA SICU_1': 27,
 '4-DMP 8W NEUROLOGY/NEUROSURGERY_1': 28,
 '1-DRAH PERIOP_1': 29,
 '2-DMP 8E NEURO ICU_1': 30,
 '3-DUH N4100 NEUROSURGERY_1': 31,
 '3-DRAH INTENSIVE  CARE UNIT_1': 32,
 '4-DMP 8W NEUROLOGY/NEUROSURGERY_2': 33,
 '1-DUH N3100 CARDIOTHORACIC SURGERY_1': 34,
 '1-DRH 52 TELEMETRY_1': 35,
 '4-DMP 8E NEURO ICU_1': 36,
 '3-DRAH NEUROSCIENCE UNIT_1': 37,
 '2-DRH PERIOP_1': 38,
 '3-DUH N6300 URO/OTO/OPTH/PLAS/GYN SURGERY_1': 39,
 '3-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_2': 40,
 '4-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 41,
 '4-DUH N2300 GENERAL SURGERY_1': 42,
 '2-DRAH INTENSIVE  CARE UNIT_1': 43,
 '5-DUH N3100 CARDIOTHORACIC SURGERY_1': 44,
 '2-DUKE MED PAV PERIOP_1': 45,
 '3-DMP 6W TRAUMA SICU_1': 46,
 '1-DUH EMERGENCY DEPT_1': 47,
 '3-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 48,
 '1-DMP 8E NEURO ICU_1': 49,
 '4-DUH N3100 CARDIOTHORACIC SURGERY_1': 50,
 '5-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 51,
 '1-DRAH EMERGENCY DEPARTMENT_1': 52,
 '1-DRH PERIOP_1': 53,
 '2-DMP 7W GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 54,
 '2-DRAH PERIOP_1': 55,
 '1-DUH N9000-S SHORT STAY_1': 56,
 '4-DMP 7E CARDIOTHORACIC SURGERY_2': 57,
 '1-DMP 7E CARDIOTHORACIC SURGERY_1': 58,
 '4-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 59,
 '1-DUH N7300 CARDIOLOGY_1': 60,
 '1-DUH N4100 NEUROSURGERY_1': 61,
 '3-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 62,
 '1-DRH EMERGENCY DEPARTMENT_1': 63,
 '5-DUH N3300 GENERAL/THORACIC/CARDIOTHORACIC SURGERY_1': 64}

name_numb_simplified = {simplify_name(key): value for key, value in name_numb.items()}
test = pd.DataFrame({'From': Source, 'To': Target, 'Values': Value})
cn = list(map(find_unit_color, name_numb.keys()))
cl = list(map(lambda x: x.replace('1)', '0.5)'), cn))
cn = np.array(cn)
cl = np.array(cl)

test['Colors'] = cl[test['From'].values.astype(int)]

data_trace = dict(
    type='sankey',
    arrangement = "freeform",
    domain = dict(
      x =  [0,1],
      y =  [0,1]
    ),
    orientation = "h",
    valueformat = ".0f",
    
    node = dict(
      pad = 5,
      thickness = 5,
      label =  list(name_numb_simplified),
      color =  cn
    ),
    link = dict(
      source = test['From'],
      target = test['To'],
      value = test['Values'],
      color = test['Colors'],
  ))
layout =  dict(
    title = "Adult Decompensation OR To ICU Subcohort Patient Flow",
    titlefont = dict(size = 20),
    font = dict(size = 10)
)

Number of distinct type of transfers threshold: 120

In [7]:
fig = dict(data=[data_trace], layout=layout)
plotly.offline.iplot(fig, validate=False)