Shortages

Row

Active shortages

1814

New shortages expected this week

16

New discontinuations this week

0

Shortages of Essential Medicines this year

272

Row

Drugs in shortage

Causes of drug shortages this year

Row

New shortages per week since 2018

Shortage durations

COVID-19

Column

COVID-19 Indicator Medicines

We are tracking the following list of medications (and groups of medications) to assess for COVID-related shortages:

ATC Description
C01CA Adrenergics
H01BA Vasopressin and analogues
M03AB01 Succinylcholine (suxamethonium)
M03AC Muscle relaxants (e.g. paralytics)
N01A Anesthetics (includes opioid, ketamines)
N05CD Benzodiazepines
N05CM18 Dexmedetomidine
C01BD01 Amiodarone
C03CA01 Furosemide
J05AR10 Kaletra (anti-viral)
N02BE01 Acetaminophen (Paracetamol)
P01BA02 Hydroxychloroquine
R01AA04 Phenylephrine (nasal)
R03AC Respiratory Beta Agonists

The following charts shows the availability of COVID-19 indicator medications since November 2019. Only medications (by ATC code) that have had a shortages will be shown.

Column

Anesthetic Use

Other Use

About

Column

About this website

Hello. I am Jon Pipitone, resident in Psychiatry at Queen’s University, Kingston, Ontario, Canada. This dashboard is my attempt to show interesting trends and summaries of Canadian drug shortage reports. Code for this website can be found on github. This work is licensed under a Creative Commons Attribution 4.0 International License.

Much of this work has been done in collaboration with Dr. Jacalyn Duffin, who has a her own site dedicated to analysis of the canadian drug shortages: https://canadadrugshortage.com.

We published a peer-reviwed paper in 2018 on this issue:

  • Donelle, Jessy, Jacalyn Duffin, Jonathan Pipitone, and Brian White-Guay. “Assessing Canada’s Drug Shortage Problem.” CD Howe Institute Commentary 515, 2018. https://doi.org/10.2139/ssrn.3192558

I also keep a blog and I sometimes discuss drug shortages.

Questions, comments, requests? Please send email to .

FAQ

  1. Why are there different totals listed under “active shortages” and on the “drugs in shortage” graph?

    In short, it’s because there is missing or incorrect data in some of the shortage reports that means they get excluded from historical counts. I’ve written a blog post about it.

  2. How often is the dashboard updated?

    Daily. It was last updated 2024-05-08 00:03:01.

Column

Data sources

drugshortagescanada.ca

Database of shortage and discontinuation reports. Active from March 2017 to present. Manufacturers are required to report.

Last updated: 2024-05-08 00:03:01

Drug Product Database

Database of historical records of drug products in Canada. Maintained by Health Canada.

Last updated: 2024-05-08 00:03:01

---
title: "Assessing Canada’s Drug Shortage Problem"
output: 
  flexdashboard::flex_dashboard:
    orientation: rows
    source_code: embed
    self_contained: false
---

```{r setup, include=FALSE}
options(tidyverse.quiet=T)
library(flexdashboard)
library(tidyverse)
library(lubridate, warn.conflicts=F)
library(devtools, quietly=T)
library(rdrugshortages)     # devtools::install_github('pipitone/rdrugshortages')
library(kableExtra, warn.conflicts=F)
library(plotly, warn.conflicts=F)

# settings
DATA_DIR = 'data/'  # Load the Health Canada Drug Product Database
DPD_DOWNLOAD = getOption('dpd_download', default=F) # download the latest drug product database dataset
DSC_DOWNLOAD = getOption('dsc_download', default=F) # download latest DSC database
DSC_PATH     = 'data/dsc.csv'
DSC_START    = ymd("2018-01-01", tz="")
FAR_FUTURE   = ymd("2100-12-31", tz="")
FAR_PAST     = origin

theme_set(theme_minimal())
caption = list(labs(caption =  paste(
  paste(
    "source: drugshortagescanada.ca, last updated:", as_date(file.info(DSC_PATH)$ctime)), 
    "source: Health Canada Drug Product Database",
    "jon.pipitone.ca/research/drug-shortages", 
    sep="\n")))

# load data
if (DSC_DOWNLOAD) { 
  dsc = dsc_search()  %>%
    select_if(~ !is.list(.x)) %>%
    write_csv(DSC_PATH)
}
dsc.orig = read_csv(DSC_PATH, trim_ws = T, col_types = cols(
  status = col_factor(),
  drug.company.post_office_box = col_character(),
  hc_en_comments = col_character(), 
  hc_fr_comments = col_character(),
  shortage_reason.en_reason = col_factor()
  )) %>%
  select(-matches("^fr_|\\.fr_|_fr")) %>%
  mutate(
    reason = fct_recode(shortage_reason.en_reason,  
      "Demand increase" = "Demand increase for the drug.", 
      "Manufacturing distruption" = "Disruption of the manufacture of the drug.",
      "Ingredient shortage" = "Shortage of an active ingredient.", 
      "Ingredient shortage" = "Shortage of an inactive ingredient or component.",
      "Manufacturing practices" = "Requirements related to complying with good manufacturing practices.",
      "Other" = "Other (Please describe in comments)",
      "Shipping delay" = "Delay in shipping of the drug." 
    ) 
  ) %>%
  mutate(
    end_date_no_na = coalesce(actual_end_date, FAR_FUTURE))

## Parsual EML
# https://dx.doi.org/10.9778%2Fcmajo.20160122
eml = read_csv('data/cleanmeds.csv', trim_ws=T, col_types = cols()) %>% 
  select(category, medication, ATC)

## DPD
dpd = dpd_load(DATA_DIR, download=DPD_DOWNLOAD)

## Add the DPD ATC number to the DSC data
dsc = inner_join(dpd$drug, dpd$ther, by="DRUG_CODE") %>% 
  select(din = DRUG_IDENTIFICATION_NUMBER, dpd_atc_number = TC_ATC_NUMBER, TC_ATC) %>% 
  distinct(din, .keep_all = TRUE) %>%
  right_join(dsc.orig, by=c("din"))
dsc.shortages = dsc %>% filter(type.label == "shortage")
dsc.discontinuations = dsc %>% filter(type.label == "discontinuance")

this_year = interval(floor_date(now(), "year"), ceiling_date(now(), "year"))
week_start = floor_date(now(), "week")
this_week = interval(week_start, week_start + dweeks(1))
next_week = int_shift(this_week, by = dweeks(1))
```

Shortages
==========

Row {data-height=125}
--------------------------------

### Active shortages
```{r}
valueBox(dsc %>%  filter(status == "active_confirmed") %>% count() %>% pull(), 
         icon="fa-capsules", 
         color = "warning")
```

### New shortages expected this week
```{r}
.shortages_this_week =  dsc.shortages %>% 
  filter(
    status %in% c("active_confirmed", "anticipated_shortage"), 
    coalesce(actual_start_date, anticipated_start_date) %within% this_week)
valueBox(nrow(.shortages_this_week),icon="fa-needle")
```

### New discontinuations this week
```{r}
.active_disconts = dsc.discontinuations  %>% 
  filter(
    status == "discontinued", 
    discontinuation_date %within% this_week)
valueBox(nrow(.active_disconts), icon="fa-ban")
```

### Shortages of Essential Medicines this year
```{r}
.emls_with_shortages = dsc.shortages %>%
  filter(actual_start_date %within% this_year) %>%
  right_join(eml, by=c("dpd_atc_number" = "ATC"))  %>%
  filter(!is.na(din))  %>%
  group_by(category, medication, dpd_atc_number)
valueBox(nrow(.emls_with_shortages), icon="fa-ambulance")
```

Row  
----------------------------------
### Drugs in shortage
```{r}
weeks = tibble(
  week.start = seq(as_date(floor_date(DSC_START, "week")), 
                   as_date(floor_date(now(), "week")), by = "1 week"), 
  i = 1
)
p = dsc.shortages %>%
  mutate(i = 1) %>%
  full_join(weeks, by = "i") %>%
  filter(week.start %within% interval(actual_start_date, end_date_no_na))%>%
  group_by(week.start) %>%
  summarize(n = n()) %>%
  mutate(week = week.start) %>%
  ggplot( aes(x = week, y = n)) + 
  geom_line() + 
  scale_x_date(date_minor_breaks = "1 month", 
               date_labels="%b %Y") + 
  labs(x = NULL, y = NULL)
ggplotly(p)
```


### Causes of drug shortages this year {.no-padding}
```{r, fig.height=4, asp=0.6}
p = dsc.shortages %>%
  transmute(started = as_date(floor_date(actual_start_date, "month")), 
            reason = reason) %>%  
  filter(started >= ymd('2019-10-01'), started < now()) %>%
  count(started, reason) %>%
  group_by(started) %>%
  mutate(percent = round(n / sum(n) * 100, 1)) %>%
  transmute(month = started, percent, reason) %>%
  ggplot(aes(x = month, y = percent, fill=reason)) + 
  geom_bar(stat='identity') + 
  scale_x_date(date_breaks="1 month", 
               date_labels="%b %Y") +
  labs(x=NULL, y="% of total") + 
  theme(legend.position="bottom")
ggplotly(p) %>% layout(legend = list(orientation = 'h', x=0, y=-.1))
```


Row
-----------------------------------------------------------------------

### New shortages per week since 2018
```{r}
p = dsc.shortages %>%
  mutate(week = as_date(floor_date(actual_start_date, "week"))) %>%
  filter(week >= ymd('2018-01-01'), week < now()) %>%
  count(week) %>%
  ggplot(aes(x=week, y=n)) + 
  geom_bar(stat='identity') + 
  scale_x_date(date_minor_breaks = "1 month", 
               date_labels = "%b %Y") +  
  labs(x=NULL, y=NULL)
 ggplotly(p) 
```

### Shortage durations
```{r}
p = dsc.shortages %>% 
  filter(actual_start_date >= ymd("2018-01-01")) %>%
  mutate(duration = (actual_end_date - coalesce(actual_start_date, estimated_end_date))/dweeks(1), 
         month_str=floor_date(actual_start_date, unit="year")) %>%
  filter(!is.na(duration), duration > 0) %>%
  ggplot(aes(x=duration)) +
  geom_histogram(binwidth=4) +
  facet_wrap(~factor(month_str), nrow=1, labeller = as_labeller(function(x) format.Date(x, "%Y"))) + 
  labs(x="Duration (weeks)", y="# of shortages")
ggplotly(p)
```

COVID-19 {data-orientation=columns}
=======================================

```{r}
.dpd_status_ordered = dpd$status %>%
  group_by(DRUG_CODE) %>%
  arrange(HISTORY_DATE, .by_group = T) %>%
  mutate(.curr = 1:n(), .prev = .curr-1, .last = n()) %>%
  ungroup()

# complex join so that LHS has FROM status and RHS has TO status
# Also includes pair when LHS is the most recent status and then RHS is ignored
dpd_status_intervals = inner_join(.dpd_status_ordered, .dpd_status_ordered, by="DRUG_CODE") %>%
  select(-starts_with("CURRENT"), -starts_with("LOT"), -starts_with("EXP")) %>%
  filter(.prev.y == .curr.x | (.curr.x == .last.x & .curr.x == .curr.y)) %>%
  transmute(
    DRUG_CODE = DRUG_CODE, 
    start_date = HISTORY_DATE.x,
    end_date = if_else(.curr.x == .last.x, as_date(FAR_FUTURE), HISTORY_DATE.y), 
    status = STATUS.x)

# Map ATC code to NAME
atc_codes = dpd$ther %>% transmute(dpd_atc_number = TC_ATC_NUMBER, atc_name = TC_ATC) %>% distinct()

# date range to show 
FROM = floor_date(ymd("2019-11-01"), "week")
TO = as_date(now())
BY = "1 week"  # warning: if the FROM-TO range is large (e.g. > 20 years), change BY to something larger, e.g. 3 months

# now, for each month, let's compute the total number of drugs marketed, for each ATC code
dpd.months = tibble(month.date = seq(FROM, TO, by=BY),  n = 1)

dpd_num_marketed_by_atc = dpd_status_intervals %>% mutate(n=1) %>%
  inner_join(dpd.months, by="n") %>%
  filter(month.date %within% interval(start_date, end_date)) %>%
  select(DRUG_CODE, status, month.date) %>%
  inner_join(select(dpd$ther, DRUG_CODE, TC_ATC_NUMBER), by = "DRUG_CODE") %>%
  group_by(month.date, TC_ATC_NUMBER) %>%
  summarize(marketed = sum(status == "MARKETED"))

dsc_num_shortages_by_atc = dsc.shortages %>%
  mutate(n=1) %>%
  inner_join(dpd.months, by="n") %>%
  filter(month.date %within% interval(actual_start_date, end_date_no_na)) %>%
  distinct(month.date, dpd_atc_number, din) %>%
  group_by(month.date, dpd_atc_number) %>%
  summarize(shortages = n())


atc_plot = function(atc) {
  shortages = dsc_num_shortages_by_atc %>%
    filter(str_starts(dpd_atc_number, atc))
  
  marketed = dpd_num_marketed_by_atc  %>%
    filter(str_starts(TC_ATC_NUMBER, atc))
  
  right_join(shortages, marketed, 
             by = c("month.date" = "month.date", "dpd_atc_number" = "TC_ATC_NUMBER")) %>%
    ungroup() %>%
    mutate(shortages = replace_na(shortages, 0)) %>%
    mutate(available = marketed - shortages) %>%
    group_by(month.date) %>%
    summarize(Available = sum(available), Marketed = sum(marketed)) %>%
    pivot_longer(cols = Available:Marketed, names_to = "status", values_to = "count") %>%
    ggplot(aes(x = week, y = count, linetype = fct_rev(status))) +
    geom_line()  + 
    labs(x=NULL, y = "# of DINs", 
         linetype = NULL) + 
    expand_limits(y=0)  + 
    theme(legend.position="bottom")
}

integer_breaks <- function(n = 5, ...) { 
  function(x) { 
    breaks = floor(pretty(x, n, ...))
    names(breaks) = attr(breaks, "labels")
    breaks
  }
}


atc_group_plot = function(atcs) {
  atc = paste(atcs, collapse="|")
  shortages = dsc_num_shortages_by_atc %>%
    filter(str_starts(dpd_atc_number, atc))
  
  marketed = dpd_num_marketed_by_atc  %>%
    filter(str_starts(TC_ATC_NUMBER, atc))
  
  right_join(shortages, marketed, 
             by = c("month.date" = "month.date", "dpd_atc_number" = "TC_ATC_NUMBER")) %>%
    ungroup() %>%
    mutate(shortages = replace_na(shortages, 0)) %>%
    mutate(available = marketed - shortages) %>%
    group_by(month.date, dpd_atc_number) %>%
    summarize(Available = sum(available), Marketed = sum(marketed)) %>%
    ungroup() %>%
    group_by(dpd_atc_number) %>%
    filter(sum(Marketed-Available) > 0, Available > 0) %>%
    mutate(percent_available = Available/max(Available)*100) %>%
    mutate(week = month.date) %>%
    left_join(atc_codes, by="dpd_atc_number") %>%
    mutate(name_and_atc = paste0(atc_name, ' [', dpd_atc_number, ']')) %>%
    ggplot(aes(x = week, y = Available, fill=name_and_atc)) +
    geom_area()  + 
    scale_x_date(date_breaks="2 month", 
                 date_labels="%b %Y", 
                 minor_breaks=NULL) + 
    scale_y_continuous(breaks=integer_breaks(3)) + 
    facet_wrap(~name_and_atc, ncol=2, strip.position = "right", scales="free_y") + 
    theme(legend.position="none", 
          strip.text.y = element_text(angle=0)
          )
}
```


Column  {data-width=150}
------------------------------------- 

### COVID-19 Indicator Medicines
We are tracking the following list of medications (and groups of medications) to
assess for COVID-related shortages:

```{r}
# To add: propofol, rocuronium, cisatracurium, ketamine, midazolam, norepinephrine, phenylephrine, fentanyl
covid_meds = tribble(
  ~ATC,      ~Type,             ~Description,
  "N01A",    "Anesthetic use",  "Anesthetics (includes opioid, ketamines)",  
  "M03AC",   "Anesthetic use",  "Muscle relaxants (e.g. paralytics)", 
  "M03AB01", "Anesthetic use",  "Succinylcholine (suxamethonium)",
  "N05CD",   "Anesthetic use",  "Benzodiazepines", 
  "C01CA",   "Anesthetic use",  "Adrenergics",  
  "N05CM18", "Anesthetic use",  "Dexmedetomidine", 
  "H01BA",   "Anesthetic use",  "Vasopressin and analogues",
  "R03AC",   "Other",           "Respiratory Beta Agonists",  
  "R01AA04", "Other",           "Phenylephrine (nasal)",
  "C03CA01", "Other",           "Furosemide",
  "C01BD01", "Other",	          "Amiodarone",
  "J05AR10", "Other",           "Kaletra (anti-viral)", 
  "P01BA02", "Other",           "Hydroxychloroquine", 
  "N02BE01", "Other",           "Acetaminophen (Paracetamol)",  
)
   
kable(covid_meds %>% arrange(Type, ATC) %>% select(-Type)) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"), 
    full_width=T) 
```

The following charts shows the availability of COVID-19 indicator medications since November 2019. 
Only medications (by ATC code) that have had a shortages will be shown. 

Column {.tabset}
-----------------------------------------------------

### Anesthetic Use {data-padding=30}
```{r}
p = atc_group_plot(filter(covid_meds, Type == "Anesthetic use")$ATC)
p1 = p + 
    geom_vline(xintercept=as.numeric(ymd("2020-01-15")), linetype=1) + 
    geom_vline(xintercept=as.numeric(ymd("2020-03-09")), linetype=1, colour="red") + 
    labs(x=NULL, y = "# DIN available") 
p1_ly = ggplotly(p1) %>% 
  layout(annotations = list(x = 1, y = -0.06, xref="paper", yref="paper", 
                            xanchor="right", yanchor="bottom", xshift=0, yshift=0, 
                            font=list(size=10), showarrow=F,
                            text="Black line indicates first COVID-19 case in canada, red line indicates first death"))  
p1_ly
```

### Other Use {data-padding=30}
```{r}
p = atc_group_plot(filter(covid_meds, Type == "Other")$ATC)
p1 = p + 
    geom_vline(xintercept=as.numeric(ymd("2020-01-15")), linetype=1) + 
    geom_vline(xintercept=as.numeric(ymd("2020-03-09")), linetype=1, colour="red") + 
    labs(x=NULL, y = "# DIN available") 
p1_ly = ggplotly(p1) %>% 
  layout(annotations = list(x = 1, y = -0.06, xref="paper", yref="paper", 
                            xanchor="right", yanchor="bottom", xshift=0, yshift=0, 
                            font=list(size=10), showarrow=F,
                            text="Black line indicates first COVID-19 case in canada, red line indicates first death"))  
p1_ly
```


Search
======

Row 
------------------------------------- 
### Searchable list of shortage reports since January, 2023
```{r}
dsc.shortages %>%
  mutate(start_date = date(coalesce(actual_start_date, anticipated_start_date)),) %>%
  filter(start_date %within% interval(ymd("2023-01-01"), now()+dweeks(4))) %>% 
  arrange(start_date) %>%
  mutate(drug_name = if_else(str_length(en_drug_brand_name) > 30, paste0(substring(en_drug_brand_name, 0, 30), '...'), en_drug_brand_name), 
         company_name = str_to_title(company_name), 
         duration = round((start_date %--% estimated_end_date)/days(1)),
         status = fct_recode(status, 
                             "Active" = "active_confirmed", 
                             "Anticipated" = "anticipated_shortage", 
                             "Avoided" = "avoided_shortage", 
                             "Resolved" = "resolved"),
         link = text_spec(id, link=paste0("https://www.drugshortagescanada.ca/shortage/", id))) %>%
  select(dpd_atc_number, drug_name, drug_strength, company_name, status, start_date, duration, reason, link) %>%
  DT::datatable(escape=F, 
                filter="top",
                rownames=T,
                options = list(bPaginate = FALSE, 
                               autoWidth = T,
                               rowid = F, 
                               search = list(regex = T), 
                               order=list(list(6, "desc"), list(5,"asc"))), 
                colnames = c("ATC", "Drug Name", "Dose", "Company", "Status", "Start", "Duration (days)", "Reason", "View report")) %>%
  DT::formatStyle("start_date", "white-space"="nowrap") %>%
  DT::formatStyle("drug_name", "white-space"="nowrap") %>%
  DT::formatStyle("reason", "white-space"="nowrap") %>%
  DT::formatStyle('status', color = DT::styleEqual(c('Anticipated', 'Active'), c('blue', 'green')))
```

About
=====

Column
------

### About this website

Hello. I am Jon Pipitone, resident in Psychiatry at Queen's University, Kingston,
Ontario, Canada. This dashboard is my attempt to show interesting trends and
summaries of Canadian drug shortage reports. Code for this website can be found
on [github](https://github.com/pipitone/drug-shortages-dashboard). This work is
licensed under a <a rel="license"
href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution
4.0 International License</a>.

Much of this work has been done in collaboration with [Dr. Jacalyn
Duffin](https://jacalynduffin.ca/), who has a her own site dedicated to
analysis of the canadian drug shortages: https://canadadrugshortage.com. 

We published a peer-reviwed paper in 2018 on this issue:

* Donelle, Jessy, Jacalyn Duffin, Jonathan Pipitone, and Brian White-Guay.
“Assessing Canada’s Drug Shortage Problem.” CD Howe Institute Commentary 515, 2018. https://doi.org/10.2139/ssrn.3192558

I also keep [a blog](https://jon.pipitone.ca/blog) and I sometimes discuss drug
shortages.

Questions, comments, requests? Please send email to drugshortages@pipitone.ca.

### FAQ 

1. Why are there different totals listed under "active shortages" and on the "drugs in shortage" graph?

    In short, it's because there is missing or incorrect data in some of the
shortage reports that means they get excluded from historical counts. I've
written [a blog post about it](https://jon.pipitone.ca/blog/2020-04-18-how-many-drug-shortages-are-there/).

1. How often is the dashboard updated? 

    Daily. It was last updated `r now()`. 

Column  {data-width=100}
----------------------------

### Data sources

##### [drugshortagescanada.ca](https://www.drugshortagescanada.ca)
Database of shortage and discontinuation reports.  Active from March 2017 to
present. Manufacturers are required to report.

Last updated: `r now()`

##### [Drug Product Database](https://www.canada.ca/en/health-canada/services/drugs-health-products/drug-products/drug-product-database.html)
Database of historical records of drug products in Canada. Maintained by Health Canada.

Last updated: `r now()`

##### [CLEANMeds Essential Medicines List](https://cleanmeds.ca)