HealthIntelAct Training Week - Aalborg
Assistant professor - Center for Danish Health Services Research
2026-10-06
Background
Interests
Research
Non-reproducible workflow
Examples:
Rainhart & Rogoff - Growth in a Time of Debt (2010)
The replication crisis
Reproducible workflow
Unfortunately incentives doesn’t always align
Important initiatives:
An Application Programming Interface lets code ask a server for data directly - no clicking in a web page.
Many institutions issue public facing API
Increase in use of API keys in public api - to manage access
OECD Data Explorer See if you can find life expectancy in Denmark in 2015
What do you think the lift expectancy for DK, USA and Mexico was in 2015 (and why?)
Health > Health status > Life expectancy > DK: 80.7 years, US: 78.7, MEX: 75.1
We will be using the unofficial R package OECD Github
The server returns the data, and the R OECD package turns it into a data.frame. The OECD uses the SDMX standard, so all datasets are organised in the same way: dimensions (e.g. country, age, sex) and observations (the value).
Tasks:
Use the data explorer in R or:
life2 <- life |>
select(REF_AREA, MEASURE, AGE, TIME_PERIOD, SEX, ObsValue) |>
filter(TIME_PERIOD == 2015,
MEASURE == "LFEXP",
REF_AREA %in% c("MEX","SWE","NOR","DNK","DEU","USA")) |>
mutate(AGE2 = parse_number(AGE),
SEX2 = case_when(SEX=="M"~"Male",
SEX=="F"~"Female",
SEX=="_T"~"Total"),
ObsValue = as.numeric(ObsValue))
life2 |> filter(SEX!="_T") |>
ggplot(aes(x=AGE2, y=ObsValue, color=REF_AREA)) +
geom_line(linewidth = 1) +
scale_x_continuous(breaks=c(0,40,60,65,80)) +
scale_color_brewer(palette="Paired")+
facet_wrap(~SEX) +
theme(panel.grid = element_blank()) +
labs(title="Life expectancy 2015", y="years",x="Age")life2 |> filter(SEX!="_T") |>
select(-SEX) |>
pivot_wider(names_from = SEX2, values_from = ObsValue) |>
mutate(rate = Male/(Female)) |>
ggplot(aes(x=AGE2, y=rate)) +
geom_line(linewidth = 1) +
scale_x_continuous(breaks=c(0,40,60,65,80)) +
scale_color_brewer(palette="Paired")+
facet_wrap(~REF_AREA) +
theme(panel.grid = element_blank()) +
labs(title="Expected lifespwn", y="years",x="Age")life3 <- life |>
select(REF_AREA, MEASURE, AGE, TIME_PERIOD, SEX, ObsValue) |>
filter(TIME_PERIOD %in% c(1990,2000,2010,2020),
MEASURE == "LFEXP",
REF_AREA %in% c("MEX","SWE","NOR","DNK","DEU","USA")) |>
mutate(AGE2 = parse_number(AGE),
SEX2 = case_when(SEX=="M"~"Male",
SEX=="F"~"Female",
SEX=="_T"~"Total"),
ObsValue = as.numeric(ObsValue))
life3 |> filter(SEX!="_T") |>
select(-SEX) |>
pivot_wider(names_from = SEX2, values_from = ObsValue) |>
mutate(rate = Male/(Female)) |>
ggplot(aes(x=AGE2, y=rate, linetype=TIME_PERIOD)) +
geom_line(linewidth = 0.5) +
scale_x_continuous(breaks=c(0,40,60,65,80)) +
scale_color_brewer(palette="Paired")+
facet_wrap(~REF_AREA) +
theme(panel.grid = element_blank()) +
labs(title="Expected lifespwn", y="years",x="Age")life3 <- life |>
select(REF_AREA, MEASURE, AGE, TIME_PERIOD, SEX, ObsValue) |>
filter(MEASURE == "LFEXP",
REF_AREA %in% c("MEX","SWE","NOR","DNK","DEU","USA")) |>
mutate(AGE2 = parse_number(AGE),
SEX2 = case_when(SEX=="M"~"Male",
SEX=="F"~"Female",
SEX=="_T"~"Total"),
ObsValue = as.numeric(ObsValue))
life3 |> filter(SEX!="_T") |>
select(-SEX) |>
pivot_wider(names_from = SEX2, values_from = ObsValue) |>
mutate(rate = Male/(Female)) |>
ggplot(aes(x=AGE2, y=rate, color=TIME_PERIOD)) +
geom_line(linewidth = 0.5) +
scale_x_continuous(breaks=c(0,40,60,65,80)) +
facet_wrap(~REF_AREA) +
theme(panel.grid = element_blank()) +
labs(title="Expected lifespwn", y="years",x="Age")Copy the code and do the following tasks.
Tasks:
| code | Financing Scheme |
|---|---|
| HF11 | Government schemes |
| HF121 | Social health insurance schemes |
| HF122 | Compulsory private insurance schemes |
| HF2 | Voluntary healthcare payment schemes |
| HF3 | Household out-of-pocket payment |
plot <- funding |>
mutate(obsTime = as.numeric(TIME_PERIOD)) |>
mutate(ObsValue = as.numeric(ObsValue)) |>
filter(between(obsTime, 2001, 2025)) |>
mutate(date = as.Date(paste0(obsTime,"-01-01"))) |>
select(date, ObsValue, FINANCING_SCHEME, REF_AREA) |>
add_row(date = as.Date("2013-01-01"), ObsValue = 0, FINANCING_SCHEME = "HF122", REF_AREA = "USA") |>
add_row(date = as.Date("2005-01-01"), ObsValue = 0, FINANCING_SCHEME = "HF122", REF_AREA = "NLD") |>
add_row(date = as.Date("2015-01-01"), ObsValue = 0, FINANCING_SCHEME = "HF122", REF_AREA = "FRA") |>
add_row(date = as.Date("2005-01-01"), ObsValue = 0, FINANCING_SCHEME = "HF121", REF_AREA = "FRA") |>
add_row(date = as.Date("2008-01-01"), ObsValue = 0, FINANCING_SCHEME = "HF122", REF_AREA = "DEU") |>
mutate(REF_AREA = fct_relevel(REF_AREA, "DNK","SWE","GBR")) |>
ggplot(aes(date, ObsValue, fill = FINANCING_SCHEME)) +
geom_area() +
facet_wrap(~REF_AREA, nrow = 1) +
scale_y_continuous(breaks = c(seq(0,20, by = 4)),
label = unit_format(unit = "%", sep = "", accuracy = 1)) +
scale_fill_manual("",values = palblue(5),
labels = c("Government\nschemes",
"Social health\ninsurance schemes",
"Compulsory private\ninsurance schemes",
"Voluntary healthcare\npayment schemes",
"Household out-of-pocket\npayment")) +
scale_x_date(breaks = seq(ymd("2002-01-01"), ymd("2023-01-01"), by = '3 years'),
date_labels = "%y",
limits = as.Date(c("2001-01-01","2024-01-01"))) +
labs(title = NULL,
caption = "Source: OECD - SHA (Health expenditure and financing)",
x = NULL, y = "GDP") +
theme(legend.key.height = unit(2,"line"),
legend.key = element_blank(),
legend.position = "top",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
plotfund <- get_dataset(dataset = "OECD.ELS.HD,DSD_SHA@DF_SHA",
filter = ".A.EXP_HEALTH.PT_B1GQ.HF1+HF2+HF3.._T._T._T..._Z") |>
select(FINANCING_SCHEME, REF_AREA, TIME_PERIOD, ObsValue) |>
group_by(TIME_PERIOD, REF_AREA) |>
summarize(healthcare = sum(as.numeric(ObsValue), na.rm=T))
combined <- life |> filter(MEASURE == "LFEXP", SEX == "F") |>
select(AGE, ObsValue, REF_AREA, TIME_PERIOD) |>
left_join(fund) |>
mutate(expectedLife = as.numeric(ObsValue),
year=as.numeric(TIME_PERIOD)) |>
filter(REF_AREA!="RUS", TIME_PERIOD<2025, !is.na(healthcare))
combined |>
ggplot(aes(x=healthcare, y=expectedLife, color=REF_AREA)) +
geom_point(size=0.5,show.legend = F) + facet_wrap(~AGE, scales="free_y") +
geom_path(show.legend = F) +
geom_text(data=combined |> filter(TIME_PERIOD==2024), color="black",size=2,aes(label=REF_AREA))combined |> filter(AGE == "Y65") |>
ggplot(aes(x=healthcare, y=expectedLife, color=REF_AREA)) +
geom_point(size=0.5,show.legend = F) + facet_wrap(~AGE, scales="free_y") +
geom_path(show.legend = F) +
geom_text(data=combined |> filter(TIME_PERIOD==2024,AGE == "Y65"), color="black",size=2,aes(label=REF_AREA))combined |> filter(AGE == "Y65", year>1999) |>
ggplot(aes(x=healthcare, y=expectedLife, color=REF_AREA)) +
geom_point(size=0.5,show.legend = F) + facet_wrap(~REF_AREA, scales="free") +
geom_path(show.legend = F)+
geom_text(data=combined |> filter(TIME_PERIOD==2024,AGE == "Y65"), color="black",size=2,aes(label=REF_AREA))combined2 <- combined |> filter(AGE == "Y65") |>
filter(REF_AREA %in% c("DNK","USA","SWE","MEX","GER","DEU","FRA","JPN","POL","ITA")) |>
mutate(healthcare = if_else(REF_AREA=="ITA" & healthcare <4,NA,healthcare))
combined2 |>
ggplot(aes(x=healthcare, y=expectedLife, color=REF_AREA)) +
geom_path(show.legend = F,size=0.7) +
geom_point(size=1.2,show.legend = T, shape=21, fill="white",stroke = 1.3) +
scale_y_continuous(breaks=seq(0,26,2),labels = scales::label_number(suffix = " years"))+
scale_x_continuous(breaks=seq(0,26,2),labels = scales::label_number(suffix = "%"))+
scale_color_brewer(palette="Paired")+
geom_text(data=combined2 |> filter(TIME_PERIOD==2024,AGE == "Y65"), color="black",size=3,aes(label=REF_AREA,y=expectedLife+0.2)) +
labs(title = "Healthcare spending vs. expected remaining lifetime at 65 1970 - 2024",
caption = "Source: OECD - SHA (Health expenditure and financing) and LE (Life expectancy)",
x = "Healthcare spending as fraction of GDP", y = "Expected remaining lifetime at 65",
color="Country") +
theme(legend.key = element_blank(),
legend.position = "right",
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())