Estimate lifetime risk of developing or dying from cancer using ltRISK

Introduction

The lifetime risk of developing cancer is a fundamental measure in cancer surveillance and public health communication. It provides an intuitive estimate of the probability that an individual will be diagnosed with cancer during a specified remaining lifetime, conditional on being alive and cancer-free at the starting age. Related age-conditional risks are also useful for communicating cancer burden over clinically meaningful age ranges, such as from age 50 to 70 years.

Estimating lifetime cancer risk from population-based registry data is not always straightforward. Cancer incidence, cancer mortality, mortality from other causes, and the possibility of multiple primary cancers all affect the interpretation of the final estimate. Several methods have therefore been proposed for routine surveillance settings, including the Wun method (Wun et al. 1998), the DevCan method (Fay et al. 2003; Fay 2004), the adjusted for multiple primaries (AMP) method (Sasieni et al. 2011), and the traditional cumulative risk method.

The National Cancer Institute’s DevCan software is widely used for estimating the lifetime probability of developing or dying from cancer. However, open-source implementations that allow users to compare DevCan, AMP, Wun, and cumulative risk approaches in a single workflow are limited. The ltRISK package provides a lightweight and reproducible R interface for estimating lifetime and age-conditional risks of developing or dying from cancer using aggregated registry data.

Installation

You can install ltRISK from R-universe with:

install.packages(
  "ltRISK",
  repos = c("https://gigu003.r-universe.dev", "https://cloud.r-project.org")
)

You can install the development version from GitHub with:

# install.packages("devtools")
devtools::install_github("gigu003/ltRISK")

Methods

Overview of available approaches

The ltRISK package implements four approaches for estimating cancer risk. These methods differ in their assumptions, data requirements, and treatment of competing mortality.

Comparison of four methods for estimating lifetime cancer risk.
Feature DevCan Wun AMP Cumulative risk
Main software or implementation DevCan; ltRISK DevCan versions before 5.0; ltRISK ltRISK ltRISK
Incidence input typically expected First primary cancers First primary cancers All primary cancers or routinely published incidence Incidence or cancer mortality counts
Requires cancer incidence Yes Yes Yes Yes, for developing risk
Requires cancer mortality Yes Yes Yes Yes, for dying risk
Requires all-cause mortality Yes Yes Yes No
Accounts for competing mortality Yes Yes Yes No
Uses an arbitrary upper age limit No No No Often yes, for example age 74
Supports maj_method = "pmaj" Yes Yes Yes Yes

Which method should I use?

  • Use risk_func = "devcan" when incidence counts represent first primary cancers and you want estimates aligned with the DevCan current probability framework.
  • Use risk_func = "wun" for historical comparisons with earlier DevCan approaches or when explicitly applying the Wun et al. method.
  • Use risk_func = "amp" when incidence data may include multiple primary cancers and only aggregated registry data are available.
  • Use risk_func = "cumulative" in calc_ltr(), or use cumulative() with get_risk(), when you need the traditional cumulative risk over a fixed age range and do not want to account for competing mortality.

The package also supports two approaches for confidence intervals in calc_ltr(): a gamma-based method and a delta-method approximation.

For routine analyses, start with calc_ltr() when the age-specific vectors are already available and calc_ltr_df() when the inputs are columns in a data frame or must be analyzed by population, sex, site, period, or another grouping variable. The lower-level devcan(), amp(), wun(), and cumulative() functions expose each point-estimation model directly, while get_risk() extracts specified age ranges from those fitted model objects. This separation keeps the unified confidence-interval workflow in calc_ltr() without hiding the underlying model calculations.

Literature and implementation status

Several related approaches have been proposed for estimating lifetime or age-conditional cancer risk from registry data. The methods differ in their target estimand, treatment of competing mortality, and handling of multiple primary cancers. The table below summarizes how the main methodological papers map to the current ltRISK implementation.

Literature and implementation status of lifetime cancer risk methods.
Literature Main idea Status in ltRISK Main function(s) Notes
Bender et al. (1992) Standard person-years approach; interprets lifetime risk as a population cancer risk or expected number of cancers Not implemented as a separate estimator – Related to cumulative/person-year summaries, but the estimand differs from individual lifetime probability
Feuer et al. (1993) Multiple-decrement life-table application to breast cancer lifetime risk Covered indirectly wun(), devcan() Historical application and interpretation of current lifetime risk
Wun et al. (1998) Early DevCan-style age-conditional probabilities with cancer prevalence adjustment and optional surgery correction Implemented wun() Includes optional surgery correction through correct_for_surgery, H, and Ch
Fay et al. (2003) Current-probability estimator under a unified competing-risks framework, with gamma confidence intervals Implemented devcan(), calc_ltr() Core DevCan-style estimator for developing or dying from cancer
Fay (2004) Mid-age group joinpoint and piecewise approximation for age-specific rates Implemented as PMAJ approximation pmaj() Exact continuous MAJ is approximated by sub-intervals controlled by pmaj_sub_interval
Sasieni et al. (2011) Adjusted for multiple primaries (AMP) method for routinely published incidence Implemented amp() Suitable when incidence may include multiple primary cancers
Inoue et al. (2000) Traditional cumulative risk over the whole life span or fixed upper ages Implemented cumulative() Does not account for competing mortality
Zheng et al. (2023) Large-scale GLOBOCAN application using AMP for global, regional, and national lifetime probabilities Method covered amp(), calc_ltr_df() The included GCO_Today_2022 data support similar grouped workflows
Liu et al. (2023) DevCan-based lifetime probabilities of developing and dying from cancer over time Probability component covered devcan(), calc_ltr_df() Trend modeling such as Joinpoint is outside the current package scope

Overall, ltRISK currently implements the main probability estimators needed for routine registry-based lifetime risk analyses: Wun, DevCan/Fay, AMP, and traditional cumulative risk. Methods focused on trend modeling or alternative population-risk estimands can be combined with ltRISK outputs but are not implemented as separate core estimators.

Using ltRISK

Load the package

library(ltRISK)

Data preparation

The main functions in ltRISK use age-specific aggregated data. The required variables are:

  • ages: lower bounds of the age intervals, such as 0, 1, 5, 10, ..., 85.
  • cancer: number of incident cancer cases in each age interval. For DevCan and Wun, this usually represents first primary cancers. For AMP, routinely published incidence including multiple primary cancers may be used.
  • cancer_death: number of deaths from the cancer or cancer group under study.
  • death: number of deaths from all causes in each age interval.
  • pys: person-years at risk or the corresponding population denominator for each age interval.

All vectors should have the same length and should refer to the same population, calendar period, sex, cancer site, and age grouping. The ages vector should be sorted in ascending order.

Example datasets

The package includes two example datasets.

  • seer_example_data: data used to reproduce examples from DevCan-related publications, including breast cancer data from Fay et al. and all-sites SEER data embedded in DevCan.
  • GCO_Today_2022: world-total, both-sexes-combined data assembled from Global Cancer Observatory estimates and World Population Prospects 2022 population and all-cause mortality data, stratified by age and cancer site. Seven site-age cells retain missing source estimates; the all-cancers-excluding-NMSC series is complete.
data("seer_example_data")
breast <- seer_example_data[seer_example_data$site == "Breast", ]
head(seer_example_data)
#>     site    period sex ages cancer death cancer_death     pys
#> 1 Breast 1996-1998   2    0      0  5893            0 4052953
#> 2 Breast 1996-1998   2    5      0   561            0 4032790
#> 3 Breast 1996-1998   2   10      1   628            1 3784789
#> 4 Breast 1996-1998   2   15      9  1367            0 3810986
#> 5 Breast 1996-1998   2   20     43  1547            6 3675646
#> 6 Breast 1996-1998   2   25    335  2064           35 4138795
data("GCO_Today_2022")
head(GCO_Today_2022)
#>   site_code  icd10        site_abbr ages cancer cancer_death   death       pys
#> 1         1 C00-06 Lip, oral cavity    0    329          114 4715187 654355571
#> 2         1 C00-06 Lip, oral cavity    5    319          107  709923 682607098
#> 3         1 C00-06 Lip, oral cavity   10    455          145  457714 664762157
#> 4         1 C00-06 Lip, oral cavity   15    648          261  683239 627243354
#> 5         1 C00-06 Lip, oral cavity   20   1023          683  895411 601601216
#> 6         1 C00-06 Lip, oral cavity   25   3762         1527  981907 589721628

The example datasets included in this package contain first primary cancer counts.

Data-frame and tidyverse-style interface

The core function calc_ltr() takes five age-specific vectors: ages, cancer, cancer_death, death, and pys. This interface is useful when the vectors have already been extracted from a registry table. In many applied workflows, however, cancer registry data are stored as a data frame with one row per age group. For this situation, ltRISK provides calc_ltr_df(), a data-frame interface to the same calculations.

The following call gives the same type of result as calc_ltr(), but the input columns are selected directly from the data frame. Columns can be supplied as bare names, character strings, or character variables.

calc_ltr_df(
  breast,
  ages = ages,
  cancer = cancer,
  cancer_death = cancer_death,
  death = death,
  pys = pys,
  risk_func = "devcan",
  maj_method = "constant",
  ci_method = "gamma",
  age_start = c(0, 30, 50, 70),
  age_end = c(30, 50, 70, Inf),
  digits = 4
)
#>    start end    risk   lower   upper
#> 1      0  30  0.0470  0.0424  0.0519
#> 2      0  50  1.8995  1.8708  1.9286
#> 3      0  70  7.7861  7.7130  7.8598
#> 4      0 Inf 13.3198 13.2170 13.4235
#> 5     30  50  1.8817  1.8529  1.9108
#> 6     30  70  7.8609  7.7868  7.9355
#> 7     30 Inf 13.4816 13.3773 13.5868
#> 8     50  70  6.2505  6.1793  6.3224
#> 9     50 Inf 12.1264 12.0217 12.2320
#> 10    70 Inf  7.3149  7.2202  7.4109

This data-frame interface also works naturally with the base R pipe. For example, the breast cancer data can be filtered and passed directly to calc_ltr_df():

seer_example_data |>
  subset(site == "Breast") |>
  calc_ltr_df(
    ages = ages,
    cancer = cancer,
    cancer_death = cancer_death,
    death = death,
    pys = pys,
    risk_func = "devcan",
    maj_method = "constant",
    ci_method = "delta",
    age_start = c(0, 50, 70),
    age_end = Inf,
    digits = 4
  )
#>   start end    risk   lower   upper
#> 1     0 Inf 13.3198 13.2168 13.4228
#> 2    50 Inf 12.1264 12.0214 12.2313
#> 3    70 Inf  7.3149  7.2199  7.4100

For registry tables containing multiple populations, cancer sites, sexes, or regions, calc_ltr_df() can calculate risks separately within groups. The grouping variables are returned together with the risk estimates, producing a tidy result that can be used in downstream summaries, joins, or plots.

calc_ltr_df(
  seer_example_data,
  by = c("site", "sex"),
  ages = ages,
  cancer = cancer,
  cancer_death = cancer_death,
  death = death,
  pys = pys,
  risk_func = "devcan",
  maj_method = "constant",
  ci_method = "delta",
  age_start = 0,
  age_end = Inf,
  digits = 4
)
#>     site sex start end    risk   lower   upper
#> 1    All   0     0 Inf 39.1670 39.1072 39.2268
#> 2    All   1     0 Inf 39.4881 39.4018 39.5744
#> 3    All   2     0 Inf 38.9734 38.8899 39.0569
#> 4 Breast   2     0 Inf 13.3198 13.2168 13.4228

If dplyr is installed, grouped data frames are also supported. The grouping variables from dplyr::group_by() are detected automatically.

seer_example_data |>
  dplyr::group_by(site, sex) |>
  calc_ltr_df(
    ages = ages,
    cancer = cancer,
    cancer_death = cancer_death,
    death = death,
    pys = pys,
    risk_func = "devcan",
    maj_method = "constant",
    ci_method = "delta",
    age_start = 0,
    age_end = Inf,
    digits = 4
  )
#>     site sex start end    risk   lower   upper
#> 1    All   0     0 Inf 39.1670 39.1072 39.2268
#> 2    All   1     0 Inf 39.4881 39.4018 39.5744
#> 3    All   2     0 Inf 38.9734 38.8899 39.0569
#> 4 Breast   2     0 Inf 13.3198 13.2168 13.4228

Rate modeling and confidence intervals

Risk estimation in ltRISK has two independent configuration layers. maj_method controls how age-specific rates are represented and which age grid is used for point estimation. ci_method selects the confidence-interval construction, while variance_method controls how the underlying variance is calculated. Gamma and delta intervals can be combined with either constant or PMAJ rates. Wun models with surgery correction continue to use finite-difference variances because sampling variances and covariances for H and Ch are not specified by the current API.

PMAJ rate modeling

DevCan version 6.0 and later uses a piecewise mid-age group joinpoint (PMAJ) model (Fay 2004). In ltRISK, PMAJ is an age-specific rate-modeling option rather than a separate risk estimator. It is available for the DevCan, Wun, AMP, and cumulative-risk estimators by setting maj_method = "pmaj". The PMAJ step changes how rates are represented between reported age groups and the grid used for numerical integration; it does not change the definition of the selected risk estimator.

Wun retains maj_method = "constant" as its default so existing analyses continue to use the original Wun intervals. PMAJ must be requested explicitly for Wun. When surgery correction is enabled, its net surgery rate is represented on the same PMAJ grid; confidence intervals continue to use finite differences.

Availability of age-specific rate models by risk estimator.
Rate model DevCan Wun AMP Cumulative risk
Piecewise constant Yes Original Wun intervals Yes Yes
PMAJ Yes Yes Yes Yes

The following DevCan example uses PMAJ for both developing and dying risks.

# Extract all-sites cancer data for both sexes combined
all_sites <- seer_example_data[
  seer_example_data$site == "All" & seer_example_data$sex == 0,
]

# Estimate age-conditional and lifetime risks of developing cancer
calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "devcan",
  maj_method = "pmaj",
  age_start = c(0, seq(40, 90, 10)),
  age_end = Inf,
  digits = 4
)
#>   start end    risk   lower   upper
#> 1     0 Inf 38.9045 38.8450 38.9640
#> 2    40 Inf 39.1821 39.1203 39.2440
#> 3    50 Inf 38.3877 38.3241 38.4513
#> 4    60 Inf 36.1041 36.0367 36.1715
#> 5    70 Inf 30.3517 30.2772 30.4264
#> 6    80 Inf 20.9244 20.8408 21.0083
#> 7    90 Inf 11.5426 11.4408 11.6452

# Estimate age-conditional and lifetime risks of dying from cancer
calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "devcan",
  maj_method = "pmaj",
  type = "dying",
  age_start = c(0, seq(40, 90, 10)),
  age_end = Inf,
  digits = 4
)
#>   start end    risk   lower   upper
#> 1     0 Inf 17.2576 17.2195 17.2958
#> 2    40 Inf 17.7570 17.7175 17.7965
#> 3    50 Inf 17.8328 17.7925 17.8732
#> 4    60 Inf 17.5314 17.4895 17.5733
#> 5    70 Inf 16.1641 16.1192 16.2092
#> 6    80 Inf 13.2571 13.2067 13.3076
#> 7    90 Inf  9.0968  9.0317  9.1623

Using these example data, the estimated lifetime risk of developing cancer is approximately 38.9045%, and the estimated lifetime risk of dying from cancer is approximately 17.2576%.

The same interface applies PMAJ to the Wun, AMP, and cumulative-risk estimators. Wun and AMP can use an open-ended lifetime target, whereas traditional cumulative risk is normally reported to a finite upper age.

# Wun with PMAJ-smoothed event and mortality rates
calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "wun",
  maj_method = "pmaj",
  pmaj_sub_interval = 1,
  ci_method = "none",
  age_start = 0,
  age_end = Inf,
  digits = 4
)
#>   start end    risk
#> 1     0 Inf 38.7774

# AMP with PMAJ-smoothed incidence and removal rates
calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "amp",
  maj_method = "pmaj",
  pmaj_sub_interval = 1,
  ci_method = "none",
  age_start = 0,
  age_end = Inf,
  digits = 4
)
#>   start end   risk
#> 1     0 Inf 33.229

# Traditional cumulative risk with a PMAJ-smoothed event rate
calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "cumulative",
  maj_method = "pmaj",
  pmaj_sub_interval = 1,
  ci_method = "none",
  age_start = 0,
  age_end = 75,
  digits = 4
)
#>       start end    risk
#> [77,]     0  75 27.3233

Why interpolate age-specific rates?

Registry rates are commonly reported for age bands rather than for every single year of age. A piecewise-constant model assigns one rate to an entire band and changes it abruptly at the next boundary. Those jumps reflect the chosen grouping as well as the underlying age pattern. PMAJ instead linearly interpolates between the rates at adjacent age-group midpoints and represents the interpolated curve on short, piecewise-constant sub-intervals. This gives a more gradual age profile when the underlying incidence or mortality rate is expected to change continuously with age.

The following synthetic example makes the contrast visible. The observed 5-year rates are identical under both approaches; only the way rates are represented between their midpoints changes.

example_ages <- seq(40, 70, by = 5)
example_rates <- c(5, 8, 14, 24, 38, 55, 70) / 100000
age_width <- 5

fine_breaks <- seq(
  min(example_ages),
  max(example_ages) + age_width / 2,
  by = 0.5
)
fine_midages <- head(fine_breaks, -1) + diff(fine_breaks) / 2
fine_rates <- head(
  pmaj(
    ages = example_ages,
    rate_hat = example_rates,
    pmaj_sub_interval = 0.5
  ),
  -1
)

constant_breaks <- c(
  example_ages,
  max(example_ages) + age_width / 2
)
constant_rates <- c(example_rates, tail(example_rates, 1))

plot(
  constant_breaks,
  constant_rates * 100000,
  type = "s",
  lty = 2,
  lwd = 2,
  col = "grey45",
  xlab = "Age (years)",
  ylab = "Rate per 100,000"
)
lines(fine_midages, fine_rates * 100000, lwd = 2, col = "#0072B2")
points(
  example_ages + age_width / 2,
  example_rates * 100000,
  pch = 19
)
legend(
  "topleft",
  legend = c("Piecewise constant", "PMAJ", "Age-group rate"),
  lty = c(2, 1, NA),
  lwd = c(2, 2, NA),
  pch = c(NA, NA, 19),
  col = c("grey45", "#0072B2", "black"),
  bty = "n"
)
Piecewise-constant and PMAJ representations of the same grouped age-specific rates.

Piecewise-constant and PMAJ representations of the same grouped age-specific rates.

This interpolation can reduce artifacts caused by arbitrary age-group boundaries and gives the risk calculation a finer grid for numerical integration. It is especially useful when rates rise or fall rapidly across adjacent age bands or when risks are needed for endpoints inside an original band. PMAJ does not create new observations, however, and it does not by itself remove sampling noise from sparse age-specific rates. Decreasing pmaj_sub_interval improves the numerical approximation to the continuous MAJ calculation; it should not be interpreted as increasing the resolution of the source data. Smaller sub-intervals create more integration steps and therefore increase computation time, particularly when confidence intervals require many risk evaluations.

Because the PMAJ model evaluates age-specific rates on a finer age grid, it can also be used to estimate risks over age ranges whose endpoints are not original age-group boundaries. For example, if the input data are grouped in 5-year age bands, a piecewise-constant calculation is naturally tied to those 5-year boundaries. By contrast, maj_method = "pmaj" with pmaj_sub_interval = 1 creates an approximately 1-year grid, allowing endpoints such as age 78 or age 66. Smaller values of pmaj_sub_interval provide finer age resolution and more closely approximate the continuous mid-age group joinpoint calculation.

# Estimate risks for age ranges such as 0--78 and 40--66 years.
# age_start and age_end are crossed by calc_ltr(), so the rows below include
# all combinations of the supplied starts and ends.
calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "devcan",
  maj_method = "pmaj",
  pmaj_sub_interval = 1,
  ci_method = "delta",
  age_start = c(0, 40),
  age_end = c(78, 66),
  digits = 4
)
#>   start end    risk   lower   upper
#> 1     0  66 15.5343 15.5046 15.5641
#> 2     0  78 28.4377 28.3941 28.4812
#> 3    40  66 14.3424 14.3134 14.3714
#> 4    40  78 28.0513 28.0068 28.0957

The same endpoint behavior applies to DevCan, Wun, AMP, and cumulative risk. With maj_method = "constant", finite endpoints generally need to coincide with the original age-group boundaries. With PMAJ, endpoints must lie on the finer grid created by pmaj_sub_interval.

Confidence intervals

The calc_ltr() function reports point estimates together with confidence intervals for lifetime and age-conditional risks. The inference framework follows the approach described by Fay et al. (2003) for age-conditional probabilities of developing cancer. In that work, the risk estimator is treated as a smooth function of the observed age-specific counts, including incident cancer cases, cancer deaths, and deaths from other causes.

Let \(\hat{A}\) denote the estimated risk for a specified age range, and let \(\widehat{V}(\hat{A})\) denote its estimated variance. ltRISK propagates count variability with the delta method. By default, variance_method = "auto" uses analytic gradients for DevCan, AMP, Wun without surgery correction, and cumulative risk. These gradients avoid repeatedly refitting the risk model and are much faster for confidence-interval calculations. variance_method = "finite_difference" remains available as an independent implementation check. Wun calculations with surgery correction currently use finite differences because the API does not specify sampling variances or covariances for the surgery counts H and Ch.

Sampling assumptions and interpretation

The variance calculation represents the observed vectors as the count components \(c_i\), \(d_i\), and \(o_i\), where \(c_i\) is cancer incidence, \(d_i\) is cancer mortality, and \(o_i = death_i - d_i\) is other-cause mortality in age group \(i\). The default calculation assumes that these components are independent Poisson counts across components and age groups, with variances estimated by their observed counts. Person-years are treated as fixed denominators. Following the small-count convention used by Fay et al. (2003), zero count variances are replaced by 0.5 for the normal delta interval but not for the gamma interval.

These assumptions are most natural for non-overlapping registry strata and calendar periods. Results from overlapping populations, nested cancer sites, or repeated periods are generally correlated. ztest(), pairwise_ztest(), and trend_test() do not estimate that covariance and should not be interpreted as valid independent-group tests when such overlap is present. The estimators also describe a hypothetical cohort exposed throughout life to the supplied cross-sectional rates; they are not predictions for an observed birth cohort.

For DevCan developing risk, a central identifying assumption is that the other-cause mortality rate is the same among people who are cancer-free and in the population represented by the supplied rates (Fay et al. 2003). AMP additionally assumes that routinely published incidence can be adjusted through the specified competing-risk construction, whereas traditional cumulative risk does not account for competing mortality.

Gamma confidence intervals

The default option, ci_method = "gamma", uses a gamma-based confidence interval. Following Fay et al. (2003), the estimated risk is approximated by a gamma distribution whose mean is \(\hat{A}\) and whose variance is \(\widehat{V}(\hat{A})\). The shape and scale parameters are therefore

\[ \alpha_A = \frac{\hat{A}^2}{\widehat{V}(\hat{A})}, \qquad \theta_A = \frac{\widehat{V}(\hat{A})}{\hat{A}}. \]

The lower confidence limit is calculated from the lower tail of this gamma distribution. For the upper confidence limit, ltRISK follows the conservative strategy used in the Fay et al. framework by first identifying a locally maximal perturbed risk and then evaluating the corresponding gamma quantile. This helps maintain a non-negative confidence interval and is especially useful when the estimated risk is small or when a normal approximation may perform poorly.

For a two-sided \(100(1 - \alpha)\%\) confidence interval, the gamma method can be summarized as

\[ L = G^{-1}\left(\frac{\alpha}{2};\, \alpha_A, \theta_A\right), \qquad U = G^{-1}\left(1 - \frac{\alpha}{2};\, \alpha_M, \theta_M\right), \]

where \(G^{-1}(p; \alpha, \theta)\) is the \(p\)-th quantile of a gamma distribution with shape \(\alpha\) and scale \(\theta\). The parameters \(\alpha_M\) and \(\theta_M\) are computed from the locally maximal perturbed risk and its variance.

Delta-method confidence intervals

The alternative option, ci_method = "delta", uses a normal approximation based on the delta method. With estimated variance \(\widehat{V}(\hat{A})\), the confidence interval is

\[ \hat{A} \pm z_{1 - \alpha/2}\sqrt{\widehat{V}(\hat{A})}, \]

where \(z_{1 - \alpha/2}\) is the corresponding standard normal quantile. This interval is simple and useful for comparison, but because it is based on a symmetric normal approximation, it may produce a lower limit below zero when the estimated risk is small or the variance is large. For this reason, the gamma method is used as the default in calc_ltr().

Both methods are available for the risk estimators implemented in calc_ltr(). Users can select the confidence interval method with the ci_method argument:

calc_ltr(..., risk_func = "devcan", ci_method = "gamma")
calc_ltr(..., risk_func = "devcan", ci_method = "delta")
calc_ltr(..., risk_func = "amp", ci_method = "gamma")
calc_ltr(..., risk_func = "wun", ci_method = "gamma")
calc_ltr(..., risk_func = "cumulative", ci_method = "delta")

The gamma construction is non-negative, but it is still an approximation. Very sparse data, unidentified open-ended terminal intervals, or rate structures near parameter boundaries require particular care. Invalid non-finite local perturbations are excluded from the upper-limit maximization. For sensitivity analysis, compare gamma and delta intervals and, when practical, inspect a problem-specific parametric simulation.

Method 1: DevCan method

The DevCan method estimates the probability of developing or dying from cancer while accounting for competing mortality. In ltRISK, this method is selected with risk_func = "devcan".

The following example estimates the lifetime and age-conditional risks of developing breast cancer using the piecewise constant rate model and gamma confidence intervals.

# Extract breast cancer data from the example dataset
# The breast object was created above in the example data section.

# Estimate the risk of developing breast cancer
calc_ltr(
  ages = breast$ages,
  cancer = breast$cancer,
  cancer_death = breast$cancer_death,
  death = breast$death,
  pys = breast$pys,
  risk_func = "devcan",
  maj_method = "constant",
  ci_method = "gamma",
  age_start = c(0, 30, 50, 70),
  age_end = c(30, 50, 70, Inf),
  digits = 4
)
#>    start end    risk   lower   upper
#> 1      0  30  0.0470  0.0424  0.0519
#> 2      0  50  1.8995  1.8708  1.9286
#> 3      0  70  7.7861  7.7130  7.8598
#> 4      0 Inf 13.3198 13.2170 13.4235
#> 5     30  50  1.8817  1.8529  1.9108
#> 6     30  70  7.8609  7.7868  7.9355
#> 7     30 Inf 13.4816 13.3773 13.5868
#> 8     50  70  6.2505  6.1793  6.3224
#> 9     50 Inf 12.1264 12.0217 12.2320
#> 10    70 Inf  7.3149  7.2202  7.4109

The same estimates can be obtained with delta-method confidence intervals by setting ci_method = "delta".

calc_ltr(
  ages = breast$ages,
  cancer = breast$cancer,
  cancer_death = breast$cancer_death,
  death = breast$death,
  pys = breast$pys,
  risk_func = "devcan",
  maj_method = "constant",
  ci_method = "delta",
  age_start = c(0, 30, 50, 70),
  age_end = c(30, 50, 70, Inf),
  digits = 4
)
#>    start end    risk   lower   upper
#> 1      0  30  0.0470  0.0423  0.0517
#> 2      0  50  1.8995  1.8707  1.9284
#> 3      0  70  7.7861  7.7128  7.8594
#> 4      0 Inf 13.3198 13.2168 13.4228
#> 5     30  50  1.8817  1.8527  1.9106
#> 6     30  70  7.8609  7.7866  7.9351
#> 7     30 Inf 13.4816 13.3771 13.5861
#> 8     50  70  6.2505  6.1791  6.3220
#> 9     50 Inf 12.1264 12.0214 12.2313
#> 10    70 Inf  7.3149  7.2199  7.4100

For these data, the estimated lifetime risk of developing breast cancer from birth is 13.3198%, with a gamma 95% confidence interval of 13.2170% to 13.4235%. These values, together with the age ranges 30–50, 30–infinity, and 50–70, reproduce Fay et al. (2003) Table II at its published four-decimal precision. The package test suite stores the published values separately from package-generated output so this remains an external regression check.

To estimate the probability of dying from breast cancer rather than developing breast cancer, set type = "dying".

calc_ltr(
  ages = breast$ages,
  cancer = breast$cancer,
  cancer_death = breast$cancer_death,
  death = breast$death,
  pys = breast$pys,
  risk_func = "devcan",
  maj_method = "constant",
  ci_method = "gamma",
  type = "dying",
  age_start = c(0, 30, 50, 70),
  age_end = c(30, 50, 70, Inf),
  digits = 4
)
#>    start end   risk  lower  upper
#> 1      0  30 0.0051 0.0037 0.0069
#> 2      0  50 0.2901 0.2789 0.3016
#> 3      0  70 1.4657 1.4339 1.4981
#> 4      0 Inf 3.2027 3.1514 3.2548
#> 5     30  50 0.2893 0.2781 0.3009
#> 6     30  70 1.4830 1.4507 1.5158
#> 7     30 Inf 3.2465 3.1944 3.2994
#> 8     50  70 1.2276 1.1965 1.2593
#> 9     50 Inf 3.0413 2.9890 3.0945
#> 10    70 Inf 2.1361 2.0862 2.1870

Method 2: Wun method

Before DevCan version 5.0, the Wun method (Wun et al. 1998) was commonly used for lifetime and age-conditional cancer risk estimation. It remains useful for reproducing historical estimates and for methodological comparisons.

In ltRISK, use risk_func = "wun" to apply this method. The default maj_method = "constant" preserves the historical Wun calculation. Set maj_method = "pmaj" and optionally adjust pmaj_sub_interval to model the input rates on a finer grid. Additional Wun-specific arguments are documented in ?wun.

calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "wun",
  maj_method = "pmaj",
  pmaj_sub_interval = 1,
  age_start = c(0, seq(40, 90, 10)),
  age_end = Inf,
  digits = 4
)
#>   start end    risk   lower   upper
#> 1     0 Inf 38.7774 38.7184 38.8364
#> 2    40 Inf 39.0369 38.9757 39.0982
#> 3    50 Inf 38.2360 38.1731 38.2990
#> 4    60 Inf 35.9437 35.8771 36.0104
#> 5    70 Inf 30.2010 30.1274 30.2748
#> 6    80 Inf 20.8191 20.7365 20.9019
#> 7    90 Inf 11.5053 11.4040 11.6074

Method 3: AMP method

The adjusted for multiple primaries (AMP) method (Sasieni et al. 2011) is designed for settings where incidence data include multiple primary cancers. This situation commonly occurs when users work with aggregated data routinely published by cancer registries.

In ltRISK, use risk_func = "amp" to apply the AMP method. Additional AMP-specific arguments are documented in ?amp.

calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "amp",
  age_start = c(0, seq(40, 90, 10)),
  age_end = Inf,
  digits = 4
)
#>   start end    risk   lower   upper
#> 1     0 Inf 33.2386 33.1989 33.2784
#> 2    40 Inf 33.1746 33.1337 33.2156
#> 3    50 Inf 32.1052 32.0634 32.1470
#> 4    60 Inf 29.3778 29.3341 29.4215
#> 5    70 Inf 23.3479 23.3008 23.3951
#> 6    80 Inf 14.8545 14.8037 14.9055
#> 7    90 Inf  7.6128  7.5531  7.6729

Method 4: Traditional cumulative risk

Traditional cumulative risk is often reported over a fixed age range, such as from birth to age 74 years. Unlike DevCan, Wun, and AMP, this approach does not account for competing mortality. It therefore tends to overestimate risk at older ages, especially when mortality from other causes is substantial.

In ltRISK, cumulative risk can be calculated directly through calc_ltr() by setting risk_func = "cumulative". This provides the same point estimate as cumulative() followed by get_risk(), while also returning confidence intervals from the common delta-method variance framework used by calc_ltr().

calc_ltr(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  risk_func = "cumulative",
  type = "developing",
  maj_method = "constant",
  ci_method = "delta",
  age_start = 0,
  age_end = c(75, 85),
  digits = 4
)
#>   start end    risk   lower   upper
#> 1     0  75 31.9419 31.9007 31.9831
#> 2     0  85 42.4122 42.3572 42.4673

The lower-level workflow is also available through cumulative() and get_risk() when confidence intervals are not needed.

# Cumulative risk of developing cancer
cumu_developing <- cumulative(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  type = "developing",
  maj_method = "constant"
)

# Retrieve cumulative risk from birth to ages 75 and 85
get_risk(cumu_developing, age_start = 0, age_end = c(75, 85))
#> [1] 0.3194190 0.4241222

# Cumulative risk of dying from cancer
cumu_dying <- cumulative(
  ages = all_sites$ages,
  cancer = all_sites$cancer,
  cancer_death = all_sites$cancer_death,
  death = all_sites$death,
  pys = all_sites$pys,
  type = "dying",
  maj_method = "constant"
)

# Retrieve cumulative mortality risk from birth to ages 75 and 85
get_risk(cumu_dying, age_start = 0, age_end = c(75, 85))
#> [1] 0.1212217 0.2329601

Conclusion

The ltRISK package provides an open-source and reproducible framework for estimating lifetime and age-conditional risks of developing or dying from cancer. By implementing DevCan, Wun, AMP, and cumulative risk methods in a single R package, ltRISK allows researchers and cancer registries to select an estimator that matches their data structure and surveillance question. For data-frame and grouped workflows, calc_ltr_df() provides a convenient interface that integrates with base pipes and, optionally, with dplyr grouped data frames.

For data restricted to first primary cancers, the DevCan method is often appropriate. For aggregated incidence data that may include multiple primary cancers, the AMP method provides a practical alternative. The cumulative risk method remains useful for simple descriptive summaries over fixed age intervals, but its assumptions should be clearly communicated.

References

Bender, Alan P., Judy Punyko, Allan N. Williams, and Sally A. Bushhouse. 1992. “A Standard Person-Years Approach to Estimating Lifetime Cancer Risk.” Cancer Causes and Control 3 (1): 69–75. https://doi.org/10.1007/BF00051915.
Fay, Michael P. 2004. “Estimating Age Conditional Probability of Developing Disease from Surveillance Data.” Popul. Health Metr. 2 (1): 6.
Fay, Michael P., Ruth Pfeiffer, Kathleen A. Cronin, Chunfeng Le, and Eric J. Feuer. 2003. “Age-Conditional Probabilities of Developing Cancer.” Statistics in Medicine 22 (11): 1837–48. https://doi.org/10.1002/sim.1428.
Feuer, E. J., L. M. Wun, C. C. Boring, W. D. Flanders, M. J. Timmel, and T. Tong. 1993. “The Lifetime Risk of Developing Breast Cancer.” Journal of the National Cancer Institute 85 (11): 892–97. https://doi.org/10.1093/jnci/85.11.892.
Inoue, Manami, Kazuo Tajima, and Suketami Tominaga. 2000. “Probabilities of Developing Cancer over the Whole Life Span of a Japanese.” Asian Pacific Journal of Cancer Prevention 1 (4): 333–36.
Liu, JaiQi L., Shary Xinyu Zhang, Jean-Michel Billette, and Alain A. Demers. 2023. “Lifetime Probability of Developing Cancer and Dying from Cancer in Canada, 1997 to 2020.” Health Reports 34 (9): 14–21. https://doi.org/10.25318/82-003-x202300900002-eng.
Sasieni, P D, J Shelton, N Ormiston-Smith, C S Thomson, and P B Silcocks. 2011. “What Is the Lifetime Risk of Developing Cancer?: The Effect of Adjusting for Multiple Primaries.” Br. J. Cancer 105 (3): 460–65.
Wun, Lap-Ming, Ray M Merrill, and Eric J Feuer. 1998. “Estimating Lifetime and Age-Conditional Probabilities of Developing Cancer.” Lifetime Data Anal. 4 (2): 169–86.
Zheng, Rongshou, Shaoming Wang, Siwei Zhang, et al. 2023. “Global, Regional, and National Lifetime Probabilities of Developing Cancer in 2020.” Science Bulletin 68 (21): 2620–28. https://doi.org/10.1016/j.scib.2023.09.041.