Introduction

Joinpoint regression is commonly used in epidemiology to identify changes in temporal trends. The joinpointR package provides tools to fit joinpoint regression models, estimate annual percentage changes, and present results.

Joinpoint regression models by group

The function model_jp()allows to fit joinpoint regression models with a log-response by levels of one or more categorical variable(s). For the examples, we are going to use a simulated dataset with the HIV rates by sex in five regions

# Load required packages
library(dplyr)
library(tidyr)
library(ggplot2)
library(joinpointR)

# Load example data
data(hiv_data)

Stepwise joinpoint regression by sex

mod1 <- model_jp(
  data = hiv_data,
  value = hiv_rate,
  time = year,
  group = "sex",
  k = 3,
  step = TRUE,
  test = TRUE
)
#> Model: Female | Joinpoint(s): no significant joinpoints detected
#> Model: Male | Joinpoint(s): no significant joinpoints detected

# Show the model output
mod1
#> $Female
#> 
#> Call:
#> segmented::selgmented(olm = stats::lm(formula = log(hiv_rate) ~ 
#>     year), Kmax = 3)
#> 
#> Coefficients:
#> (Intercept)     .jp_time  
#>    37.81573     -0.01648  
#> 
#> 
#> $Male
#> 
#> Call:
#> segmented::selgmented(olm = stats::lm(formula = log(hiv_rate) ~ 
#>     year), Kmax = 3)
#> 
#> Coefficients:
#> (Intercept)     .jp_time  
#>    35.25705     -0.01516

Fixed number of joinpoints by sex

mod2 <- model_jp(
  data = hiv_data,
  value = hiv_rate,
  time = year,
  group = "sex",
  k = 1,
  step = FALSE,
  test = FALSE
)
#> Model: Female | Joinpoint(s): 2020
#> Model: Male | Joinpoint(s): 2019

# Show the model output
mod2
#> $Female
#> Call: segmented::segmented(obj = stats::lm(formula = log(hiv_rate) ~ 
#>     year), seg.Z = ~year, npsi = 1)
#> 
#> Coefficients of the linear terms:
#> (Intercept)     .jp_time  U1..jp_time  
#>    54.61669     -0.02482      0.02718  
#> 
#> Estimated Break-Point(s):
#> psi1..jp_time  
#>          2020  
#> 
#> $Male
#> Call: segmented::segmented(obj = stats::lm(formula = log(hiv_rate) ~ 
#>     year), seg.Z = ~year, npsi = 1)
#> 
#> Coefficients of the linear terms:
#> (Intercept)     .jp_time  U1..jp_time  
#>    51.39025     -0.02318      0.02431  
#> 
#> Estimated Break-Point(s):
#> psi1..jp_time  
#>          2019

Stepwise joinpoint regression models by sex and region

mod3 <- model_jp(
  data = hiv_data,
  value = hiv_rate,
  time = year,
  group = c("region", "sex"),
  step = TRUE
)
#> Model: Central_Female | Joinpoint(s): 2019
#> Model: Central_Male | Joinpoint(s): 2013.7; 2019.0
#> Model: East_Female | Joinpoint(s): 2014.8; 2019.1
#> Model: East_Male | Joinpoint(s): 2014.4; 2019.0
#> Model: North_Female | Joinpoint(s): 2015.4; 2019.0
#> Model: North_Male | Joinpoint(s): 2014.9; 2019.1
#> Model: South_Female | Joinpoint(s): 2019
#> Model: South_Male | Joinpoint(s): 2019
#> Model: West_Female | Joinpoint(s): 2017.9; 2019.4
#> Model: West_Male | Joinpoint(s): 2019

Annual percentage change (APC)

The function get_apc() provides the annual percentage change and its 95% confidence interval for models with significant joinpoints (class: "segmented" "lm")

get_apc(mod2)
#>    model segment   APC CI_low CI_upp
#> 1 Female       1 -2.5%  -3.6%  -1.3%
#> 2 Female       2  0.2%  -2.3%   2.8%
#> 3   Male       1 -2.3%  -3.3%  -1.3%
#> 4   Male       2  0.1%  -2.1%   2.4%

The function returns empty rows when no significant joinpoints are detected (class = "lm").

get_apc(mod1, digits = 1)
#> # A tibble: 2 × 5
#>   model  segment APC   CI_low CI_upp
#>   <chr>  <chr>   <chr> <chr>  <chr> 
#> 1 Female <NA>    <NA>  <NA>   <NA>  
#> 2 Male   <NA>    <NA>  <NA>   <NA>

Average annual percent change (AAPC)

The function get_aapc() allows users to estimate the global trend even when no significant joinpoints were detected.

get_aapc(mod1, digits = 1, show_ci = TRUE)
#> # A tibble: 2 × 2
#>   model  AAPC                
#>   <chr>  <chr>               
#> 1 Female -1.6% (-2.2%; -1.1%)
#> 2 Male   -1.5% (-2.0%; -1.0%)

You can choose to show significance stars instead of CI:

get_aapc(mod1, digits = 1, show_ci = FALSE)
#> # A tibble: 2 × 2
#>   model  AAPC   
#>   <chr>  <chr>  
#> 1 Female -1.6% *
#> 2 Male   -1.5% *

Summary tables

Results as a data frame

summary_jp(mod1)
#> # A tibble: 2 × 7
#>   group     jp period APC   CI_low CI_upp AAPC   
#>   <chr>  <dbl> <chr>  <chr> <chr>  <chr>  <chr>  
#> 1 Female     0 <NA>   <NA>  <NA>   <NA>   -1.6% *
#> 2 Male       0 <NA>   <NA>  <NA>   <NA>   -1.5% *

HTML Tables

# id: tab-2
mod2 |>
  summary_jp(digits = 1) |>
  jp_to_ft()

Group

JP

Period

APC

CI

AAPC

Female

1

2010-2020

-2.5%

-3.6%; -1.3%

-1.5% *

1

2020-2025

0.2%

-2.3%; 2.8%

Male

1

2010-2019

-2.3%

-3.3%; -1.3%

-1.4% *

1

2019-2025

0.1%

-2.1%; 2.4%

* p < 0.05
JP: number of joinpoints; APC: annual percent change; CI: 95% confidence interval; AAPC: average annual percent change (95% CI).

HTML Tables by two grouping variables

mod3 |>
  summary_jp(digits = 1) |>
  jp_to_ft()

Group

Subgroup

JP

Period

APC

CI

AAPC

Central

Female

1

2010-2019

-4.7%

-4.9%; -4.6%

-1.7% *

Female

1

2019-2025

3.4%

3.0%; 3.8%

Male

2

2010-2014

-3.9%

-4.5%; -3.3%

-1.5% *

Male

2

2014-2019

-4.7%

-5.1%; -4.4%

Male

2

2019-2025

3.1%

2.8%; 3.3%

East

Female

2

2010-2015

-5.5%

-6.1%; -4.9%

-3.0% *

Female

2

2015-2019

-8.0%

-8.6%; -7.4%

Female

2

2019-2025

3.1%

2.6%; 3.6%

Male

2

2010-2014

-4.9%

-5.3%; -4.5%

-2.9% *

Male

2

2014-2019

-7.2%

-7.6%; -6.9%

Male

2

2019-2025

2.1%

1.8%; 2.4%

North

Female

2

2010-2015

-5.2%

-5.6%; -4.7%

-1.8% *

Female

2

2015-2019

-6.8%

-8.2%; -5.5%

Female

2

2019-2025

4.7%

4.2%; 5.1%

Male

2

2010-2015

-4.7%

-5.1%; -4.3%

-1.5% *

Male

2

2015-2019

-5.9%

-6.2%; -5.5%

Male

2

2019-2025

4.4%

4.1%; 4.7%

South

Female

1

2010-2019

3.3%

3.1%; 3.6%

0.3% *

Female

1

2019-2025

-3.9%

-4.3%; -3.6%

Male

1

2010-2019

2.8%

2.6%; 3.0%

0.2% *

Male

1

2019-2025

-3.6%

-3.9%; -3.4%

West

Female

2

2010-2018

2.2%

2.0%; 2.4%

-1.0% *

Female

2

2018-2019

0.2%

-1.9%; 2.4%

Female

2

2019-2025

-5.6%

-6.0%; -5.3%

Male

1

2010-2019

1.9%

1.7%; 2.1%

-0.9% *

Male

1

2019-2025

-5.0%

-5.5%; -4.6%

* p < 0.05
JP: number of joinpoints; APC: annual percent change; CI: 95% confidence interval; AAPC: average annual percent change (95% CI).

Change the table language to Spanish

mod3 |>
  summary_jp(digits = 1) |>
  jp_to_ft(lan = "es")

Grupo

Subgrupo

JP

Periodo

APC

IC

AAPC

Central

Female

1

2010-2019

-4,7%

-4,9%; -4,6%

-1,7% *

Female

1

2019-2025

3,4%

3,0%; 3,8%

Male

2

2010-2014

-3,9%

-4,5%; -3,3%

-1,5% *

Male

2

2014-2019

-4,7%

-5,1%; -4,4%

Male

2

2019-2025

3,1%

2,8%; 3,3%

East

Female

2

2010-2015

-5,5%

-6,1%; -4,9%

-3,0% *

Female

2

2015-2019

-8,0%

-8,6%; -7,4%

Female

2

2019-2025

3,1%

2,6%; 3,6%

Male

2

2010-2014

-4,9%

-5,3%; -4,5%

-2,9% *

Male

2

2014-2019

-7,2%

-7,6%; -6,9%

Male

2

2019-2025

2,1%

1,8%; 2,4%

North

Female

2

2010-2015

-5,2%

-5,6%; -4,7%

-1,8% *

Female

2

2015-2019

-6,8%

-8,2%; -5,5%

Female

2

2019-2025

4,7%

4,2%; 5,1%

Male

2

2010-2015

-4,7%

-5,1%; -4,3%

-1,5% *

Male

2

2015-2019

-5,9%

-6,2%; -5,5%

Male

2

2019-2025

4,4%

4,1%; 4,7%

South

Female

1

2010-2019

3,3%

3,1%; 3,6%

0,3% *

Female

1

2019-2025

-3,9%

-4,3%; -3,6%

Male

1

2010-2019

2,8%

2,6%; 3,0%

0,2% *

Male

1

2019-2025

-3,6%

-3,9%; -3,4%

West

Female

2

2010-2018

2,2%

2,0%; 2,4%

-1,0% *

Female

2

2018-2019

0,2%

-1,9%; 2,4%

Female

2

2019-2025

-5,6%

-6,0%; -5,3%

Male

1

2010-2019

1,9%

1,7%; 2,1%

-0,9% *

Male

1

2019-2025

-5,0%

-5,5%; -4,6%

* p < 0,05
JP: cantidad de joinpoints; APC: cambio porcentual anual; IC: intervalo de confianza al 95%; AAPC: cambio porcentual anual promedio (IC 95%).

Regression plots

Default plot

gg_jpoint(mod1)

Stack plots

gg_jpoint(mod1, facets = "none")
#> Number of columns will be ignored when facets 'none' or 'grid'.

Two grouping variables

gg_jpoint(mod3)

Split by region and sex

gg_jpoint(mod3, facets = "grid")
#> Number of columns will be ignored when facets 'none' or 'grid'.

Modify the size of data points

gg_jpoint(mod3, facets = "grid", psize = 5)
#> Number of columns will be ignored when facets 'none' or 'grid'.

Modify the transparency of data points

gg_jpoint(mod3, facets = "grid", ptr = 1)
#> Number of columns will be ignored when facets 'none' or 'grid'.

Hide data points

gg_jpoint(mod3, facets = "grid", obs = FALSE)
#> Number of columns will be ignored when facets 'none' or 'grid'.
#> Data points will not be displayed.

Hide joinpoint(s) marker(s)

gg_jpoint(mod3, facets = "grid", jp = FALSE)
#> Number of columns will be ignored when facets 'none' or 'grid'.
#> Joinpoint(s) position(s) will not be displayed.

Change the default colorblind-friendly palette

gg_jpoint(mod3, facets = "grid", jp = FALSE, cbpal = "blue_fluoride")
#> Number of columns will be ignored when facets 'none' or 'grid'.
#> Joinpoint(s) position(s) will not be displayed.

Disable colorblind-friendly palettes

gg_jpoint(mod3, facets = "grid", cb = FALSE)
#> Number of columns will be ignored when facets 'none' or 'grid'.
#> Colorblind-friendly palettes disabled, use `scale_color_`functions to set line and point colors.