Too few medicines to treat neuropathic pain on national essential medicines lists
Peter Kamerman
11 February 2017Map: Inclusion of medicines to treat neuropathic pain on national essential medicines lists
Interactive plot: Choose the drug class or IMF income category using the radio buttons in the top-right corner, and click on the country of interest to get more information.
############################################################
# #
# Load raw data #
# #
############################################################
<- read_csv('./_data/2017-02-10-eml-drug-map/2015-neml-data.csv')
access_data
############################################################
# #
# Prepare data #
# #
############################################################
# Create filtering objects
<- c('Anticonvulsant', 'SNRI', 'TCA', 'Opioid', 'Topical')
drug_class <- c('Gabapentin', 'Pregabalin', 'Duloxetine', 'Venlafaxine',
drug_name 'Amitriptyline', 'Clomipramine', 'Desipramine', 'Imipramine',
'Nortriptyline', 'Morphine', 'Oxycodone', 'Tramadol',
'Capsaicin', 'Lidocaine')
<- c('Gabapentin', 'Pregabalin', 'Duloxetine', 'Venlafaxine',
first_line 'Amitriptyline', 'Clomipramine', 'Desipramine', 'Imipramine',
'Nortriptyline')
<- 'Amitriptyline|Clomipramine|Desipramine|Imipramine|Nortriptyline'
simplify_tca <- c('Tramadol', 'Capsaicin', 'Lidocaine')
second_line <- c('Morphine', 'Oxycodone')
third_line
# Remove extraneous drugs/countries
<- access_data %>%
access_data # Select required columns
select(Country, Class, Drug, Listed) %>%
# Get rid of Sweden (OECD High Income)
filter(Country != 'Sweden') %>%
# Make lower case
rename(country = Country,
class = Class,
drug = Drug,
listed = Listed) %>%
## filter by drug name
filter(drug %in% drug_name)
# Get country names from access_data df for expanding first_line df (see below)
<- access_data$country
countries_access
#-- Extract first-line drugs per country -------------------------------------#
<- access_data %>%
first_line filter(drug %in% first_line) %>%
mutate(grade = rep('first_line', nrow(.))) %>%
filter(listed == 'Yes') %>%
# Simplify TCAs
mutate(drug = str_replace(drug,
pattern = simplify_tca,
replacement = 'Tricyclic')) %>%
select(country, grade, class, drug) %>%
group_by(country, grade, class, drug) %>%
summarise(n = n()) %>%
# Flesh-out countries so that they all have all second-line drugs
complete(country = unique(countries_access), drug) %>%
unique(.) %>%
# Convert NA to 0
mutate(n = str_replace_na(n)) %>%
mutate(n = as.numeric(str_replace(n,
pattern = 'NA',
replacement = '0'))) %>%
# Recode all values > 0 as 1 (i.e., yes or no)
mutate(n = ifelse(test = n > 0, yes = 1, no = 0)) %>%
# Spread and flatten across drug classes
spread(drug, n) %>%
ungroup() %>%
select(-class) %>%
mutate(Tricyclic = str_replace_na(Tricyclic),
Gabapentin = str_replace_na(Gabapentin),
Pregabalin = str_replace_na(Pregabalin),
Duloxetine = str_replace_na(Duloxetine),
Venlafaxine = str_replace_na(Venlafaxine)) %>%
mutate(Tricyclic = as.numeric(str_replace(Tricyclic,
pattern = 'NA',
replacement = '0')),
Gabapentin = as.numeric(str_replace(Gabapentin,
pattern = 'NA',
replacement = '0')),
Pregabalin = as.numeric(str_replace(Pregabalin,
pattern = 'NA',
replacement = '0')),
Duloxetine = as.numeric(str_replace(Duloxetine,
pattern = 'NA',
replacement = '0')),
Venlafaxine = as.numeric(str_replace(Venlafaxine,
pattern = 'NA',
replacement = '0'))) %>%
# Calculate totals per drug per country
# (used to reduce rows from 345 to 115 after expansion of '0' rows
# caused by previous steps)
group_by(grade, country) %>%
summarise(tricyclic = sum(Tricyclic),
gabapentin = sum(Gabapentin),
pregabalin = sum(Pregabalin),
duloxetine = sum(Duloxetine),
venlafaxine = sum(Venlafaxine)) %>%
# Calculate class totals
mutate(class_tca = ifelse(tricyclic > 0,
yes = 1, no = 0),
class_alpha2delta = ifelse(gabapentin + pregabalin > 0,
yes = 1, no = 0),
class_snri = ifelse(duloxetine + venlafaxine > 0,
yes = 1, no = 0)) %>%
# Calculate classes per country
mutate(classes_first = class_tca + class_alpha2delta + class_snri) %>%
# Convert 0 and 1 to 'No' and 'Yes' for the drug columns (for popup)
mutate(tricyclic = ifelse(tricyclic == 1,
yes = 'yes', no = 'no'),
gabapentin = ifelse(gabapentin == 1,
yes = 'yes', no = 'no'),
pregabalin = ifelse(pregabalin == 1,
yes = 'yes', no = 'no'),
duloxetine = ifelse(duloxetine == 1,
yes = 'yes', no = 'no'),
venlafaxine = ifelse(venlafaxine == 1,
yes = 'yes', no = 'no')) %>%
# Rename for merging to SPDF
rename(name = country) %>%
# Reorder columns
select(name, grade, tricyclic, gabapentin, pregabalin, duloxetine,
venlafaxine, class_tca, class_alpha2delta,%>%
class_snri, classes_first) # Get rid of strange attr that have cropped-up
as.data.frame()
#-- Extract second-line drugs per country ------------------------------------#
<- access_data %>%
second_line filter(drug %in% second_line) %>%
mutate(grade = rep('second_line', nrow(.))) %>%
filter(listed == 'Yes') %>%
select(country, grade, class, drug) %>%
group_by(country, grade, class, drug) %>%
summarise(n = n()) %>%
# Flesh-out countries so that they all have all second-line drugs
complete(country = countries_access, drug) %>%
unique(.) %>%
# Convert NA to 0
mutate(n = str_replace_na(n)) %>%
mutate(n = as.numeric(str_replace(n,
pattern = 'NA',
replacement = '0'))) %>%
# Recode all values > 0 as 1 (i.e., yes or no)
mutate(n = ifelse(test = n > 0, yes = 1, no = 0)) %>%
# Spread and flatten across drug classes
spread(drug, n) %>%
ungroup() %>%
select(-class) %>%
mutate(Lidocaine = str_replace_na(Lidocaine),
Tramadol = str_replace_na(Tramadol)) %>%
mutate(Lidocaine = as.numeric(str_replace(Lidocaine,
pattern = 'NA',
replacement = '0')),
Tramadol = as.numeric(str_replace(Tramadol,
pattern = 'NA',
replacement = '0'))) %>%
# Calculate totals per drug per country
group_by(grade, country) %>%
summarise(lidocaine = sum(Lidocaine),
tramadol = sum(Tramadol)) %>%
# Calculate class totals
mutate(class_opioid2 = ifelse(tramadol > 0,
yes = 1, no = 0),
class_topical = ifelse(lidocaine > 0,
yes = 1, no = 0)) %>%
# Calculate classes per country
mutate(classes_second = class_opioid2 + class_topical) %>%
# Convert 0 and 1 to 'No' and 'Yes' for the drug columns (for popup)
mutate(tramadol = ifelse(tramadol == 1,
yes = 'yes', no = 'no'),
lidocaine = ifelse(lidocaine == 1,
yes = 'yes', no = 'no')) %>%
# Make sure the is a zero for the classes_total (for plotting)
complete(classes_second = c(0, 1, 2)) %>%
# Rename for merging to SPDF
rename(name = country) %>%
# Reorder columns
select(name, grade, tramadol, lidocaine, class_opioid2,
%>%
class_topical, classes_second) # Get rid of strange attr that have cropped-up
as.data.frame()
#-- Extract third-line drugs per country -------------------------------------#
<- access_data %>%
third_line filter(drug %in% third_line) %>%
mutate(grade = rep('third_line', nrow(.))) %>%
filter(listed == 'Yes') %>%
select(country, grade, class, drug) %>%
group_by(country, grade, class, drug) %>%
summarise(n = n()) %>%
# Flesh-out countries so that they all have all second-line drugs
complete(country = countries_access, drug) %>%
unique(.) %>%
# Convert NA to 0
mutate(n = str_replace_na(n)) %>%
mutate(n = as.numeric(str_replace(n,
pattern = 'NA',
replacement = '0'))) %>%
# Recode all values > 0 as 1 (i.e., yes or no)
mutate(n = ifelse(test = n > 0, yes = 1, no = 0)) %>%
# Spread and flatten across drug classes
spread(drug, n) %>%
ungroup() %>%
select(-class) %>%
mutate(Morphine = str_replace_na(Morphine), # None had Botox
Oxycodone = str_replace_na(Oxycodone)) %>%
mutate(Morphine = as.numeric(str_replace(Morphine,
pattern = 'NA',
replacement = '0')),
Oxycodone = as.numeric(str_replace(Oxycodone,
pattern = 'NA',
replacement = '0'))) %>%
# Calculate totals per drug per country
group_by(grade, country) %>%
summarise(morphine = sum(Morphine),
oxycodone = sum(Oxycodone)) %>%
# Calculate class totals
mutate(classes_third = ifelse(morphine > 0 | oxycodone > 0,
yes = 1, no = 0)) %>%
# Convert 0 and 1 to 'No' and 'Yes' for the drug columns (for popup)
mutate(morphine = ifelse(morphine == 1,
yes = 'yes', no = 'no'),
oxycodone = ifelse(oxycodone == 1,
yes = 'yes', no = 'no')) %>%
# Make sure the is a zero for the classes_total (for plotting)
complete(classes_third = c(0, 1, 2)) %>%
# Get rid of strange NA row that gets added when using 'complete'
filter(!is.na(country)) %>%
# Rename for merging to SPDF
rename(name = country) %>%
# Reorder columns
select(name, grade, morphine, oxycodone, classes_third) %>%
# Get rid of strange attr that have cropped-up
as.data.frame()
############################################################
# #
# Make geospacial object #
# #
############################################################
# Load geojson
<- geojson_read(
geo_data './_data/2017-02-10-eml-drug-map/admin0_countries_50m.geojson',
what = 'sp')
# Other countries
## Filter out countries with data
<- geo_data
geo_other <- geo_other[!(geo_data$name %in% countries_access), 'name']
geo_other
# Simplify SPDF to avoid NAs
## Filter geo_data by countries in first_line df
<- geo_data[geo_data$name %in% countries_access,
geo_data c('name', 'income_grp', 'gdp_md_est')]
## Filter *_line dfs by countries in geo_data
<- geo_data$name
countries_geo <- first_line[first_line$name %in% countries_geo, ]
first_line <- second_line[second_line$name %in% countries_geo, ]
second_line <- third_line[third_line$name %in% countries_geo, ]
third_line
## Factor 'classes_*'
$factor_first <- factor(first_line$classes_first,
first_linelevels = c('0', '1', '2', '3'),
ordered = TRUE)
$factor_second <- factor(second_line$classes_second,
second_linelevels = c('0', '1', '2'),
ordered = TRUE)
$factor_third <- factor(third_line$classes_third,
third_linelevels = c('0', '1', '2'),
ordered = TRUE)
## Clean-up 'income grp'
<- data.frame(name = geo_data$name, imf = geo_data$income_grp) %>%
imf mutate(imf = str_replace(imf,
pattern = '1. High income: OECD',
replacement = 'High income')) %>%
mutate(imf = str_replace(imf,
pattern = '2. High income: nonOECD',
replacement = 'High income')) %>%
mutate(imf = str_replace(imf,
pattern = '3. Upper middle income',
replacement = 'Upper-middle income')) %>%
mutate(imf = str_replace(imf,
pattern = '4. Lower middle income',
replacement = 'Lower-middle income')) %>%
mutate(imf = str_replace(imf,
pattern = '5. Low income',
replacement = 'Low income')) %>%
mutate(imf = factor(imf,
levels = c('Low income', 'Lower-middle income',
'Upper-middle income', 'High income'),
ordered = TRUE))
# Merge SPDF with *_line dfs
<- merge(x = geo_data, y = first_line,
geo_data by.x = 'name', by.y = 'name') %>%
merge(x = ., y = second_line,
by.x = 'name', by.y = 'name') %>%
merge(x = ., y = third_line,
by.x = 'name', by.y = 'name') %>%
merge(x = ., y = imf,
by.x = 'name', by.y = 'name')
# Remove 'income_grp' column
@data <- geo_data@data[-2]
geo_data
# Palette (from Color Brewer single hue sequential palette)
<- colorFactor(c('#feedde','#fdbe85','#fd8d3c','#d94701'),
pal_first $factors_first, ordered = TRUE)
geo_data<- colorFactor(c('#efedf5','#bcbddc','#756bb1'),
pal_second $factor_second, ordered = TRUE)
geo_data<- colorFactor(c('#e5f5e0','#a1d99b','#31a354'),
pal_third $factor_third, ordered = TRUE)
geo_data<- colorFactor(c('#eff3ff','#bdd7e7','#6baed6','#2171b5'),
pal_income $imf, ordered = TRUE)
geo_data# Popups
<- paste0('<b>Country: </b>', geo_data$name,
popup_first ' (', geo_data$imf, ')',
'<br><b>No. 1st-line classes on NEML: </b>',
$classes_first,
geo_data'<br><em>TCA (any): </em>',
$tricyclic,
geo_data'<br><em>α2δ-ligand (gabapentin): </em>',
$gabapentin,
geo_data'<br><em>α2δ-ligand (pregabalin): </em>',
$pregabalin,
geo_data'<br><em>SNRI (duloxetine): </em>',
$duloxetine,
geo_data'<br><em>SNRI (venlafaxine): </em>',
$venlafaxine)
geo_data
<- paste0('<b>Country: </b>', geo_data$name,
popup_second ' (', geo_data$imf, ')',
'<br><b>No. 2nd-line classes on NEML: </b>',
$classes_second,
geo_data'<br><em>Weak Opioid (Tramadol): </em>',
$tramadol,
geo_data'<br><em>Topical agent (5% lidocaine): </em>',
$lidocaine,
geo_data'<br><em>Topical agent (8% capsaicin): </em> no')
<- paste0('<b>Country: </b>', geo_data$name,
popup_third ' (', geo_data$imf, ')',
'<br><b>No. 3rd-line classes on NEML: </b>',
$classes_third,
geo_data'<br><em>Strong opioid (morphine): </em>',
$morphine,
geo_data'<br><em>Strong opioid (oxycodone): </em>',
$oxycodone,
geo_data'<br><em>Other (Botulinium toxin A): </em> no')
<- paste0('<b>Country: </b>', geo_other$name,
popup_NA '<br><b>No data</b>')
############################################################
# #
# Plot #
# #
############################################################
leaflet(data = geo_data,
width = '100%',
height = 600) %>%
setView(lat = 10, lng = 0, zoom = 2) %>%
# Add other countries
addPolygons(data = geo_other, weight = 1, color = '#777777',
opacity = 1, fillColor = '#777777',
fillOpacity = 1, popup = popup_NA) %>%
# Add analysed countries
addPolygons(weight = 1, color = '#000000',
opacity = 1, fillColor = ~ pal_first(factor_first),
fillOpacity = 1, popup = popup_first,
group = 'First-line drug classes') %>%
addPolygons(weight = 1, color = '#000000',
opacity = 1,fillColor = ~ pal_second(factor_second),
fillOpacity = 1, popup = popup_second,
group = 'Second-line drug classes') %>%
addPolygons(weight = 1, color = '#000000',
opacity = 1, fillColor = ~ pal_third(factor_third),
fillOpacity = 1, popup = popup_third,
group = 'Third-line drug classes') %>%
addPolygons(weight = 1, color = '#000000',
opacity = 1, fillColor = ~ pal_income(imf),
fillOpacity = 1, group = 'IMF income group') %>%
addLayersControl(baseGroups = c('First-line drug classes',
'Second-line drug classes',
'Third-line drug classes',
'IMF income group'),
options = layersControlOptions(collapsed = FALSE)) %>%
# Added new class to leafletfix.css - .info-small with smaller font size
addLegend("bottomright",
pal = pal_income,
values = ~ imf,
title = "International Monetary Fund (IMF) <br>income category",
opacity = 1) %>%
addLegend("bottomleft",
pal = pal_third,
values = ~ factor_third,
title = "No. of 3rd-line classes on NEML <br><em>(strong opioid, other)<sup>§</sup></em>",
opacity = 1) %>%
addLegend("bottomleft",
pal = pal_second,
values = ~ factor_second,
title = "No. of 2nd-line classes on NEML <br><em>(weak opioid, topical agents)<sup>‡</sup></em>",
opacity = 1) %>%
addLegend("bottomleft",
pal = pal_first, values = ~ factor_first,
title = "No. of 1st-line classes on NEML <br><em>(TCA, α2δ-ligand, SNRI)<sup>†</sup></em>",
opacity = 1)
Classification of medicines as first-, second-, or third-line was based on the recent recommendations by the Neuropathic Pain Special Interest Group of the International Association for the Study of Pain. Get the manuscript here.
†: TCA = tricyclic antidepressants, α2δ-ligand = gabapentin and pregabaline; SNRI = serotonin and noradrenaline reuptake inhibitors (duloxetine and venlafaxine)
‡: weak opioid = tramadol; topical agents = 5% lidocaine patch and 8% capsaicin patch
§: strong opioids = morphine and oxycodone; other = botulinium toxin A
About
The World Health Organization (WHO) has urged its members to ensure, "the availability of essential medicines for the management of symptoms, including pain". But, a recent audit of over 100 national essential medicines lists (NEML) for drugs recommended for the treatment of neuropathic pain (pain caused by damage to sensory nerves) identified that ~66% of countries had only one class of first-line agent (typically tricyclic antidepressants), and ~50% had only one second-line agent (typically tramadol, a weak opioid), included on their NEMLs. This means that should a patient fail to respond to initial therapy, have significant side effects, or have contraindications to a drug’s use, there are no or limited alternative agents available to switch to, or add-on to the initial agent.
To raise awareness of the plight of people in developing countries who suffer from neuropathic pain, I have previously published an online storyboard on the topic. That post included an interactive map of drugs recommended for managing neuropathic pain that are listed on national essential medicines lists of over 100 developing countries.
The space available on the storyboard for the map was very limited, so to mark the submission of an application for the inclusion of gabapentin (a first-line medicine for managing neuropathic pain) on the WHO Model List of Essential Medicines, I have reposted the map as a standalone item. The application was made by the International Association for the Study of Pain (IASP), the Neuropathic Pain Special Interest Group of the IASP (NeuPSIG), and the International Association of Hospice and Palliative Care (IAHPC), and you can download the application here and all the information used to generate the application here.
Access to anasyis script
Get the RMarkdown script, data and geojson used to generate this (and all other) painblogR posts here
Citations
Dataset
Kamerman PR, Wadley AL, Davis KD, Hietaharju A, Jain P, Kopf A, Meyer A-C, Raja SN, Rice ASC, Smith BH, Treede R-D, Wiffen PJ. Data and analysis scripts: Inclusion of medicines to treat neuropathic pain on national essential medicines lists. Figshare DOI: 10.6084/m9.figshare.4644412
Manuscript
Kamerman PR, Wadley AL, Davis KD, Hietaharju A, Jain P, Kopf A, Meyer A-C, Raja SN, Rice ASC, Smith BH, Treede R-D, Wiffen PJ. World Health Organization essential medicines lists: where are the drugs to treat neuropathic pain? PAIN 156: 793–797, 2015. DOI: 10.1097/01.j.pain.0000460356.94374.a1
Session information
sessionInfo()
## R version 4.0.4 (2021-02-15)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Catalina 10.15.7
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] geojsonio_0.9.4 lubridate_1.7.10 xml2_1.3.2
## [4] sp_1.4-5 leaflet_2.0.4.1 highcharter_0.8.2
## [7] flexdashboard_0.5.2 gganimate_1.0.7 gdtools_0.2.3
## [10] ggiraph_0.7.8 svglite_2.0.0 pander_0.6.3
## [13] knitr_1.31 forcats_0.5.1 stringr_1.4.0
## [16] dplyr_1.0.5 purrr_0.3.4 readr_1.4.0
## [19] tidyr_1.1.3 tibble_3.1.0 ggplot2_3.3.3
## [22] tidyverse_1.3.0
##
## loaded via a namespace (and not attached):
## [1] fs_1.5.0 sf_0.9-7 xts_0.12.1 progress_1.2.2
## [5] httr_1.4.2 tools_4.0.4 backports_1.2.1 bslib_0.2.4
## [9] rgdal_1.5-23 utf8_1.2.1 R6_2.5.0 KernSmooth_2.23-18
## [13] lazyeval_0.2.2 rgeos_0.5-5 DBI_1.1.1 colorspace_2.0-0
## [17] withr_2.4.1 tidyselect_1.1.0 prettyunits_1.1.1 curl_4.3
## [21] compiler_4.0.4 cli_2.3.1 rvest_1.0.0 geojsonsf_2.0.1
## [25] labeling_0.4.2 sass_0.3.1 scales_1.1.1 classInt_0.4-3
## [29] systemfonts_1.0.1 digest_0.6.27 foreign_0.8-81 rmarkdown_2.7
## [33] pkgconfig_2.0.3 htmltools_0.5.1.1 dbplyr_2.1.0 highr_0.8
## [37] htmlwidgets_1.5.3 rlang_0.4.10 readxl_1.3.1 TTR_0.24.2
## [41] httpcode_0.3.0 rstudioapi_0.13 quantmod_0.4.18 jquerylib_0.1.3
## [45] farver_2.1.0 generics_0.1.0 zoo_1.8-9 jsonlite_1.7.2
## [49] crosstalk_1.1.1 magrittr_2.0.1 rlist_0.4.6.1 Rcpp_1.0.6
## [53] munsell_0.5.0 fansi_0.4.2 lifecycle_1.0.0 stringi_1.5.3
## [57] yaml_2.2.1 jqr_1.2.0 maptools_1.0-2 grid_4.0.4
## [61] crayon_1.4.1 lattice_0.20-41 haven_2.3.1 geojson_0.3.4
## [65] hms_1.0.0 magick_2.7.0 pillar_1.5.1 igraph_1.2.6
## [69] uuid_0.1-4 crul_1.1.0 reprex_1.0.0 glue_1.4.2
## [73] evaluate_0.14 V8_3.4.0 data.table_1.14.0 modelr_0.1.8
## [77] vctrs_0.3.6 tweenr_1.0.1 cellranger_1.1.0 gtable_0.3.0
## [81] assertthat_0.2.1 xfun_0.22 broom_0.7.5 e1071_1.7-4
## [85] class_7.3-18 viridisLite_0.3.0 units_0.7-0 ellipsis_0.3.1