--- title: "The Phoenix Sepsis Criteria" output: rmarkdown::html_vignette: toc: true toc_depth: 2 number_sections: false bibliography: references.bib vignette: > %\VignetteIndexEntry{The Phoenix Sepsis Criteria} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r label = "setup", include = FALSE} ################################################################################ ### !!! IMPORTANT NOTE !!! ### ### ### ### THIS FILE WAS GENERATED BY vignette-spinners/phoenix.R ### ### ### ### MAKE EDITS IN THAT FILE ### ################################################################################ library(qwraps2) options(qwraps2_markup = "markdown") knitr::opts_chunk$set(collapse = TRUE) ``` ```{r} library(phoenix) packageVersion("phoenix") ``` # Development and International Consensus Details on the development of the diagnostic Phoenix Sepsis Criteria are in [@sanchezpinto_2024_development](https://doi.org/10.1001/jama.2024.0196). This manuscript covers the data-driven and modified Delphi process leading to an international consensus for the criteria ([@schlapbach_2024_international](https://doi.org/10.1001/jama.2024.0179)). This R package provides utilities to quickly and easily apply the Phoenix Sepsis scoring rubric to your data sets. # Phoenix Sepsis and Septic Shock There are two sets of criteria, the Phoenix and Phoenix 8 criteria. It was determined during the development of the criteria that a score based on four organ systems was sufficient for diagnosis of sepsis and septic shock. An extended score based on eight organ systems was reported as well. ## Sepsis Definition: A pediatric patient is to be diagnosed as having sepsis if: 1. Suspected/Confirmed infection (receipt of systemic antimicrobials and microbiological testing with in first 24 hours of hospital presentation), and 2. A total Phoenix Sepsis Score ≥ 2. The score is the sum of the 1. respiratory, 2. cardiovascular, 3. coagulation, and 4. neurologic scores. ## Septic Shock Definition: A patient is to be diagnosed as having septic shock if the patient meets the criteria for sepsis and has any cardiovascular dysfunction (cardiovascular score ≥ 1). ## Phoenix-8 Score: The research focused metric is the sum of the four organ dysfunction scores for Phoenix and 5. endocrine, 6. immunologic, 7. renal, and 8. hepatic. ## Phoenix Scoring Rubric ```{r echo = FALSE, results = "asis"} tab <- scan(file = system.file("phoenix_rubric.md", package = "phoenix"), what = character(), sep = "\n", quiet = TRUE) cat(tab, sep = "\n") cat("\n") ftnt <- scan(file = system.file("phoenix_rubric_footnotes.md", package = "phoenix"), what = character(), sep = "\n", quiet = TRUE) cat(ftnt, sep = "\n\n") ``` # Development of the Criteria Details on the developed of the criteria are described in @sanchezpinto_2024_development and @schlapbach_2024_international and end users are encouraged to review these papers. A couple quick notes about the data and use in general. Some specific details will be provided in each of the sections for each organ system. **Missing data = 0 points:** During the development of the Phoenix criteria missing data was mapped to zero points. This was done as it was reasonable to assume that for some labs and metrics, missing data could indicate that there was no concern and the testing was not order. Further, the Phoenix criteria was developed to be useful in both high, medium, and low resource settings where some labs and values might be uncommon or impossible to get. As such, we encourage end users of this package to do the same - missing values are missing and should not be imputed. **__The Phoenix Criteria is valid on "known data."__** **Worst in first 24 hours:** The score was developed on the worse measured value during the first 24 hours of an hospital encounter. For example, consider the following patient encounter and organ dysfunction scores: | encounter time | resp | card | coag | neuro | total | | :------------: | :--: | :--: | :--: | :---: | :---: | | 0 | 1 | 0 | 0 | 1 | 2 | | 120 | 0 | 1 | 0 | 1 | 2 | | 720 | 0 | 0 | 0 | 1 | 1 | This patient never had a total score exceeding 2 during the first 720 minutes of the encounter. However, if we took the max score for the four components and summed them for the total then the resulting score would be 3. The development of the Phoenix Criteria was based on the worse total score observed during the first 24 hours of an encounter. The correct signal score for the patient in the above example is 2, not 3. Please keep this in mind when you are preparing your data sets. **Age Restrictions:** Age measurements are not adjusted for prematurity. The Phoenix criteria was developed on non-birth hospitalizations (implication is age > 0), children when gestational age > 37 weeks; and those aged ≤ 216 months (18 years). # Package Use There are functions to apply the Phoenix criteria to a data set for each of the organ systems and wrappers for full Phoenix and Phoenix-8 scores. Inputs for the organ dysfunction scoring functions are expected to be numeric vectors and to have a common length _or_ a length of 1. For example, the respiratory score requires four inputs. The lengths of the inputs could be `r backtick(c(1, 10, 10, 10)) ` indicating that the first input is constant for all other values. An input of `r backtick(c(2, 10, 10, 10)) ` will result in an error. If you wish to 'recycle' an input of length greater than 1 and less than the max length of another, then wrap the shorter inputs in `r backtick(rep()) ` for clarity. In the following subsections example use of the functions require the use of the provided example synthetic dataset `r paste0(backtick(sepsis), ".") ` This data set has `r nrow(sepsis) ` rows and `r ncol(sepsis) ` variables. See `r backtick(?sepsis) ` for more details. ## Respiratory ### Inputs: * `r backtick(pf_ratio) ` is the ratio of PaO2 (partial pressure or oxygen in arterial blood, units of mmHg) to FiO2 (fraction of inspiratory oxygen, values expected to be between 0.21 for room air, to 1.00). Gathering the PaO2 is an invasive procedure. * `r backtick(sf_ratio) ` The SpO2 (pulse oximetry) to FiO2 ratio is a non-invasive surrogate for the PF ratio. Important note: during the development of the Phoenix criteria SF ratios were only valid to consider if the SpO2 is ≤ 97. * `r backtick(imv) ` Invasive mechanical ventilation. This is an integer valued indicator variable: 0 = not intubated; 1 = intubated. * `r backtick(other_respiratory_support) ` Any oxygen support, e.g., high-flow, non-invasive positive pressure, or IMV. ### Scores ```{r echo = FALSE, results = "hide", fig.width = 7, fig.height = 4} resp_data <- expand.grid(pfr = seq(0, 450, by = 10), sfr = seq(0, 450, by = 10), imv = c(0, 1), o2 = c(0, 1)) resp_data$o2 <- pmax(resp_data$imv, resp_data$o2) resp_data <- unique(resp_data) resp_data$phoenix_respiratory_score <- factor(phoenix_respiratory(pfr, sfr, imv, o2, data = resp_data)) resp_data$oxygen_support <- factor( interaction(resp_data$imv, resp_data$o2), levels = c("0.0", "0.1", "1.1"), labels = c("No Oxygen Support", "Non-invasive Oxygen Support", "Invasive Oxygen Support") ) ggplot2::ggplot(data = resp_data) + ggplot2::theme_classic() + ggplot2::aes(x = sfr, y = pfr, fill = phoenix_respiratory_score) + ggplot2::geom_tile() + ggplot2::facet_wrap( ~ oxygen_support, nrow = 1) + ggplot2::scale_fill_brewer(name = "Phoenix Respiratory Score", palette = "Spectral", direction = -1) + ggplot2::ylab(expression(PaO[2] : FiO[2])) + ggplot2::xlab(expression(SpO[2] : FiO[2])) + ggplot2::theme(legend.position = "bottom") ``` ### Example Use The function `r backtick(phoenix_respiratory) ` will return an integer vector. A patient with a nasal cannula at 5 L/min (approximate FiO2 of 0.4) and SpO2 of 87: ```{r} phoenix_respiratory( sf_ratio = 87 / 0.40, other_respiratory_support = 1) ``` Notice that the `r backtick(pf_ratio) ` and `r backtick(imv) ` arguments have been omitted. When an input is missing it is assumed to be `r paste0(backtick(NA), ".") ` When your data is in a `r backtick(data.frame) ` then: ```{r} DF <- read.table(sep = "|", header = TRUE, text = " pfr | sfr | imv | o2 | 438 | | | 175 | | 1 | 175 | | 1 186 | | 1 | 0 300 | 277 | 0 | 1 | | | ") DF$resp_score <- phoenix_respiratory(pfr, sfr, imv, o2, DF) DF ``` Another example using the provided example data set `r paste0(backtick(sepsis), ".") ` Here we have to construct a few variables as we only have information on FiO2, PaO2, SpO2, and invasive mechanical ventilation. ```{r} resp_example <- sepsis[c("pid", "fio2", "pao2", "spo2", "vent")] ``` Implied in this data is other respiratory support when FiO2 is greater than 0.21, the approximate fraction of oxygen in the atmosphere. ```{r results = "hide"} resp_example$score <- phoenix_respiratory( pf_ratio = pao2 / fio2, sf_ratio = ifelse(spo2 <= 97, spo2 / fio2, NA_real_), imv = vent, other_respiratory_support = as.integer(fio2 > 0.21), data = sepsis ) resp_example ``` ```{r echo = FALSE, results = "asis"} knitr::kable(resp_example) ``` ## Cardiovascular ### Inputs * `r backtick(vasoactives) ` is an integer count of the number of systemic vasoactive medications the patient is currently receiving. During development of the Phoenix criteria it was found that just the count of the medications was sufficient to be useful, the dosage was not needed. There were six medications considered, dobutamine, dopamine, epinephrine, milrinone, norepinephrine, and vasopressin. Again, it is systemic use of the medication that is important. For example, an injection of epinephrine to halt an allergic reaction would not count, whereas having an epinephrine drip to treat hypotension or bradycardia would count. * `r backtick(lactate) ` level of lactate in the blood, measured in mmol/L * `r backtick(age) ` in months * `r backtick(map) ` mean arterial pressure (mmHg). During development of the Phoenix criteria, map, and blood pressure values in general, obtained from arterial measures were used preferentially over values obtained from cuffs. Reported values were used preferentially over calculated values. If you need to calculate the map use DBP + (1/3) * (SBP - DBP) where DBP is diastolic blood pressure (mmHg) and SBP is systemic blood pressure (mmHg). ### Scores ```{r echo = FALSE, results = "hide", fig.width = 7, fig.height = 7} DF <- expand.grid(vasoactives = 0:2, lactate = seq(0, 15, by = 1), age = seq(0, 18 * 12, by = 1), MAP = seq(00, 60, by = )) DF$phoenix_cardiovascular_score <- factor( phoenix_cardiovascular(vasoactives, lactate, age, MAP, data = DF) ) DF$lactate_bin <- cut(DF$lactate, breaks = c(0, 5, 11, Inf), right = F) levels(DF$lactate_bin) <- paste("Lactate", levels(DF$lactate_bin)) ggplot2::ggplot(DF) + ggplot2::theme_classic() + ggplot2::aes(x = age, y = MAP, fill = phoenix_cardiovascular_score) + ggplot2::geom_tile() + ggplot2::facet_grid(lactate_bin ~ paste("Vasoactive Medications:", vasoactives)) + ggplot2::scale_fill_brewer(name = "Phoenix Cardiovascular Score", palette = "Spectral", direction = -1) + ggplot2::ylab("Mean Arterial Pressure (mmHg)") + ggplot2::xlab("Age (months)") + ggplot2::guides(fill = ggplot2::guide_legend(nrow = 1)) + ggplot2::theme(legend.position = "bottom") ``` ### Example Use Note: the mean arterial pressure can be approximated by 2/3*DBP + 1/3*SBP. To simplify the work and help reduce potential errors, the function `r backtick(map) ` has been provided to estimate the mean arterial pressure given sbp and dbp. ```{r results = "hide"} card_example <- sepsis[c("pid", "dobutamine", "dopamine", "epinephrine", "milrinone", "norepinephrine", "vasopressin", "lactate", "dbp", "sbp", "age")] card_example$score <- phoenix_cardiovascular( vasoactives = dobutamine + dopamine + epinephrine + milrinone + norepinephrine + vasopressin, lactate = lactate, age = age, map = map(sbp = sbp, dbp = dbp), data = sepsis) card_example ``` ```{r results = "asis", echo = FALSE} knitr::kable(card_example) ``` ## Coagulation ### Inputs * `r backtick(platelets) ` in units of 1,000/μL * `r backtick(inr) ` international normalized ratio; a metric for time require for blood to clot. * `r backtick(d_dimer) ` in units of mg/L FEU * `r backtick(fibrinogen) ` in units of mg/dL ### Scores While there are four components to this score, the maximum number of points assigned is 2. ```{r echo = FALSE, results = "hide", fig.height = 7, fig.width = 7} DF <- expand.grid(platelets = c(10, 150), inr = c(1.0, 1.5), d_dimer = c(1.2, 4.0), fib = c(10, 150)) DF$plt_bin <- factor(DF$platelets, levels = c(150, 10), labels = c("Platelets >= 100", "Platelets < 100")) DF$inr_bin <- factor(DF$inr, levels = c(1.0, 1.5), labels = c("INR <= 1.3", "INR > 1.3")) DF$ddm_bin <- factor(DF$d_dimer, levels = c(1.2, 4.0), labels = c("D-dimer <= 2", "D-dimer > 2")) DF$fib_bin <- factor(DF$fib, levels = c(150, 10), labels = c("Fibrinogen >= 100", "Fibrinogen < 100")) DF$phoenix_coagulation_score <- factor( phoenix_coagulation(platelets, inr, d_dimer, fib, data = DF) ) ggplot2::ggplot(DF) + ggplot2::theme_classic() + ggplot2::aes(x = plt_bin, y = fib_bin, fill = phoenix_coagulation_score) + ggplot2::geom_tile() + ggplot2::facet_grid(inr_bin ~ ddm_bin) + ggplot2::scale_fill_brewer(name = "Phoenix Coagulation Score", palette = "Spectral", direction = -1) + ggplot2::ylab("")+ ggplot2::xlab("")+ ggplot2::theme(legend.position = "bottom") ``` ### Example Use ```{r results = "hide"} coag_example <- sepsis[c("pid", "platelets", "inr", "d_dimer", "fibrinogen")] coag_example$score <- phoenix_coagulation(platelets, inr, d_dimer, fibrinogen, data = sepsis) coag_example ``` ```{r echo = FALSE, results = 'asis'} knitr::kable(coag_example) ``` ## Neurologic ### Inputs * `r backtick(gcs) ` an integer vector for the (total) Glasgow Comma Score. The total score is the sum of the eye, verbal, and motor scores. * `r backtick(fixed_pupils) ` an integer vector of zeros and ones. 1 = bilaterally fixed pupils, 0 otherwise. ### Example Use ```{r results = 'hide'} neuro_example <- sepsis[c("pid", "gcs_total", "pupil")] neuro_example$score <- phoenix_neurologic(gcs = gcs_total, fixed_pupils = as.integer(pupil == "both-fixed"), data = sepsis) neuro_example ``` ```{r echo = FALSE, results = "asis"} knitr::kable(neuro_example) ``` ## Endocrine The endocrine criteria is only applicable to the extended Phoenix-8 scoring. ### Inputs * `r backtick(glucose) ` in mg/dL ### Example Use ```{r results = 'hide'} endo_example <- sepsis[c("pid", "glucose")] endo_example$score <- phoenix_endocrine(glucose, data = sepsis) endo_example ``` ```{r echo = FALSE, results = "asis"} knitr::kable(endo_example) ``` ## Immunologic The immunologic score is only applicable to the extended Phoenix-8 scoring. ### Inputs * `r backtick(anc) ` in units of 1,000 cells per cubic millimeter * `r backtick(alc) ` in units of 1,000 cells per cubic millimeter ### Example Use ```{r results = "hide"} immu_example <- sepsis[c("pid", "anc", "alc")] immu_example$score <- phoenix_immunologic(anc, alc, sepsis) immu_example ``` ```{r echo = FALSE, results = "asis"} knitr::kable(immu_example) ``` ## Renal The renal score is only applicable to the extended Phoenix-8 scoring. ### Inputs * `r backtick(creatinine) ` in units of mg/dL * `r backtick(age) ` in months ### Example Use ```{r results = "hide"} renal_example <- sepsis[c("creatinine", "age")] renal_example$score <- phoenix_renal(creatinine, age, sepsis) renal_example ``` ```{r echo = FALSE, results = "asis"} knitr::kable(renal_example) ``` ## Hepatic The hepatic score is only applicable to the extended Phoenix-8 scoring. ### Inputs `r backtick(bilirubin) ` total bilirubin in units of mg/dL `r backtick(alt) ` in units of IU/L ### Example Use ```{r results = "hide"} hep_example <- sepsis[c("pid", "bilirubin", "alt")] hep_example$score <- phoenix_hepatic(bilirubin, alt, sepsis) hep_example ``` ```{r echo = FALSE, results = "asis"} knitr::kable(hep_example) ``` ## Phoenix The `r backtick(phoenix) ` function is a wrapper around `r paste0(backtick(phoenix_respiratory), ", ", backtick(phoenix_cardiovascular), ", ", backtick(phoenix_coagulation), ", and ", backtick(phoenix_neurologic), ".") ` Where the individual component scoring functions return integer vectors, `r backtick(phoenix) ` returns a `r backtick(data.frame) ` with a column for each of the component organ dysfunction scores, a total score, and two indicator columns, one for sepsis (total score ≥ 2) and another for septic shock (total score ≥ 2 and cardiovascular dysfunction ≥ 1). The range of Phoenix Sepsis scores is from 0 to 13. ### Inputs All of the inputs are the same as for the component organ dysfunction scores. ### Example Use ```{r} phoenix_scores <- phoenix( # respiratory pf_ratio = pao2 / fio2, sf_ratio = ifelse(spo2 <= 97, spo2 / fio2, NA_real_), imv = vent, other_respiratory_support = as.integer(fio2 > 0.21), # cardiovascular vasoactives = dobutamine + dopamine + epinephrine + milrinone + norepinephrine + vasopressin, lactate = lactate, age = age, map = map(sbp, dbp), # coagulation platelets = platelets, inr = inr, d_dimer = d_dimer, fibrinogen = fibrinogen, # neurologic gcs = gcs_total, fixed_pupils = as.integer(pupil == "both-fixed"), data = sepsis ) str(phoenix_scores) ``` The results as a easy to read table: ```{r echo = FALSE, results = "asis"} names(phoenix_scores) <- gsub("_", "\n", names(phoenix_scores)) knitr::kable(phoenix_scores, format = "html", align = "c") ``` ## Phoenix 8 `r backtick(phoenix8) ` returns a `r backtick(data.frame) ` with all the columns that `r backtick(phoenix) ` returns, with the addition of columns for the endocrine, immunologic, renal, hepatic, and Phoenix-8 total score. ### Inputs All the same inputs as the individual organ dysfunction scoring functions. The one minor caveat is that `r backtick(age) ` is used in the cardiovascular and the renal scores and need only be provided once when calling `r paste0(backtick(phoenix8), ".") ` ### Example Use ```{r} phoenix8_scores <- phoenix8( # respiratory pf_ratio = pao2 / fio2, sf_ratio = ifelse(spo2 <= 97, spo2 / fio2, NA_real_), imv = vent, other_respiratory_support = as.integer(fio2 > 0.21), # cardiovascular vasoactives = dobutamine + dopamine + epinephrine + milrinone + norepinephrine + vasopressin, lactate = lactate, age = age, # Also used in the renal assessment. map = map(sbp = sbp, dbp = dbp), # coagulation platelets = platelets, inr = inr, d_dimer = d_dimer, fibrinogen = fibrinogen, # neurologic gcs = gcs_total, fixed_pupils = as.integer(pupil == "both-fixed"), # endocrine glucose = glucose, # immunologic anc = anc, alc = alc, # renal creatinine = creatinine, # no need to specify age again # hepatic bilirubin = bilirubin, alt = alt, data = sepsis ) str(phoenix8_scores) ``` The results as a easy to read table: ```{r echo = FALSE, results = "asis"} names(phoenix8_scores) <- gsub("_", "\n", names(phoenix8_scores)) knitr::kable(phoenix8_scores, format = "html", align = "c") ``` # Clinical Vignettes These are taken from the supplemental material of [@sanchezpinto_2024_development](https://doi.org/10.1001/jama.2024.0196) ## Clinical Vignette 1 A previously healthy 3-year-old girl presents to an emergency department in Lima, Peru, with a temperature of 39°C, tachycardia, and irritability. Blood pressure with an oscillometric device is 67/32 mmHg (mean arterial pressure of 43 mmHg). She is given fluid resuscitation per local best practice guidelines, is started on broad spectrum antibiotics, and blood and urine cultures are sent. After an hour, she becomes hypotensive again and she is started on a norepinephrine drip. A complete blood count reveals leukocytosis, mild anemia, and a platelet count of 95 K/μL. _Phoenix Sepsis Score:_ * 0 respiratory points (no hypoxemia or respiratory support) * 2 cardiovascular points (1 for low mean arterial pressure for age, 1 for use of a vasoactive medication) * 1 coagulation points (for low platelet count) * 0 neurologic points (irritability would result in a Glasgow Coma Scale of approximately 14) * total = 3 points. _Phoenix Sepsis Criteria:_ The patient has suspected infection, ≥ 2 points of the Phoenix Sepsis Score, and ≥1 cardiovascular points, so she meets criteria for septic shock. ```{r} phoenix( vasoactives = 1, # norepinephrine drip map = 32 + (67 - 32) / 3, # 43.667 mmHg platelets = 95, gcs = 14, # irritability age = 3 * 12 # expected input for age is in months ) ``` ## Clinical Vignette 2 A 6-year-old boy with a history of prematurity presents with respiratory distress to his pediatrician’s office in Tucson, Arizona. He is noted to have a temperature of 38.7°C, tachypnea, crackles in the left lower quadrant on chest auscultation, and an oxygen saturation of 89% on room air. He is started on supplemental oxygen and is transported to the local emergency department via ambulance. In the emergency department, a chest X-ray shows a consolidation in the left lower lobe and hazy bilateral lung opacities, so he is started on antibiotics for a suspected bacterial pneumonia. His respiratory status worsens, and he is started on non-invasive positive pressure ventilation. While awaiting to be admitted, his level of consciousness deteriorates rapidly: with nailbed pressure he only opens his eyes briefly, moans in pain, and withdraws his hand (Glasgow Coma Scale: 2 for eye response + 2 for verbal response + 4 for motor response = 8). He is intubated using rapid sequence induction and placed on a conventional ventilator. During this time, his lowest mean arterial pressure using a non-invasive oscillometric device is 52 mmHg and he receives a fluid bolus. He is then transferred to the pediatric intensive care unit where he requires a high positive end expiratory pressure and an FiO2 of 0.45 to achieve an oxygen saturation of 92% (S/F ratio: 204). Complete blood count and lactate level reveal a platelet count of 120 K/μL and a serum lactate of 2.9 mmol/L. Given his platelet count below the normal reference range, a coagulation panel is sent, which reveals an INR of 1.7, a D-Dimer of 4.4 mg/L, and a fibrinogen of 120 mg/dL. _Phoenix Sepsis Score:_ * 2 respiratory points (for an S/F ratio <292 on invasive mechanical ventilator) * 0 cardiovascular points (mean arterial pressure >48 mmHg and Lactate level <5 mmol/L) * 2 coagulation points (for high INR and D-Dimer) * 1 neurologic point (Glasgow Coma Scale ≤10) * total = 5 points. _Phoenix Sepsis Criteria:_ The patient has a suspected infection, ≥2 points of the Phoenix Sepsis Score, and 0 cardiovascular points, so he meets criteria for sepsis. ```{r} phoenix( gcs = 2 + 2 + 4, # eye + verbal + motor map = 52, imv = 1, sf_ratio = 92 / 0.45, platelets = 120, lactate = 2.9, inr = 1.7, d_dimer = 4.4, fibrinogen = 120) ``` # References