Skip to main content
Have a personal or library account? Click to login
Q2q: An R Package for Interpolating Age-Specific Mortality Rates at All Ages Cover

Q2q: An R Package for Interpolating Age-Specific Mortality Rates at All Ages

By: Farid Flici  
Open Access
|Apr 2025

Full Article

(1) Overview

Introduction

A life-table depicts the evolution of survivorship for a hypothetical cohort (referred to as the radix of the table) from birth until extinction (or close). Typically, demographic life tables are built using 5-year age intervals. The age groups 0 and 1–4 are often exceptions to this rule due to the comparatively high mortality in the first years of life compared to the other ages.

The survivorship function in a life-table (lx) represents the number of people who remain alive at age x among l0. The evolution is based on the age pattern of mortality rates nQx, which represents the probability of dying between the ages x and x + n. For x = 0, 1, 5, 10, 15,.̇. p, we can write:

lx+n=lx*(1nQx)

A life-table may include death rates (nMx), which are the initial indicators calculated in a life-table. It is calculated by dividing the number of deaths in each age interval by the population at risk of death. Then, nMx are transformed into nQx utilizing conversion methods, such as the method of kimball [1]. Specific techniques are frequently required for the age group 0 and 1–4 years, as shown in Wilmoth et al. (2007) [2].

A complete life-table, also known as detailed life-table, presents the same information but with detailed ages from 0 to the closure age p. Interpolation is the technique by which an abridged description can be transformed into a detailed one. Multiple methods have been developed to interpolate demographic data at intermediate age points (e.g., survival, death rates) or to breakdown five-age interval data into detailed ages. Both operations are commonly known as ‘mortality interpolation’. Some proposed methods, such as Lagrange and Sprague, are well suited to interpolation (properly speaking), but others, such as Karup-King, allow for the breakdown of mortality data. In other words, rates are to be interpolated while probabilities and numbers are to be broken down.

The probability nQx is proportional to the length of the associated age interval and assigned to the beginning of the age interval. It can be written as this:

nQx=1[(1qx)*(1qx+1)*(1qx+2)*(1qx+3)*(1qx+4)]

With qx representing the age-specific mortality rate (ASMR) at age x.

In contrast, the death rate nMx is not proportional to the length of the age intervals and is assigned to the center of the age interval. A five-age death rate can be derived by taking the geometrical mean of the respective age-specific death rates (ASDRs). We could write:

1nMx=(mx*mx+1*mx+2*mx+3*mx+4)(1/5)

Thus, five-age rates and single-age rates are comparable. A full explanation of the distinction between mortality rates and death rates is available elsewhere [3].

Indeed, death rates correspond to the middle of the corresponding age interval. Therefore, the previous formula should be written as:

1nMx=(mx+0.5*mx+1.5*mx+2.5*mx+3.5*mx+4.5)(1/5)

When interpolating mx from nMx, we should begin by setting 1nMx=m1x+n2. The interpolation function is estimated by comparing the expected rates (m^x+n2) to the observed rates (those approximated from nMx) at the centers of the different age groups.

In the litterature, other methods were proposed to interpolate mortality rates at all ages. Some authors proposed to fit the five-age death rates nMx using the model of Heligman and Pollar [4]. Once the model is fitted on the center of the different age groups, i.e. 0.5, 3, 7.5, 12.5, …, p–2.5, the estimated parameters are used to estimate the age-specific death rates (ASDRs) at the intermediate ages, i.e. 0.5, 1.5, 2.5, …, P–0.5. In this sense, a significant contribution was made by Kostaki [5]. Others proposed to fit the lx curve at the 5-ages points using splines [6].

In this paper, we are not expected to compare the different interpolation methods from the performance point of view. Interested readers can refer to Kostaki et al. [7] for a comparison of interpolation methods.

Implementation and architecture

The ‘Q2q’ package is based on the junction of two different methods, one more adapted to juvenile and adult ages (i.e. the Lagrange method), and the other to adult and higher ages (i.e. the Karup-King method). First, we will explain how each of the two methods is implemented, and then how the junction is produced.

Implementation of Lagrange and Karup-King Interpolation Methods

The method of Lagrange

Here, ‘the method of Lagrange’ refers to the Lagrange Six Terms Interpolation Formula described by Siegel and collaborators [8]. Consider a life-table that provides five-age mortality rates nQx for x = 0, 1, 5, 10, …, p. The Lagrange method interpolates survivorship evolution at missing ages using three sets of coefficients, denoted as C1, C2, and C3. The coefficients C1 are used to interpolate mortality rates at the first age group, i.e., [1, 4]; C2 coefficients are used for the second age group, i.e., [5, 6, 7, 8, 9]; and C3 for the age groups older than 10.

R> C1

#> x=1 x=5 x=10 x=15 x=20 x=25

#> U2 0.562030 0.717600 -0.478400 0.283886 -0.100716 0.015600

#> U3 0.273392 1.047199 -0.531911 0.299200 -0.103747 0.015867

#> U4 0.096491 1.108800 -0.328533 0.172800 -0.058358 0.008800

R> C2

#> x=1 x=5 x=10 x=15 x=20 x=25

#> U6 -0.041667 0.7980 0.354667 -0.152000 0.048000 -0.007000

#> U7 -0.048872 0.5616 0.665600 -0.240686 0.072758 -0.010400

#> U8 -0.037281 0.3332 0.888533 -0.244800 0.070148 -0.009800

#> U9 -0.018379 0.1408 1.001244 -0.160914 0.043116 -0.005867

R> C3

#> x=5m-10 x=5m-5 x=5m x=5m+5 x=5m+10 x=5m+15

#> U(5m+1) 0.008064 -0.07392 0.88704 0.22176 -0.0492800 0.006336

#> U(5m+2) 0.011648 -0.09984 0.69888 0.46592 -0.0873600 0.010752

#> U(5m+3) 0.010752 -0.08736 0.46592 0.69888 -0.0998399 0.011648

#> U(5m+4) 0.006336 -0.04928 0.22176 0.88704 -0.0739200 0.008064

We define lx as the survival numbers at age x = 1, 5, 10, 15, 20, …, p. lx is usually provided as a component of abridged life tables; if not, it can be derived from nQx.

lx+n=lx*(1nQx)

For x = 2, 3, 4, the evolution of lx can be deduced from:

l2=0.562030*l1+0.717600*l50.478400*l10+0.283886*l150.100716*l20+0.015600*l25
l3=0.273392*l1+1.047199*l50.531911*l10+0.299200*l150.103747*l20+0.015867*l25
l4=0.096491*l1+1.108800*l50.328533*l10+0.172800*l150.058358*l20+0.008800*l25

The same procedure is used to interpolate lx between 5 and 9 years old using the coefficients C2.

The coefficients C3 are utilized for those over the age of 10. For example, to interpolate lx at ages 21, 22, 23, and 24, we use the following equation:

l21=0.008064*l100.07392*l15+0.88704*l20+0.22176*l250.0492800*l30+0.006336*l35
l24=0.006336*l100.04928*l15+0.22176*l20+0.88704*l250.0739200*l30+0.008064*l35

The Lagrange method allows interpolating mortality rates until 10 years before the closure age (p). For example, if a life-table is closed-out at the open age group [75 and older], which means that l75 is the last lx provided in the considered life table, the lx can only be interpolated until the age of 65 years old. The interpolated age-specific mortality rates (ASMRs), denoted qx, can be determined via the equation:

qx=(lxlx+1)lx

The Lagrange method’s main shortcoming is its inability to interpolate mortality data across all age groups. Some authors [9] suggested estimating mortality rates at missing ages by extending interpolated rates using the Gompertz model [10].

The method of Karup-King

The Karup-King method allows for the breakdown of five-age demographic counts, including exposure and death numbers, into a single age description. It is based on three groups of coefficients: k1, k2, and k3. Typically, each five-age quantity is divided into five detailed age quantities using three coefficients combined with the the quantities of the targeted age groups as well as its predecessor and successor five-age groups.

R> k1

#> x=5m x=5m+5 x=5m+10

#> V(5m) 0.344 -0.208 0.064

#> V(5m+1) 0.248 -0.056 0.008

#> V(5m+2) 0.176 0.048 -0.024

#> V(5m+3) 0.128 0.104 -0.032

#> V(5m+4) 0.104 0.112 -0.016

R> k2

#> x=5m-5 x=5m x=5m+5

#> V(5m) 0.064 0.152 -0.016

#> V(5m+1) 0.008 0.224 -0.032

#> V(5m+2) -0.024 0.248 -0.024

#> V(5m+3) -0.032 0.224 0.008

#> V(5m+4) -0.016 0.152 0.064

R> k3

#> x=5m-10 x=5m-5 x=5m

#> V(5m) -0.016 0.112 0.104

#> V(5m+1) -0.032 0.104 0.128

#> V(5m+2) -0.024 0.048 0.176

#> V(5m+3) 0.008 -0.056 0.248

#> V(5m+4) 0.064 -0.208 0.344

To break-out the death counts recorded in the age interval [x, x+5] years, and that we note D[x,x+5] into single ages counts dx, dx+1, dx+2, dx+3, dx+4, we use the coefficients k2 as follows:

dx=0.064*D[x5,x]+0.152*D[x,x+5]0.016*D[x+5,x+10]
dx+4=0.016*D[x5,x]+0.152*D[x,x+5]+0.064*D[x+5,x+10]

When there is no predecessor for the targeted age interval, the k1 coefficient group is employed. The single age quantities are determined as follows:

dx=0.344*D[x,x+5]0.208*D[x+5,x+10]+0.064*D[x+10,x+15]
dx+4=0.104*D[x,x+5]+0.112*D[x+5,x+10]0.016*D[x+10,x+15]

In contrast, for the final age interval, we apply k3 coefficients to the current age group and its two predecessors. We could write:

dx=0.016*D[x10,x5]+0.112*D[x5,x]+0.104*D[x,x+5]
dx+4=0.064*D[x10,x5]0.208*D[x5,x]+0.344*D[x,x+5]

When the first age interval in an abridged life table is 0, 1–4 instead of 0–4, the Karup-King method can only be employed starting from age 5. Siegel and Associates [8] provide a full overview of the Lagrange and Karup-King methods.

The Q2q Package

installation

It can be installed through CRAN repository by running:

R> install.packages(“Q2q”)

How does it work?

The package works following a two-steps process. First, the methods of Lagrange and Karup-King are independently implemented. Then, the two resulting curves are joined at the age corresponding to a minimal gap between the two curves.

If we assume a life-table providing mortality rates nQx for x going from 0 to 75 by 5 ages intervals except age 0 and the age group 1–4, the method of Lagrange allows interpolating the ASMRs from age 0 to 69 that we note qx+. The method of Karup-King allows obtaining the ASMRs from age 5 to age 79, that we note qx*. The junction point xj is selected in the age interval 10–60 in a way to minimize the sum of squared errors between the two curves qx+ and qx* in the surrounding ages. We can write:

minx=xj2xj+2(qx1*qx+)2

Considering the gap at 5 years around the junction age allow to better smooth out the junction effect.

The package functions

The package ‘Q2q’ provides two functions getqxt() and getqx().

getqxt(): Interpolating mortality surfaces

The function getqxt() interpolates age-specific mortality rates (ASMRs) from an abridged mortality surface. An abridged mortality surface should contain the five-age mortality rates, commonly noted as nQx,t, with x indicating the age, t the year, and n the length of the age group, which is specified to be 5 except for age 0 and the age group 1–4.

The general form of the function is getqxt(Qxt, nag, t), where Qxt is the matrix (or a numerical data frame) of the five ages mortality rates without a header or identification column. The number of rows in the matrix should be equal to the number of the age groups referred to as nag in the function. The number of years, referred to as t, should be equal to the number of columns in Qxt.

The function’s main outcome is qxt, a matrix of age-specific mortality rates for age (x) and year (t). This matrix was formed by combining two partial matrices, qxtl and qxtk, which were created using the Karup-King and Lagrange methods, respectively. Furthermore, these two matrices are part of the function outcomes. Additionally, a vector junct_ages representing the junction age for each year t is provided. The function also returns the theoretical death matrix dxt and the survivorship matrix lxt.

getqxt(): Interpolating annual life-tables

The getqx() function is a special case of getqxt() with t=1.

R> getqx <- function(Qx,nag) { getqxt(Qxt=Qx, nag, t=1) }

Examples

The dataset, LT, includes the Algerian mortality surface for males from 1977 to 2014, grouped into five age groups. For the first two examples, we use the LT dataset. First, we upload the data using the code below:

R> data(“LT”)

Example 1

Lets consider a case of an annual life table (Algeria, males, year 1995).

R> Ax <- LT[ , 18]

R> Ax

#> [1] 0.05680 0.00912 0.00503 0.00441 0.00702 0.01544

#> [7] 0.01613 0.01561 0.01711 0.02275 0.02411 0.03784

#> [13] 0.05324 0.09057 0.11883 0.19437 0.27827

The five-age mortality rates are plotted using the R base function plot().

R> plot(x=c(0,1, seq(5, (length(Ax) -2)*5, by=5)), y=log(Ax),

xlab=“Age”, ylab=“log(nQx)”, cex=1, type=“b”)

Figure 1 shows the curve of five-age mortality rates in log scale.

Figure 1

Mortality Rates (Algeria, males, 1995).

We interpolate the Age-Specific Mortality Rates qx,t using getqx().

R> library(Q2q)

R> interpolated_curve <-getqx(Qx=Ax, nag=17)

R> str(interpolated_curve)

The output from the previous code has the following structure:

#> List of 8

#> $ qx : num [1:80, 1] 0.0568 0.00311 0.00247 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:80] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ lx : num [1:81, 1] 10000 9432 9403 9379 9361 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:81] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ dx : num [1:80, 1] 568 29.4 23.3 18.5 14.9 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:80] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ qxk : num [1:80, 1] 0.02178 0.01634 0.01209 …

#> $ qxl : num [1:70, 1] 0.0568 0.00311 0.00247 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:70] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ junct_age: num 44

#> $ Qxt : num [1:17, 1] 0.0568 0.00912 0.00503 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:17] “0” “1” “5” “10” …

#> .. ..$ : NULL

#> $ Qxt_test : num [1:17, 1] 0.0568 0.00912 0.00503 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:17] “0” “1” “5” “10” …

#> .. ..$ : NULL

The interpolated age-specific mortality rates are plotted using plot().

R> plot(x=c(0:79), y=log(interpolated_curve$qx), type=“b”,

pch=19, xlab=“Age”, ylab=“log(qx)”, cex=0.75)

The interpolated mortality rate curve in Figure 2 was created by combining two curves produced by the Karup-King and Lagrange methods. Figure 3 shows how the two curves were joined together.

Figure 2

Interpolated Mortality Rates (Algeria, males, 1995).

Figure 3

Interpolated Mortality Rates (Algeria, males, 1995)-Junction age.

R> plot(x=c(5:79), y=log(interpolated_curve$qxk[6:80]), type=“l”, lwd=5,

pch=1, xlab=“Age”, ylab=“log(qx)”, xlim=c(0,80), col=“gray”)

R> lines(x=c(0:69), y=log(interpolated_curve$qxl), lwd=2)

R> lines(x=rep(interpolated_curve$junct_age, 2), lty=2, lwd=1.25, col=“red”,

y=c(min(log(interpolated_curve$qxl)),max(log(interpolated_curve$qxk))))

R> legend(x=2, y=-3, c(“Lagrange Method”, “Karup-King Method”, “Junction age”),

lty=c(1,1,2), col=c(“black”, “gray”, “red”), lwd=c(2,5,2))

Example 2

Lets consider a multi-annual mortality surface with dimensions of 17 (age groups) by 38 (calendar years), i.e., the Algerian mortality surface for males from 1977 to 2014. We refer to the data matrix as Bx,t:

R> Bxt <- LT

Lets look at the head and tail of the data matrix:

R> head(Bxt[ ,1:5 ])

#> X1977 X1978 X1979 X1980 X1981

#>0 0.12771 0.11457 0.10983 0.10701 0.10136

#>1 0.05713 0.04550 0.05025 0.05363 0.05049

#>5 0.01737 0.02004 0.01430 0.01090 0.01035

#>10 0.01219 0.01564 0.01006 0.00688 0.00638

#>15 0.01319 0.01602 0.01230 0.01025 0.00955

#>20 0.01533 0.01735 0.01615 0.01641 0.01543

R> tail(Bxt[ ,1:5 ])

#> X1977 X1978 X1979 X1980 X1981

#>50 0.05676 0.04749 0.05590 0.06150 0.05773

#>55 0.07900 0.08034 0.08122 0.08712 0.08292

#>60 0.11803 0.11414 0.11409 0.11920 0.11195

#>65 0.17858 0.15289 0.16691 0.16548 0.16143

#>70 0.27408 0.23679 0.22764 0.21209 0.19997

#>75 0.40217 0.35014 0.34326 0.33075 0.30638

The 3D surface representing log(nQxt) can be plotted using the persp3D() function of the plot3D package. To use this function, you need to install the package plot3D().

R> install.packages(“plot3D”)

Then, we run the following code:

R> library(plot3D)

R> par(mar=c(2,2,3,3))

R> persp3D(x=c(0,1, seq(5,75, by=5)), y=c(1977:2014), z=log(as.matrix(Bxt)),

phi = 30, theta = - 175, border = “black”, facets = TRUE, inttype = 1,

add = F, ticktype = “detailed”, expand = .6, scale=1.5, r=.6, box=TRUE,

xlab=“Age”, ylab=NULL, zlab=“log nQxt”, plot=F, axes=T)

R> text3D(x= 80, y=2010, z= 0.5, labels=c(“Year”), add = T, plot=F)

R> text3D(x= c(82, 80, 80, 76), y=c(1983, 2001, 2008, 2016),

z= c(-0.4,-0.5,-0.6,-0.4),labels=c(“1977”, “1995”, “2005”, “2014”),add=T)

The results are shown in Figure 4.

Figure 4

3D surface of five-ages mortality rates of Algerian males, 1977–2014.

We utilize the getqxt() function to obtain the detailed-age mortality surface.

R> interpolated_surface <- getqxt(Qxt=Bxt, nag=17, t=38)

The output is structured as follows:

R> str(interpolated_surface)

#> List of 8

#> $ qxt : num [1:80, 1:38] 0.12771 0.02187 0.01629 0.01181 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:80] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ lxt : num [1:81, 1:38] 10000 8723 8532 8393 8294 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:81] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ dxt : num [1:80, 1:38] 1277.1 190.8 139 99.1 69.4 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:80] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ qxtk : num [1:80, 1:38] 0.0587 0.046 0.0353 0.0276 …

#> $ qxtl : num [1:70, 1:38] 0.12771 0.02187 0.01629 0.01181…

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:70] “0” “1” “2” “3” …

#> .. ..$ : NULL

#> $ junct_ages: num [1, 1:38] 16 15 26 62 46 45 28 30 30 29 …

#> $ Qxt : num [1:17, 1:38] 0.1277 0.0571 0.0174 0.0122 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:17] “0” “1” “5” “10” …

#> .. ..$ : chr [1:38] “X1977” “X1978” “X1979” “X1980” …

#> $ Qxt_test : num [1:17, 1:38] 0.1277 0.0571 0.0174 0.0122 …

#> ..- attr(*, “dimnames”)=List of 2

#> .. ..$ : chr [1:17] “0” “1” “5” “10” …

#> .. ..$ : chr [1:38] “X1977” “X1978” “X1979” “X1980” …

The 3D surface of age-specific mortality rates can be visualized with the persp3D() function of the plot3D.

R> par(mar=c(2,2,3,3))

R> persp3D(x=c(0:79), y=c(1977:2014), z=log(interpolated_surface$qxt),

phi = 30, theta = - 175, border = “black”, facets = TRUE, inttype = 1, add = F,

ticktype = “detailed”, expand = .6, scale=1.5, r=.6, box=TRUE, xlab=“Age”,

ylab=NULL, zlab=“log nQxt”, plot=F, axes=T)

R> text3D(x= 80, y=2010, z= 0.5, labels=c(“Year”), add = T, plot=F)

R> text3D(x= c(84, 82, 82, 79), y=c(1983, 2001, 2008, 2016),

z= c(-1.5,-1.5,-1.6,-1.5), labels=c(“1977”, “1995”, “2005”, “2014”), add = T)

The obtained 3D surface is shown in Figure 5.

Figure 5

3D-Surface of single-ages mortality rates of Algerian males, 1977–2014.

Quality control

Unit testing

The Q2q package includes two unit tests created with the testthat package [11]. The two tests are designed to ensure the quality of the interpolation.

The first test, the fidelity test, is to examine if the five-age mortality rates recalculated from the interpolated rates, identified in the output of the package functions by Qxt_test, are identical to the initial five-age mortality rates. A tolerance gap of 0.005 is used.

R> test_that(“Fidelity test”, {

R> data(“LT”)

R> Qxt = LT

R> expect_equal(getqxt(Qxt, nag=nrow(Qxt), t=ncol(Qxt))$Qxt,

getqxt(Qxt, nag=nrow(Qxt), t=ncol(Qxt))$Qxt_test, tolerance=0.005)

R> Qx = LT[, 11]

R> expect_equal(getqx(Qx, nag=length(Qx))$Qxt,

getqx(Qx, nag=length(Qx))$Qxt_test,tolerance=0.005)

})

In some circumstances, if the evolution of initial mortality rates over time is irregular (as with crude rates), the interpolation method can result in interpolated age-specific mortality rates that are less than zero or greater than one. The second test, the zero-one test, is designed to ensure that all interpolated age-specific death rates fall between 0 and 1.

R> test_that(“zero-one test”, {

R> data(“LT”)

R> Qxt = LT

R> q = getqxt(Qxt, nag=nrow(Qxt), t=ncol(Qxt))$qxt

R> n = nrow(q)

R> t = ncol(q)

R> expect_true(all(q>0 & q<=1))

R> Qx = LT[, 11]

R> q = getqx(Qx, nag=length(Qx))$qx

R> n = nrow(q)

R> t = ncol(q)

R> expect_true(all(q>0 & q<=1))

})

The Q2q package produced favorable results for both tests, whether utilizing Algeria’s mortality surface from 1977 to 2014 or the annual life table of 1987.

Application to external data

In addition to the application of the Q2q package and its two functions to Algerian data using the provided dataset LT, as well as an external dataset. We utilize data from the Japanese Mortality Database https://www.ipss.go.jp to run examples 3 (annual life-table) and 4 (mortality surface).

Example 3: Application to a Japanese annual life-table

Lets consider the life table of Japanese women in 1950, which can be retrieved from the link https://www.ipss.go.jp/p-toukei/JMD/00/STATS/fltper_5x1.txt and denoted here by LT_3. It is required to install the package tidyverse to facilitate data manipulation.

R> library(tidyverse)

# Upload the dataset

R> LT_3 = read.csv(

url(“https://www.ipss.go.jp/p-toukei/JMD/00/STATS/fltper_5x1.txt”),

sep=””, header = F)

# Data manipulation & Preparation

R> LT_3 = LT_3[-c(1),]

R> colnames(LT_3) = LT_3[1,]

R> LT_3 = LT_3[-c(1),]

# Select only the variables of interest (Age and qx)

R> LT_3 = LT_3 %>% filter(LT_3$Year==1950) %>% select(c(2,4))

# Make sure the dataset is a numerical vector/matrix

R> m = 19

R> Qx = as.numeric(LT_3$qx[1:m])

Figure 6 shows the five-age mortality rates for Japanese females in 1950.

Figure 6

5-Age Mortality Rates (Japan, females, 1950).

We interpolate age-specific mortality rates using the function getqx().

R> qx = getqx(Qx, nag=m)

The interpolated age-specific mortality rates are shown in Figure 7.

Figure 7

Interpolated Age-Specific Mortality Rates (Japan, females, 1950).

Example 4: Application to a Japanese mortality surface

The second example of external data is based on Japanese abridged mortality rates for females from 1950–1970. The dataset is available at https://www.ipss.go.jp/p-toukei/JMD/00/STATS/fltper_5x1.txt and is referred to as LT_4 in our context. To simplify data manipulation, the package tidyverse should be installed.

R> library(tidyverse)

# Upload the dataset

R> LT_4 = read.csv(

url(“https://www.ipss.go.jp/p-toukei/JMD/00/STATS/fltper_5x1.txt”),

sep=””, header = F)

# Data preparation (Make sure the dataset provided is a numerical matrix)

R> LT_4_1 = LT_4[-c(1),]

R> colnames(LT_4_1) = LT_4_1[1,]

R> LT_4_1 = LT_4_1[-c(1),]

LT_4_1 = LT_4_1 %>% mutate(Age = recode(Age, “0”=0, “1-4”=1,“5-9”=5,

“10-14”=10, “15-19”=15, “20-24”=20, “25-29”=25, “30-34”=30, “35-39”=35,

“40-44”=40, “45-49”=45, “50-54”=50, “55-59”=55, “60-64”=60, “65-69”=65,

“70-74”=70, “75-79”=75, “80-84”=80, “85-89”=85, “90-94”=90, “95-99”=95,

“100-104”=100, “105-109”=105, “110+”=110))

R> LT_4_1 = as.data.frame(LT_4_1) %>% mutate(Age=as.numeric(Age),

Year = as.numeric(Year), qx=as.numeric(qx))

R> LT_4_2 = LT_4_1 %>% filter(Year %in% c(1950:1970),

Age %in% c(0:85)) %>% select(Age, Year, qx)

R> LT_4_2 = dcast(LT_4_2, Age~Year)

R> rownames(LT_4_2) = LT_4_2$Age

R> LT_4_2 = LT_4_2 %>% select(-c(Age))

R> LT_4_2 = as.matrix(LT_4_2)

R> m = 19

R> p=21

Figure 8 shows the 3D surface of five-age mortality rates of Japanese females from 1950 to 1970.

Figure 8

3D-Surface of Five-ages mortality rates of Japanese females, 1950–1970.

We interpolate age-specific mortality rates using the function getqxt().

R> LT_4_int = getqxt(LT_4_2, nag=19, t=21)

The interpolated age-specific mortality rates are shown in Figure 9.

Figure 9

3D-Surface of age-specific mortality rates of Japanese females, 1950–1970.

Result validation

The two unit tests were included in the package as routine checks to help users validate their findings.

If we consider the data from Japanese females in 1959, used in Example 3, we can validate the resulting age-specific mortality rates with the following code:

# Results Validation (1): Fidelity Test

R> test_that(“Fidelity test”, {

R> Qx = as.numeric(LT_3$qx[1:m])

R> expect_equal(getqx(Qx, nag=length(Qx))$Qxt,

getqx(Qx, nag=length(Qx))$Qxt_test, tolerance=0.005)

})

#> Test passed

# Results Validation (2): Zero-One Test

R> test_that(“zero-one test”, {

R> Qx = as.numeric(LT_3$qx[1:m])

R> q = getqx(Qx, nag=length(Qx))$qx

R> n = nrow(q)

R> t = ncol(q)

R> expect_true(all(q>0 & q<=1))

})

#> Test passed

Also, the results obtained in Example 4 were successfully tested with the two tests.

R> test_that(“Fidelity test”, {

R> Qxt = LT_4_2

R> expect_equal(getqxt(Qxt, nag=nrow(Qxt), t=ncol(Qxt))$Qxt,

getqxt(Qxt, nag=nrow(Qxt), t=ncol(Qxt))$Qxt_test, tolerance=0.005)

})

#> Test passed

R> test_that(“zero-one test”, {

R> Qxt = LT_4_2

R> q = getqxt(Qxt, nag=nrow(Qxt), t=ncol(Qxt))$qxt

R> n = nrow(q)

R> t = ncol(q)

R> expect_true(all(q>0 & q<=1))

})

#> Test passed

R CMD checks and CRAN package checks

The Q2q package passed all R CMD checks without errors, warnings, or notes. It also passed the CRAN package checks. Results can be accessed at https://cran.r-project.org/web/checks/check_results_Q2q.html.

Issues

Users can report any bugs with the package using this link: https://github.com/Farid-FLICI/Q2q/issues.

(2) Availability

Operating system

The Q2q package can be run on all operating systems supporting R (≥3.5.0).

Programming language

R (≥3.5.0)

Additional system requirements

NA

Dependencies

testthat (≥3.0.0)

List of contributors

Not Relevant

Software location

Archive

Name: CRAN

Persistent identifier: 10.32614/CRAN.package.Q2q

Licence: GPL (≥ 2.0)

Publisher: Farid FLICI

Version published: Version 0.1.1

Date published: 07/11/24

Code repository

Name: Github

Persistent identifier: https://github.com/Farid-FLICI/Q2q

Licence: GPL-2.0 license

Date published: 29/11/24

Language

English

(3) Reuse potential

The package can be used to interpolate age-specific mortality rates from an abridged life-table with an age structure of 0, 1, 5, 10, and so on. Despite this requirement, there are no constraints on gender, period, country, or population type (e.g., global population, insured population, social security population). It can also be applied to multiannual life tables (mortality surfaces). The package was evaluated on mortality data from Algeria for the years 1995 (example 1) and 1977–2014 (example 2), using the dataset included in the package. Q2q was also assessed using external data from the Japanese mortality database (https://www.ipss.go.jp/p-toukei/JMD/00/STATS/fltper_5x1.txt), based on annual (example 3) and multiannual (example 4) data.

Competing Interests

The author has no competing interests to declare.

DOI: https://doi.org/10.5334/jors.484 | Journal eISSN: 2049-9647
Language: English
Submitted on: Sep 27, 2023
Accepted on: Apr 13, 2025
Published on: Apr 24, 2025
Published by: Ubiquity Press
In partnership with: Paradigm Publishing Services
Publication frequency: 1 issue per year

© 2025 Farid Flici, published by Ubiquity Press
This work is licensed under the Creative Commons Attribution 4.0 License.