Melanoma Incidence in Nordic Countries

With special focus on Norway

Author

Raju Rimal

Published

20 June 2023

Introduction

Cutaneous melanoma (CM) is the most aggressive and lethal form of skin cancer. Melanoma can be cured if caught and treated early but if left untreated, it may spread to other parts and can be fatal. In the recent years, melanoma has increased dramatically in fair skinned population worldwide including Nordic countries like Norway, Denmark, and Sweden. Norway is ranked fifth in incidence and third in mortality worldwide. This increase can be an effect of increased awareness in general public and health care provider.

This article explores melanoma incidence and mortality in nordic countries by sex and their trend over 40-years period from 1980–2020. Further, I try to step through the analysis process from data collection to create plots and tables.

Data Preparation

Data on melanoma were obtained from NORDCAN Engholm et al. (2010), Association of the Nordic Cancer Registries, IARC. Using NORDCAN 2.0 API1 crude and age-adjusted rates were downloaded as JSON and converted to tabular data for further analysis. R(R Core Team 2020) software was used for data gathering, cleanup, analysis, and plotting.

The API endpoint has four placeholder type, sex, country, and cancer following design was used to create individual endpoint. These individual url are used to download the JSON file as list in R.

Code for preparing download URL
design_map <- list(
  sex = c(Male = 1, Female = 2),
  type = c(Incidence = 0, Mortality = 1),
  cancer = c(Melanoma = 290),
  country = c(
    Denmark = 208, Finland = 246, Iceland = 352,
    Norway = 578, Sweden = 752
  )
)
label_values <- function(data, label_map, var) {
  label_vec <- label_map[[var]]
  label <- `names<-`(names(label_vec), label_vec)
  data[[var]] <- data[, label[as.character(get(var))]]
  return(data)
}

design <- CJ(
  sex = 1:2,
  type = 0:1,
  cancer = 290,
  country = c(208, 246, 352, 578, 752)
)
design[, url := glue::glue_data(.SD, 
  "https://gco.iarc.fr/gateway_prod/api/nordcan/v2/92/data/population/{type}/{sex}/({country})/({cancer})/?ages_group=5_17&year_start=1980&year_end=2020&year_grouped=0"
)]
design <- design %>% 
  label_values(design_map, "sex") %>% 
  label_values(design_map, "type") %>% 
  label_values(design_map, "cancer") %>% 
  label_values(design_map, "country")
API URL:
https://gco.iarc.fr/gateway_prod/api/nordcan/v2/92/data/population/{type}/{sex}/({country})/({cancer})/?ages_group=5_17&year_start=1980&year_end=2020&year_grouped=0

Replacing the placeholders type (Incidence: 0, Mortality: 1), sex (Male: 1, Female: 2), country (Denmark: 208, Finland: 246, Iceland: 352, Norway: 578, and Sweden: 752), and cancer (Melanoma: 290) prepare the data API.

Code
json_data = await FileAttachment("Data/nordcan.json").json()
json_data[0]
Code for data download
if (!file.exists("Data/nordcan-json.Rds")) {
  design[, data := map(url, ~read_json(.x) %>% pluck("dataset"))]
  saveRDS(design, file = "Data/nordcan-json.Rds")
} else {
  design <- readRDS("Data/nordcan-json.Rds")
}
design[, .(sex, type, country, data)]
       sex      type country       data
 1:   Male Incidence Denmark <list[41]>
 2:   Male Incidence Finland <list[41]>
 3:   Male Incidence Iceland <list[41]>
 4:   Male Incidence  Norway <list[41]>
 5:   Male Incidence  Sweden <list[41]>
 6:   Male Mortality Denmark <list[41]>
 7:   Male Mortality Finland <list[41]>
 8:   Male Mortality Iceland <list[41]>
 9:   Male Mortality  Norway <list[41]>
10:   Male Mortality  Sweden <list[41]>
11: Female Incidence Denmark <list[41]>
12: Female Incidence Finland <list[41]>
13: Female Incidence Iceland <list[41]>
14: Female Incidence  Norway <list[41]>
15: Female Incidence  Sweden <list[41]>
16: Female Mortality Denmark <list[41]>
17: Female Mortality Finland <list[41]>
18: Female Mortality Iceland <list[41]>
19: Female Mortality  Norway <list[41]>
20: Female Mortality  Sweden <list[41]>
Rate data frame
rate_df <- design[, map_df(data, function(dta) {
  out <- data.table(
    year = map_int(dta, "year"),
    asr_w = map_dbl(dta, "asr"),
    asr_e = map_dbl(dta, "asr_e"),
    asr_n = map_dbl(dta, "asr_n"),
    crude_rate = map_dbl(dta, "crude_rate"),
    count = map_dbl(dta, "total"),
    population = map_dbl(dta, "total_pop"),
    cum_risk = map(dta, "cum_risk") %>% 
      unlist()
  )
  if ("cum_risk" %in% names(out)) {
    out[, cum_risk := as.numeric(cum_risk)]
  }
  return(out)
}), by = .(sex, type, country)]
Classes 'data.table' and 'data.frame':  820 obs. of  10 variables:
 $ sex       : chr  "Male" "Male" "Male" "Male" ...
 $ type      : chr  "Incidence" "Incidence" "Incidence" "Incidence" ...
 $ country   : chr  "Denmark" "Denmark" "Denmark" "Denmark" ...
 $ year      : int  1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 ...
 $ asr_w     : num  11.9 10.9 12.2 13.4 13.8 ...
 $ asr_e     : num  12.7 12.1 13.1 14.5 15.3 ...
 $ asr_n     : num  13.5 13.1 14.1 15.2 16 ...
 $ crude_rate: num  12.4 12 13.1 14.3 14.8 ...
 $ count     : num  196 191 210 230 239 234 269 283 291 306 ...
 $ population: num  1585436 1593815 1601416 1609960 1618038 ...
 - attr(*, ".internal.selfref")=<externalptr> 
Rate by age
rate_by_age <- design[, map_df(data, function(dta) {
  year <- map_int(dta, "year")
  count_df <- map_df(dta, "ages") %>% 
    as.data.table() %>%
    cbind(year = year) %>% 
    melt.data.table(
      id.vars = "year",
      variable.name = "age_group",
      value.name = "count"
    )
  pop_df <- map_df(dta, "populations") %>% 
    as.data.table() %>%
    cbind(year = year) %>% 
    melt.data.table(
      id.vars = "year",
      variable.name = "age_group",
      value.name = "population"
    )
  asr_df <- map_df(dta, "age_specific_rate") %>% 
    modify(as.numeric) %>% 
    as.data.table() %>%
    cbind(year = as.numeric(year)) %>% 
    melt.data.table(
      id.vars = "year",
      variable.name = "age_group",
      value.name = "asr"
    )
  out <- reduce(
    list(count_df, pop_df, asr_df),
    merge.data.table,
    by = c("year", "age_group")
  )
  age_lbl <- paste(
    seq(0, 85, 5),
    seq(0, 85, 5) + 4,
    sep = "-"
  )
  age_lbl[length(age_lbl)] <- "85+"
  names(age_lbl) <- 1:18
  
  out[, age_group := age_lbl[age_group]]
  out[, year := as.integer(year)]
  out[, count := as.integer(count)]
  out[, population := as.integer(population)]
  out[, asr := as.numeric(asr)]
  return(out[])
}), by = .(sex, type, country)]
Classes 'data.table' and 'data.frame':  14760 obs. of  8 variables:
 $ sex       : chr  "Male" "Male" "Male" "Male" ...
 $ type      : chr  "Incidence" "Incidence" "Incidence" "Incidence" ...
 $ country   : chr  "Denmark" "Denmark" "Denmark" "Denmark" ...
 $ year      : int  1980 1980 1980 1980 1980 1980 1980 1980 1980 1980 ...
 $ age_group : chr  "0-4" "5-9" "10-14" "15-19" ...
 $ count     : int  0 1 1 1 2 8 12 16 13 23 ...
 $ population: int  164317 185710 203405 199741 190444 191785 215098 184439 149015 135409 ...
 $ asr       : num  0 0.538 0.492 0.501 1.05 ...
 - attr(*, ".internal.selfref")=<externalptr> 

Analysis

For the following analysis, crude rates were used in visualization and modelling. Stratified by sex, country and type following plots presents the the crude_rate over the year of diagnosis. Additionally, using count and population, a poisson regression model (Equation 1) was fitted.

\[ \begin{align} \log\left(\frac{\lambda}{Y}\right) &= \beta_0 + \beta_1 x + \varepsilon \\ \text{equivalently, } \log\left(\lambda\right) &= \beta_0 + \beta_1 x + \log(Y) + \varepsilon \end{align} \tag{1}\]

where, \(\lambda\) is the number of events (count), \(Y\) is the number of exposed (population) and \(x\) is the year of diagnosis.

Additionally, using segmented regression the change points in the trend was identified and the annual percentage change (APC) and average annual percentage change (AAPC) in crude rate were calculated. For each strata, following R-code for poisson regression model and segmented regression model were used.

Data within each strata
nested_df <- rate_df[, .(
  data = list(.SD)
), by = .(sex, type, country)]
head(nested_df)
    sex      type country               data
1: Male Incidence Denmark <data.table[41x7]>
2: Male Incidence Finland <data.table[41x7]>
3: Male Incidence Iceland <data.table[41x7]>
4: Male Incidence  Norway <data.table[41x7]>
5: Male Incidence  Sweden <data.table[41x7]>
6: Male Mortality Denmark <data.table[41x7]>
Poisson regression model
model <- glm(
  count ~ year + offset(log(population)),
  data = rate_df,
  family = poisson(link = "log")
)
Segmented regression model
sgmt_model <- segmented(model, npsi = 2)
Poisson and segmented fit
fitted_df <- nested_df[, map_df(data, function(dta) {
  fit <- glm(
    count ~ year + offset(log(population)),
    family = poisson(link = "log"),
    data = dta
  )
  sgmt_fit <- segmented(fit, npsi = 2)

  data.table(fit = list(fit), sgmt_fit = list(sgmt_fit))
}), by = .(sex, type, country)]
head(fitted_df)
    sex      type country       fit        sgmt_fit
1: Male Incidence Denmark <glm[30]> <segmented[42]>
2: Male Incidence Finland <glm[30]> <segmented[42]>
3: Male Incidence Iceland <glm[30]> <segmented[42]>
4: Male Incidence  Norway <glm[30]> <segmented[42]>
5: Male Incidence  Sweden <glm[30]> <segmented[42]>
6: Male Mortality Denmark <glm[30]> <segmented[42]>

Following plots highlighting Norway and Finland for comparison show a higher melanoma incidence and mortality rate in Norway compared to Finland. A plateau was observed in melanoma incidence in Norway in both male and female.

  • Norway has higher melanoma incidence and mortality than Finland.
  • Finland has lowest melanoma incidence and mortality in both sexes
  • Norway has highest melanoma mortality followed by Sweden in both sexes
  • Denmark has surpassed both Norway and Sweden in melanoma incidence in the recent years
  • A plateau period was observed in melanoma incidence in Norway.
  • Most countries has raise in melanoma incidence after 2005.
  • A declining melanoma moratlity was observed in all countries in recent years.

Model

Annual percentge change (APC)

APC within segments

Code
a_apc <- readRDS("Results/APC.Rds")
apc <- copy(a_apc[[1]])
aapc <- copy(a_apc[[2]])
apc[, label_period := glue::glue_data(.SD, "{label}<br>{psi}")]
apc[, segment := gsub("slope", "", segment)]
Code
apc[country %in% c("Norway", "Denmark")] %>% 
  dcast.data.table(
  country + segment ~ type + sex,
  value.var = "label_period"
) %>% gt::gt(
  id = "apc-table",
  rowname_col = "segment",
  groupname_col = "country"
) %>% gt::tab_spanner_delim("_") %>% 
  gt::fmt_markdown(everything()) %>% 
  gt::sub_missing(everything(), missing_text = "-") %>%
  gt::tab_stubhead("Segment") %>% 
  gt::tab_options(
    data_row.padding = "0px",
    table.width = "100%",
    table.font.size = "small",
    column_labels.font.weight = "bold",
    row_group.font.weight = "bold"
  )
Segment Incidence Mortality
Female Male Female Male
Denmark

1

2.73 (2.42, 3.04)
1980-2002

3.53 (3.22, 3.84)
1980-2004

10.46 (-6.14, 29.98)
1980-1982

0.62 (0.08, 1.16)
1980-2004

2

6.92 (5.8, 8.04)
2002-2011

9.86 (7.38, 12.39)
2004-2009

0.65 (0.26, 1.03)
1982-2014

4.02 (0.85, 7.29)
2004-2012

3

1.79 (1.18, 2.41)
2011-2020

3.12 (2.55, 3.69)
2009-2020

-1.98 (-6.26, 2.5)
2014-2020

-2.14 (-4.06, -0.17)
2012-2020

Norway

1

3.92 (2.9, 4.96)
1980-1990

5.49 (4.32, 6.67)
1980-1991

1 (0.18, 1.84)
1980-2000

17.06 (-0.38, 37.54)
1980-1982

2

-0.1 (-1.26, 1.08)
1990-1999

0.45 (-0.44, 1.35)
1991-2002

2.7 (1.31, 4.1)
2000-2013

1.79 (1.4, 2.19)
1982-2011

3

3.62 (3.35, 3.88)
1999-2020

4.39 (4.08, 4.69)
2002-2020

-5.01 (-8.14, -1.76)
2013-2020

-1.97 (-3.79, -0.12)
2011-2020

Model

Incidence model:

Code
mdl_inc <- glm(
  data = rate_df,
  formula = count ~ year + sex + country,
  offset = log(population),
  family = poisson(link = "log"),
  subset = type == "Incidence"
)

Mortality model

Code
mdl_mor <- glm(
  data = rate_df,
  formula = count ~ year + sex + country,
  offset = log(population),
  family = poisson(link = "log"),
  subset = type == "Mortality"
)

Incidence model summary

Code
broom::tidy(mdl_inc, conf.int = TRUE, exponentiate = TRUE) %>% 
  modify_at(c(2:4, 6:7), round, 3)
# A tibble: 7 × 7
  term           estimate std.error statistic   p.value conf.low conf.high
  <chr>             <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
1 (Intercept)       0         0.383   -205.   0            0         0    
2 year              1.04      0        185.   0            1.04      1.04 
3 sexMale           1.01      0.004      1.90 5.68e-  2    1         1.02 
4 countryFinland    0.649     0.007    -62.6  0            0.64      0.658
5 countryIceland    0.494     0.028    -25.6  1.86e-144    0.468     0.521
6 countryNorway     1.05      0.006      8.09 5.84e- 16    1.04      1.06 
7 countrySweden     0.936     0.005    -12.1  1.41e- 33    0.926     0.946

Mortality model summary

Code
broom::tidy(mdl_mor, conf.int = TRUE, exponentiate = TRUE) %>% 
  modify_at(c(2:4, 6:7), round, 3)
# A tibble: 7 × 7
  term           estimate std.error statistic   p.value conf.low conf.high
  <chr>             <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
1 (Intercept)       0         0.844    -40.9  0            0         0    
2 year              1.01      0         29.2  1.53e-187    1.01      1.01 
3 sexMale           1.46      0.01      38.0  0            1.43      1.49 
4 countryFinland    0.731     0.016    -19.2  1.67e- 82    0.708     0.755
5 countryIceland    0.611     0.061     -8.02 1.05e- 15    0.54      0.688
6 countryNorway     1.24      0.015     14.6  3.57e- 48    1.20      1.27 
7 countrySweden     1.03      0.013      2.2  2.78e-  2    1.00      1.06 

Summary

  • Melanoma incidence and moratlity is increasing in all countries
  • Norway has highest mortality rate through out the period and has highest increase in both incidence and mortality.
  • Melanoma incidence increased rapidly in Denmark between 2002 and 2011 in women and 2004-2009 in men surpassing Norway.
  • Mortality is decreasing in all countries in recent period.

References

Engholm, Gerda, Jacques Ferlay, Niels Christensen, Freddie Bray, Marianne L. Gjerstorff, Åsa Klint, Jóanis E. Køtlum, Elínborg Ólafsdóttir, Eero Pukkala, and Hans H. Storm. 2010. NORDCAN – a Nordic Tool for Cancer Information, Planning, Quality Control and Research.” Acta Oncologica 49 (5): 725–36. https://doi.org/ch4598.
Larønningen, S., J. Ferlay, H. Beydogan, F. Bray, G. Engholm, M. Ervik, J. Gulbrandsen, et al. 2022. NORDCAN: Cancer Incidence, Mortality, Prevalence and Survival in the Nordic Countries, Version 9.2 (23.06.2022).” 2022. https://nordcan.iarc.fr/.
Muggeo, Vito M. R. 2008. “Segmented: An r Package to Fit Regression Models with Broken-Line Relationships.” R News 8 (1): 20–25. https://cran.r-project.org/doc/Rnews/.
Norway, Cancer Registry of. 2022. “Cancer in Norway 2021: Cancer Incidence, Mortality, Survival and Prevalence in Norway.” 0806-3621. Cancer Registry of Norway. https://www.kreftregisteret.no/globalassets/cancer-in-norway/2021/cin_report.pdf.
R Core Team. 2020. “R: A Language and Environment for Statistical Computing.” Manual. Vienna, Austria. https://www.R-project.org/.
Welch, H. Gilbert, Benjamin L. Mazer, and Adewole S. Adamson. 2021. “The Rapid Rise in Cutaneous Melanoma Diagnoses.” New England Journal of Medicine 384 (1): 72–79. https://doi.org/gm6vs4.
Whiteman, David C., Adele C. Green, and Catherine M. Olsen. 2016. “The Growing Burden of Invasive Melanoma: Projections of Incidence Rates and Numbers of New Cases in Six Susceptible Populations Through 2031.” The Journal of Investigative Dermatology 136 (6): 1161–71. https://doi.org/f8psv4.

Footnotes

  1. https://gco.iarc.fr/gateway_prod/api/nordcan/v2/92/data/population/{type}/{sex}/({country})/({cancer})/?ages_group=5_17&year_start=1980&year_end=2020&year_grouped=0↩︎