This script assumes that you have already ran mgus_bm_prediction_20231125. This analysis has been completed in response to the revision request by the Annals of Internal Medicine, and will test two independent hypotheses: 1) the hypothesis that an ordinal regression containing total IgG, IgA and IgM modeled non-linearly using restricted cubic splines with four knots adds significant predictive value, compared to modeling these as linear variables. 2) the hypothesis that an ordinal regression containing all predictors as non-linear variables using restricted cubic splines with four knots adds significant predictive value, compared to modeling all predictors (except for FLC ratio) as linear variables. Evidence for this will be supplied by a comparison of the pre-test variance (linear) and post-test variance (non-linear), a likelihood ratio test and various measures of increased explained variance, after confirming that both models are well calibrated. # Total IgG, IgA and IgM as non-linear compared to linear variables
Our rationale for testing these two hypotheses separately is based on the prior literature and clinical experience. Total IgG, IgA and IgM have a much smaller evidence base for being predictive of smoldering multiple myeloma and multiple myeloma risk. In the prior literature, studies has included these variables in a very unusual and likely inefficient (and likely unjustifiable) statistical manor, by first dichotomizing whether total IgG, IgA and IgM were below the lower limit of normal, and then counting the number outside of this limit, other than the MGUS isotype, and evaluating this number as a risk factor. We therefore have the lowest a priori reason to believe that total IgG, IgA and IgM add significant predictive information, and accordingly, the lowest reason to believe that modeling them non-linearly should add predictive information compared to modeling them linearly. We First we fit the non-linear ‘full’ model
fit_pred <- lrm(outcome ~ rcs(igg, 4) + rcs(iga, 4) + rcs(igm, 4) + rcs(m_protein, 4) +
rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE)
As seen below the model likelihood ratio chi-square statistic is 401.11 on 18 degrees of freedom, with an R squared of 0.358 and a concordance index of 0.766.
fit_pred
## Logistic Regression Model
##
## lrm(formula = outcome ~ rcs(igg, 4) + rcs(iga, 4) + rcs(igm,
## 4) + rcs(m_protein, 4) + rcs(flc_ratio, 4) + isotype, data = df_pred,
## x = TRUE, y = TRUE)
##
##
## Frequencies of Responses
##
## 0-4% 5-9% 10-14% >15%
## 455 430 91 67
##
## Model Likelihood Discrimination Rank Discrim.
## Ratio Test Indexes Indexes
## Obs 1043 LR chi2 401.11 R2 0.358 C 0.766
## max |deriv| 4e-08 d.f. 18 R2(18,1043)0.307 Dxy 0.533
## Pr(> chi2) <0.0001 R2(18,882.4)0.352 gamma 0.533
## Brier 0.085 tau-a 0.335
##
## Coef S.E. Wald Z Pr(>|Z|)
## y>=5-9% 4.2561 0.8788 4.84 <0.0001
## y>=10-14% 1.5889 0.8686 1.83 0.0674
## y>=>15% 0.1966 0.8704 0.23 0.8213
## igg -0.3544 0.0937 -3.78 0.0002
## igg' 1.5930 0.4062 3.92 <0.0001
## igg'' -4.4209 1.1912 -3.71 0.0002
## iga -0.1330 0.2827 -0.47 0.6380
## iga' 2.7083 1.7328 1.56 0.1181
## iga'' -6.0916 3.9008 -1.56 0.1184
## igm 1.0890 0.7754 1.40 0.1602
## igm' -16.7238 8.5061 -1.97 0.0493
## igm'' 34.2726 17.1540 2.00 0.0457
## m_protein -0.6602 0.1832 -3.60 0.0003
## m_protein' 46.0506 11.3215 4.07 <0.0001
## m_protein'' -57.9203 14.3889 -4.03 <0.0001
## flc_ratio -2.1258 0.3555 -5.98 <0.0001
## flc_ratio' 38.5632 6.5415 5.90 <0.0001
## flc_ratio'' -90.7608 15.5093 -5.85 <0.0001
## isotype=IgA -0.2766 0.2610 -1.06 0.2892
## isotype=IgG -0.5350 0.2038 -2.63 0.0087
## isotype=Light-chain -0.5640 0.3257 -1.73 0.0833
##
Importantly for all further comparisons, both models must be shown to be equally well calibrated, as we would heavily favor a calibrated over a non-calibrated model. What constitutes ‘equally well calibrated’ is subjective, and is best judged by examining calibration plots for both models. The non-linear model is well calibrated.
plot(calibrate(fit_pred, B = 200))
##
## n=1043 Mean absolute error=0.013 Mean squared error=0.00019
## 0.9 Quantile of absolute error=0.02
Next we fit the linear model. The linear model is a nested model of the non-linear model, in which the coefficients of the restricted cubic spline terms for total IgG, IgA, and IgM are set to zero.
fit_pred_linear <- lrm(
formula = outcome ~ igg + iga + igm + rcs(m_protein, 4) +
rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE
)
As seen below the model likelihood ratio chi-square statistic is 378.19 on 12 degrees of freedom, with an R squared of 0.341 and a concordance index of 0.760.
fit_pred_linear
## Logistic Regression Model
##
## lrm(formula = outcome ~ igg + iga + igm + rcs(m_protein, 4) +
## rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE)
##
##
## Frequencies of Responses
##
## 0-4% 5-9% 10-14% >15%
## 455 430 91 67
##
## Model Likelihood Discrimination Rank Discrim.
## Ratio Test Indexes Indexes
## Obs 1043 LR chi2 378.19 R2 0.341 C 0.760
## max |deriv| 4e-08 d.f. 12 R2(12,1043)0.296 Dxy 0.519
## Pr(> chi2) <0.0001 R2(12,882.4)0.340 gamma 0.519
## Brier 0.086 tau-a 0.326
##
## Coef S.E. Wald Z Pr(>|Z|)
## y>=5-9% 1.4481 0.4232 3.42 0.0006
## y>=10-14% -1.1724 0.4231 -2.77 0.0056
## y>=>15% -2.5540 0.4380 -5.83 <0.0001
## igg 0.0130 0.0230 0.56 0.5722
## iga 0.2854 0.0451 6.32 <0.0001
## igm -0.1745 0.0464 -3.76 0.0002
## m_protein -0.6690 0.1797 -3.72 0.0002
## m_protein' 48.8852 10.8537 4.50 <0.0001
## m_protein'' -61.5660 13.7741 -4.47 <0.0001
## flc_ratio -2.1699 0.3533 -6.14 <0.0001
## flc_ratio' 39.8168 6.4875 6.14 <0.0001
## flc_ratio'' -93.8202 15.3791 -6.10 <0.0001
## isotype=IgA -0.1051 0.2525 -0.42 0.6771
## isotype=IgG -0.4976 0.2008 -2.48 0.0132
## isotype=Light-chain -0.4562 0.3211 -1.42 0.1554
##
Again, we must visually be satisfied that the calibration is equally as good as the non-linear model. We believe this is satisfied.
plot(calibrate(fit_pred_linear, B = 200))
##
## n=1043 Mean absolute error=0.016 Mean squared error=0.00032
## 0.9 Quantile of absolute error=0.027
Before we evaluate the consequences of modeling total IgG, IgA and IgM as linear or non-linear predictors with regards metrics important for prediction models (precision, explained variability, calibration, discrimination and decision curve analysis), we compare the effect sizes of the predictors for both models. It is important to reiterate that effect sizes are not important when developing a prediction model and should not weigh heavily in deciding whether or not to use the linear or non-linear model.
First we examine total IgG on the absolute predicted risk scale
Predict(fit_pred_linear,
kint = 2,
igg,
fun = plogis) %>%
select(igg, yhat, lower, upper) %>%
mutate(model = "Linear") %>%
union_all(
Predict(fit_pred,
kint = 2,
igg,
fun = plogis) %>%
select(igg, yhat, lower, upper) %>%
mutate(model = "Non-linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = igg, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = .5) +
coord_cartesian(ylim = c(0, NA)) +
geom_vline(xintercept = 6.1, lty = 2) +
geom_vline(xintercept = 15.7, lty = 2) +
scale_y_continuous(labels = scales::percent, limits = c(0, 0.14)) +
scale_x_continuous(limits = c(5, 22)) +
labs(x = "IgG (g/L)", y = "Proportion (%) having \n≥10% BMPC", title = "Total IgG") +
theme_bw() +
theme(legend.position = "bottom")
Then we examine total IgG on the odds ratio scale
Predict(fit_pred_linear,
kint = 2,
igg,
ref.zero = TRUE, fun = exp) %>%
select(igg, yhat, lower, upper) %>%
mutate(model = "Linear") %>%
union_all(
Predict(fit_pred,
kint = 2,
igg,
ref.zero = TRUE, fun = exp) %>%
select(igg, yhat, lower, upper) %>%
mutate(model = "Non-linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = igg, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = .5) +
geom_hline(yintercept = 1, lty = 2) +
geom_vline(xintercept = 6.1, lty = 2) +
geom_vline(xintercept = 15.7, lty = 2) +
scale_y_continuous(
breaks = c(0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 4, 8, 16, 32),
limits = c(NA, NA)) +
scale_x_continuous(limits = c(5, 22)) +
coord_trans(y = "log10") +
labs(x = "IgG (g/L)", y = "OR of 10% BMPC or more", title = "Total IgG") +
theme_bw() +
theme(legend.position = "bottom")
Now we examine IgA on the absolute predicted risk scale
Predict(fit_pred_linear,
kint = 2,
iga,
fun = plogis) %>%
select(iga, yhat, lower, upper) %>%
mutate(model = "Linear") %>%
union_all(
Predict(fit_pred,
kint = 2,
iga,
fun = plogis) %>%
select(iga, yhat, lower, upper) %>%
mutate(model = "Non-linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = iga, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = .5) +
coord_cartesian(ylim = c(0, NA)) +
geom_vline(xintercept = 0.4, lty = 2) +
geom_vline(xintercept = 2.3, lty = 2) +
scale_y_continuous(labels = scales::percent, limits = c(0, 0.15)) +
scale_x_continuous(limits = c(NA, 6)) +
labs(x = "IgA (g/L)", y = "Proportion (%) having \n≥10% BMPC", title = "Total IgA") +
theme_bw() +
theme(legend.position = "bottom")
Then we examine total IgA on the odds ratio scale
Predict(fit_pred_linear,
kint = 2,
iga,
ref.zero = TRUE, fun = exp) %>%
select(iga, yhat, lower, upper) %>%
mutate(model = "Linear") %>%
union_all(
Predict(fit_pred,
kint = 2,
iga,
ref.zero = TRUE, fun = exp) %>%
select(iga, yhat, lower, upper) %>%
mutate(model = "Non-linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = iga, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = .5) +
geom_hline(yintercept = 1, lty = 2) +
geom_vline(xintercept = 0.7, lty = 2) +
geom_vline(xintercept = 3.7, lty = 2) +
scale_y_continuous(breaks = c(0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 4, 8, 16, 32)) +
scale_x_continuous(limits = c(NA, 10)) +
coord_trans(y = "log10") +
labs(x = "IgA (g/L)", y = "OR of 10% BMPC or more", title = "Total IgA") +
theme_bw() +
theme(legend.position = "bottom")
Finally we examine IgM on the absolute predicted risk scale
Predict(fit_pred_linear,
kint = 2,
igm,
fun = plogis) %>%
select(igm, yhat, lower, upper) %>%
mutate(model = "Linear") %>%
union_all(
Predict(fit_pred,
kint = 2,
igm,
fun = plogis) %>%
select(igm, yhat, lower, upper) %>%
mutate(model = "Non-linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = igm, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = .5) +
coord_cartesian(ylim = c(0, NA)) +
geom_vline(xintercept = 0.4, lty = 2) +
geom_vline(xintercept = 2.3, lty = 2) +
scale_y_continuous(labels = scales::percent, limits = c(0, NA)) +
scale_x_continuous(limits = c(NA, 8)) +
labs(x = "IgM (g/L)", y = "Proportion (%) having \n≥10% BMPC", title = "Total IgM") +
theme_bw() +
theme(legend.position = "bottom")
and on the odds ratio scale
Predict(fit_pred_linear,
kint = 2,
igm,
ref.zero = TRUE, fun = exp) %>%
select(igm, yhat, lower, upper) %>%
mutate(model = "Linear") %>%
union_all(
Predict(fit_pred,
kint = 2,
igm,
ref.zero = TRUE, fun = exp) %>%
select(igm, yhat, lower, upper) %>%
mutate(model = "Non-linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = igm, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper), alpha = .5) +
geom_hline(yintercept = 1, lty = 2) +
geom_vline(xintercept = 0.4, lty = 2) +
geom_vline(xintercept = 2.3, lty = 2) +
scale_y_continuous(breaks = c(0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 4, 8, 16, 32)) +
scale_x_continuous(limits = c(NA, 8)) +
coord_trans(y = "log10") +
labs(x = "IgM (g/L)", y = "OR of 10% BMPC or more", title = "Total IgM") +
theme_bw() +
theme(legend.position = "bottom")
Because the linear model is nested within the non-linear model, we can use the likelihood ratio test to test the null hypothesis that linear and non-linear models fit the data equally well, and therefore that the non-linear terms for IgG, IgA and IgM are redundant. As seen below, there is fairly strong evidence against the null hypothesis, and we may conclude that the non-linear model fits the data better.
lrtest(fit_pred_linear, fit_pred)
##
## Model 1: outcome ~ igg + iga + igm + rcs(m_protein, 4) + rcs(flc_ratio,
## 4) + isotype
## Model 2: outcome ~ rcs(igg, 4) + rcs(iga, 4) + rcs(igm, 4) + rcs(m_protein,
## 4) + rcs(flc_ratio, 4) + isotype
##
## L.R. Chisq d.f. P
## 2.292153e+01 6.000000e+00 8.231908e-04
The degree to which the non-linear model fits the data better can be quantified by calculating the adequacy of the linear model compared to the non-linear model using the models’ likelihood ratio Chi-squared statistics.
pre_test_lr <- fit_pred_linear$stats["Model L.R."] # linear likelihood chi-squared statistic
post_test_lr <- fit_pred$stats["Model L.R."] # non-linear likelihood chi-suqared statistic
Below is a measure of the adequacy of the linear model compared to the non-linear model, 0.942 out of a maximum of 1
add_base <- pre_test_lr/post_test_lr
add_base
## Model L.R.
## 0.942855
In other words, the fraction of new information provided by non-linear model by this metric is 0.0571, which is substantial.
frac_new <- 1 - add_base
frac_new
## Model L.R.
## 0.05714496
The contribution of the non-linear components of total IgG, IgA and IgM can can be shown for each immunoglobulin individually, with the overall non-linear likelihood ratio Chi-squared statistic of 22.85 on 6 degrees of freedom equaling the overall likelihood ratio test above.
anova(fit_pred, igg, iga, igm)
## Wald Statistics Response: outcome
##
## Factor Chi-Square d.f. P
## igg 17.23 3 0.0006
## Nonlinear 16.82 2 0.0002
## iga 40.20 3 <.0001
## Nonlinear 2.45 2 0.2945
## igm 21.20 3 0.0001
## Nonlinear 4.67 2 0.0968
## TOTAL NONLINEAR 22.85 6 0.0008
## TOTAL 81.32 9 <.0001
The question remains whether the increased fit of the non-linear model to the data compared to the linear model is worthwhile, given its added complexity. Akaike’s information criterion provides a method for penalizing the log likelihood achieved by a given model for its complexity to obtain a more unbiased assessment of the model’s worth. The penalty is to add twice the number of parameters to the −2 log likelihood. On this metric, the model with the lower AIC should generally be favored. In this case, the non-linear model has a considerably lower AIC.
AIC(fit_pred)
## [1] 1969.557
AIC(fit_pred_linear)
## [1] 1980.479
Although not directly answering the question of whether we should prefer modelling immunoglobulins linearly compared to using restricted cubic slines, it should be noted that we did allow for penalized maximum likelihood estimation in the main manuscript, inculding an added penalty on non-linear terms. The full model had the lowest Akaike’s information criterion, supporting the above calculations suggesting that the non-linear model fits the data well.
penalty <- pentrace(
fit_pred,
list(
simple = seq(0, 1, by = .1),
nonlinear = seq(0, 1, by = .1)
)
)
penalty
##
## Best penalty:
##
## simple nonlinear df
## 0 0 18
##
## simple nonlinear df aic bic aic.c
## 0.0 0.0 18.00000 365.11201 276.014592 364.44404
## 0.0 0.1 14.32334 75.69983 4.801354 75.27269
## 0.1 0.1 14.25522 75.87344 5.312146 75.45025
## 0.0 0.2 13.94986 73.95926 4.909454 73.55354
## 0.1 0.2 13.88750 74.12680 5.385684 73.72461
## 0.2 0.2 13.82737 74.28682 5.843318 73.88801
## 0.0 0.3 13.73193 72.89448 4.923418 72.50100
## 0.1 0.3 13.67250 73.05411 5.377168 72.66394
## 0.2 0.3 13.61513 73.20676 5.813809 72.81977
## 0.3 0.3 13.55966 73.35300 6.234615 72.96907
## 0.0 0.4 13.58051 72.14550 4.923898 71.76042
## 0.1 0.4 13.52307 72.29755 5.360291 71.91563
## 0.2 0.4 13.46755 72.44305 5.780624 72.06418
## 0.3 0.4 13.41382 72.58253 6.186068 72.20659
## 0.4 0.4 13.36176 72.71644 6.577668 72.34333
## 0.0 0.5 13.46558 71.58355 4.930884 71.20478
## 0.1 0.5 13.40965 71.72867 5.352852 71.35296
## 0.2 0.5 13.35554 71.86761 5.759601 71.49484
## 0.3 0.5 13.30314 72.00085 6.152216 71.63091
## 0.4 0.5 13.25233 72.12881 6.531670 71.76162
## 0.5 0.5 13.20302 72.25189 6.898837 71.88734
## 0.0 0.6 13.37331 71.14496 4.948989 70.77122
## 0.1 0.6 13.31864 71.28379 5.358456 70.91302
## 0.2 0.6 13.26571 71.41676 5.753419 71.04884
## 0.3 0.6 13.21441 71.54430 6.134892 71.17914
## 0.4 0.6 13.16463 71.66683 6.503786 71.30433
## 0.5 0.6 13.11629 71.78468 6.860923 71.42477
## 0.6 0.6 13.06930 71.89820 7.207042 71.54079
## 0.0 0.7 13.29631 70.79314 4.978331 70.42357
## 0.1 0.7 13.24272 70.92624 5.376673 70.55956
## 0.2 0.7 13.19081 71.05375 5.761140 70.68986
## 0.3 0.7 13.14046 71.17610 6.132686 70.81490
## 0.4 0.7 13.09159 71.29365 6.492169 70.93505
## 0.5 0.7 13.04409 71.40674 6.840360 71.05067
## 0.6 0.7 12.99789 71.51567 7.177958 71.16204
## 0.7 0.7 12.95292 71.62071 7.505600 71.26945
## 0.0 0.8 13.23016 70.50507 5.017681 70.13906
## 0.1 0.8 13.17755 70.63292 5.405941 70.26973
## 0.2 0.8 13.12655 70.75543 5.780896 70.39498
## 0.3 0.8 13.07706 70.87301 6.143443 70.51519
## 0.4 0.8 13.02899 70.98600 6.494389 70.63072
## 0.5 0.8 12.98224 71.09471 6.834466 70.74191
## 0.6 0.8 12.93676 71.19943 7.164335 70.84902
## 0.7 0.8 12.89246 71.30041 7.484598 70.95233
## 0.8 0.8 12.84928 71.39789 7.795803 71.05207
## 0.0 0.9 13.17207 70.26534 5.065509 69.90245
## 0.1 0.9 13.12035 70.38835 5.444518 70.02822
## 0.2 0.9 13.07018 70.50626 5.810741 70.14881
## 0.3 0.9 13.02147 70.61945 6.165025 70.26457
## 0.4 0.9 12.97413 70.72823 6.508133 70.37585
## 0.5 0.9 12.92808 70.83291 6.840758 70.48295
## 0.6 0.9 12.88325 70.93375 7.163527 70.58615
## 0.7 0.9 12.83956 71.03099 7.477012 70.68567
## 0.8 0.9 12.79696 71.12485 7.781735 70.78176
## 0.9 0.9 12.75539 71.21554 8.078175 70.87460
## 0.0 1.0 13.12015 70.06318 5.120345 69.70307
## 0.1 1.0 13.06925 70.18172 5.490784 69.82431
## 0.2 1.0 13.01986 70.29537 5.848917 69.94058
## 0.3 1.0 12.97188 70.40449 6.195543 70.05223
## 0.4 1.0 12.92522 70.50938 6.531387 70.15957
## 0.5 1.0 12.87981 70.61032 6.857104 70.26290
## 0.6 1.0 12.83558 70.70756 7.173291 70.36245
## 0.7 1.0 12.79246 70.80134 7.480493 70.45848
## 0.8 1.0 12.75040 70.89186 7.779206 70.55118
## 0.9 1.0 12.70934 70.97931 8.069886 70.64075
## 1.0 1.0 12.66924 71.06386 8.352954 70.72737
Next we compare the variances of the predicted risks from the linear and non-linear models. In the prediction setting, increased variance in risk estimates is generally desirable. As shown below, the variance is almost identical between the linear and non-linear models and the high-resolution histogram of predicted risks is visually identical.
pre <- predict(fit_pred_linear, type='fitted')
pre <- pre[,2]
post <- predict(fit_pred, type='fitted')
post <- post[,2]
par(mgp=c(4,1,0), mar=c(6,5,2,2))
xlab <- c(
paste0("Linear\nvariance =", round(var(pre), 3)),
paste0("Non-linear\nvariance=",round(var(post), 3))
)
histbackback(pre, post, brks=seq(0, 1, by=0.005), xlab=xlab, ylab= "Proportion (%) having \n≥10% BMPC")
We show that the relative explained variation of predicted risk of the linear model compared tothe non-linear model is 0.974 (theoretically out of a maximum of 1)
var_pre <- round(var(pre), 3) #linear
var_post <- round(var(post), 3) #non-linear
relativ_expl <- var_pre/var_post
relativ_expl
## [1] 0.974359
In other words, the fraction of new information contained in the non-linear model by this metric is only 0.0256
frac_new2 <- 1 - relativ_expl
frac_new2
## [1] 0.02564103
We can show that the predicted risks between the linear and non-linear models differ most at moderate predicted risks.
plot(pre, post-pre, xlab='Predicted risk (linear immunoglobulins)',
ylab='Difference in predicted risks (non-linear - linear)', pch=46)
lo <- Rq(post-pre ~ rcs(pre, 7), tau=0.1) # 0.1 quantile
hi <- Rq(post-pre ~ rcs(pre, 7), tau=0.9) # 0.9 quantile
at <- seq(0, 1, length=200)
lines(at, Predict(lo, pre=at)$yhat, col='red', lwd=1.5)
lines(at, Predict(hi, pre=at)$yhat, col='red', lwd=1.5)
abline(h=0, col='red', lty = 2)
Finally, as this is a prediction model that we intend for clinical use, we should be satisfied that the decision curves associated with both models are similar. If not, we should heavily favor the model that leads to increased net-benefit over plausible risk thresholds
df_linear_nonlinear <- df_pred # temporary dataframe only used for this script
Function(fit_pred) # we extract the linear predictor from the non-linear model first
## function(igg = 10.21,iga = 2.167,igm = 0.718,m_protein = 1.91,flc_ratio = 1.237794,isotype = "IgG") {-0.35437745* igg+0.012363328*pmax(igg-6.111,0)^3-0.034311919*pmax(igg-9.2,0)^3+0.023794703*pmax(igg-11.446,0)^3-0.0018461125*pmax(igg-17.462,0)^3-0.13301362* iga+0.098986509*pmax(iga-0.5914,0)^3-0.22264315*pmax(iga-1.641,0)^3+0.13057034*pmax(iga-2.6581,0)^3-0.006913695*pmax(iga-5.8221,0)^3+1.0889587* igm-3.1076573*pmax(igm-0.2561,0)^3+6.3686218*pmax(igm-0.5751,0)^3-3.3346563*pmax(igm-0.9166,0)^3+0.073691742*pmax(igm-2.5759,0)^3-0.66018976* m_protein+0.44395199*pmax(m_protein-0.481261,0)^3-0.55838218*pmax(m_protein-0.881164,0)^3+0.11511927*pmax(m_protein-2.48194,0)^3-0.00068906911*pmax(m_protein-10.665996,0)^3-2.1257973* flc_ratio+0.73647737*pmax(flc_ratio-0.2041965,0)^3-1.7333446*pmax(flc_ratio-1.0186109,0)^3+1.0005139*pmax(flc_ratio-1.6415064,0)^3-0.0036466553*pmax(flc_ratio-7.440332,0)^3-0.27663453*(isotype=="IgA")-0.53499524*(isotype=="IgG")-0.56401632*(isotype=="Light-chain") }
## <environment: 0x7fea1100d698>
df_linear_nonlinear$nonlinear_pred <- with(
df_linear_nonlinear,
expit(
1.5888896 -0.35437745 * igg + 0.012363328 * pmax(igg - 6.111, 0) ^ 3 - 0.034311919 *
pmax(igg - 9.2, 0) ^ 3 + 0.023794703 * pmax(igg - 11.446, 0) ^ 3 - 0.0018461125 *
pmax(igg - 17.462, 0) ^ 3 - 0.13301362 * iga + 0.098986509 * pmax(iga - 0.5914, 0) ^ 3
- 0.22264315 * pmax(iga - 1.641, 0) ^ 3 + 0.13057034 * pmax(iga - 2.6581, 0) ^ 3
- 0.006913695 * pmax(iga - 5.8221, 0) ^ 3 + 1.0889587 * igm -
3.1076573 * pmax(igm - 0.2561, 0) ^ 3 + 6.3686218 * pmax(igm - 0.5751, 0) ^
3 - 3.3346563 * pmax(igm - 0.9166, 0) ^ 3 + 0.073691742 * pmax(igm - 2.5759, 0) ^
3 - 0.66018976 * m_protein + 0.44395199 * pmax(m_protein - 0.481261, 0) ^
3 - 0.55838218 * pmax(m_protein - 0.881164, 0) ^ 3 + 0.11511927 * pmax(m_protein - 2.48194, 0) ^ 3
- 0.00068906911 * pmax(m_protein - 10.665996, 0) ^ 3 - 2.1257973 * flc_ratio + 0.73647737 *
pmax(flc_ratio - 0.2041965, 0) ^ 3 - 1.7333446 * pmax(flc_ratio - 1.0186109, 0) ^ 3
+ 1.0005139 * pmax(flc_ratio - 1.6415064, 0) ^ 3 - 0.0036466553 *
pmax(flc_ratio - 7.440332, 0) ^ 3 - 0.27663453 * (isotype == "IgA") - 0.53499524 *
(isotype == "IgG") - 0.56401632 * (isotype == "Light-chain")
)
)
Function(fit_pred_linear) # next we extract the linear predictor from the linear model
## function(igg = 10.21,iga = 2.167,igm = 0.718,m_protein = 1.91,flc_ratio = 1.237794,isotype = "IgG") {+0.012987234*igg+0.28536845*iga-0.17450545*igm-0.66897437* m_protein+0.47127836*pmax(m_protein-0.481261,0)^3-0.59352797*pmax(m_protein-0.881164,0)^3+0.12313286*pmax(m_protein-2.48194,0)^3-0.00088325512*pmax(m_protein-10.665996,0)^3-2.1699146* flc_ratio+0.76041857*pmax(flc_ratio-0.2041965,0)^3-1.7917724*pmax(flc_ratio-1.0186109,0)^3+1.0353426*pmax(flc_ratio-1.6415064,0)^3-0.0039887145*pmax(flc_ratio-7.440332,0)^3-0.10513226*(isotype=="IgA")-0.49763544*(isotype=="IgG")-0.45621863*(isotype=="Light-chain") }
## <environment: 0x7fe9f0263738>
df_linear_nonlinear$linear_pred <- with(
df_linear_nonlinear,
expit(
-1.17235498 +0.012987234 * igg + 0.28536845 * iga - 0.17450545 * igm - 0.66897437 * m_protein +
0.47127836 * pmax(m_protein - 0.481261, 0) ^ 3 - 0.59352797 * pmax(m_protein - 0.881164, 0) ^ 3
+ 0.12313286 * pmax(m_protein - 2.48194, 0) ^ 3 - 0.00088325512 *
pmax(m_protein - 10.665996, 0) ^ 3 - 2.1699146 * flc_ratio + 0.76041857 *
pmax(flc_ratio - 0.2041965, 0) ^ 3 - 1.7917724 * pmax(flc_ratio - 1.0186109, 0) ^
3 + 1.0353426 * pmax(flc_ratio - 1.6415064, 0) ^ 3 - 0.0039887145 * pmax(flc_ratio - 7.440332, 0) ^ 3
- 0.10513226 * (isotype == "IgA") - 0.49763544 * (isotype == "IgG")
- 0.45621863 * (isotype == "Light-chain") )
)
We create the decision curve analysis models using the dca pacakge. We examine the net-benefit of the linear and non-linear models in a opt-out strategy, in which net benefit is expressed in the relative proportion of true negatives. As demonstrated by the resulting decision curve analysis figure, there is no perceivable difference in net benefit between the two models in this setting.
nonlinear_model <- decision_curve(perc_10 ~ nonlinear_pred,
data = df_linear_nonlinear,
policy = 'opt-out',
fitted = TRUE,
thresholds = seq(0, 1, by = .001),
bootstraps = 1000)
linear_model <- decision_curve(perc_10 ~ linear_pred,
data = df_linear_nonlinear,
policy = 'opt-out',
fitted = TRUE,
thresholds = seq(0, 1, by = .001),
bootstraps = 1000)
nonlinear_model$derived.data %>%
select(thresholds, NB, NB_lower, NB_upper, model) %>%
union_all(
linear_model$derived.data %>%
select(thresholds, NB, NB_lower, NB_upper, model) %>%
filter(model == "perc_10 ~ linear_pred")
) %>%
mutate(model = factor(model, levels = c("perc_10 ~ nonlinear_pred", "perc_10 ~ linear_pred", "All", "None"))) %>%
ggplot(aes(x = thresholds, y = NB, color = model, fill = model)) +
geom_line(size = 1.1, position = position_dodge(width = 0.005)) +
geom_vline(aes(xintercept = 0.05), lty = 2) +
geom_vline(aes(xintercept = 0.10), lty = 2) +
scale_color_manual(
name = "Follow-up strategy:",
labels = c(
"Defer based on non-linear model",
"Defer based on linear model",
"Obtain sample from all",
"Obtain sample from none"
),
values = c(nejm_palette[c(2, 3)], "black", nejm_palette[c(1)])
) +
scale_x_continuous(breaks = seq(0, 1, by = 0.02), labels = scales::percent_format(accuracy = 1)) +
coord_cartesian(xlim = c(0, 0.25), ylim = c(-0.2, 0.7)) +
labs(x = NULL, y = "Net Benefit") +
theme_bw() +
theme(
legend.title = element_blank(),
legend.background = element_rect(fill = NA),
panel.grid.minor.x = element_blank(),
legend.position = c(0.2, 0.82)
)
We have shown that the model that includes immunoglobulins as non-linear variables i) fits the data significantly better on the likelihood ratio test, providing roughly a 5% increase in the fraction of new information on this metric compared to the linear model. ii) has a lower Akaike’s information criterion than does the linear model. iii) slightly increases the variance of predicted risk, providing a roughly 2.5% increase in the fraction of new information on this metric compared to the linear model.
Arguments for the removal of the non-linear components of the total immunoglobulins include the principle of parsimony and the general observation that simpler models tend to perform better on external validation (bias-variance trade off). This is also supported by the linear and non-linear models performing equally well on decision curve analysis.
Arguments against the removal of the non-linear components include the fact that they were pre-specified and only considered for removal after testing (which may introduce testimation bias), plausibility (the fact that natural relationships are almost certainly never linear), and finally the rationale provided by the statistical exploration shown in this document. Added to this is the fact that modelling total immunoglobulins as non-linear functions does not add any cost to the clinician or patient (does not increased the number of measured variables), and the added complexity is not relevant given that a clinical calculator will be provided.
After taking into account the comments by the editorial team and the statistical reviewer Dr. Rita Popat, including meeting with Dr. Wee and Dr. Popat, we are willing to remove the non-linear components of the total IgG, IgA and IgM. # All variables modeled as non-linear compared to linear variables (except FLC ratio)
M protein is a strong predictor (if not the strongest predictor) of progression from MGUS to SMM or MM. We therefore consider this separately as any misspecification of M protein will likely have drastic consequences for the predictive ability of the model. From the above chapter we have made the decision, after strong recommendations from Drs. Wee and Popat, to model total IgG, IgA and IgM as linear variables. We will therefore use this as the baseline. We will explore modeling M protein as a five- four- and three-knot restricted cubic spline and compare the AIC of these as recommended by Dr. Harrell’s Regression Modeling Strategies. We will only deviate from the four-knot restricted cubic spline if there is substantial evidence of increased predictive power, as the four-knot restricted cubic spline was pre-specified. It is self evident from the prior literature and clinical experience that the relationship between FLC ratio and the outcome will be highly non-linear. We therefore do not evaluate modeling this as a linear relationship.
fit_pred_mprotein_5 <- lrm(
formula = outcome ~ igg + iga + igm + rcs(m_protein, 5) +
rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE
)
fit_pred_mprotein_5
## Logistic Regression Model
##
## lrm(formula = outcome ~ igg + iga + igm + rcs(m_protein, 5) +
## rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE)
##
##
## Frequencies of Responses
##
## 0-4% 5-9% 10-14% >15%
## 455 430 91 67
##
## Model Likelihood Discrimination Rank Discrim.
## Ratio Test Indexes Indexes
## Obs 1043 LR chi2 378.46 R2 0.341 C 0.760
## max |deriv| 1e-07 d.f. 13 R2(13,1043)0.296 Dxy 0.520
## Pr(> chi2) <0.0001 R2(13,882.4)0.339 gamma 0.520
## Brier 0.086 tau-a 0.327
##
## Coef S.E. Wald Z Pr(>|Z|)
## y>=5-9% 1.4741 0.4269 3.45 0.0006
## y>=10-14% -1.1455 0.4271 -2.68 0.0073
## y>=>15% -2.5262 0.4422 -5.71 <0.0001
## igg 0.0121 0.0231 0.53 0.5995
## iga 0.2840 0.0452 6.28 <0.0001
## igm -0.1751 0.0465 -3.77 0.0002
## m_protein -0.7752 0.2701 -2.87 0.0041
## m_protein' 70.1534 35.8432 1.96 0.0503
## m_protein'' -97.2704 55.3084 -1.76 0.0786
## m_protein''' 26.5642 23.7929 1.12 0.2642
## flc_ratio -2.1670 0.3534 -6.13 <0.0001
## flc_ratio' 39.7564 6.4897 6.13 <0.0001
## flc_ratio'' -93.6762 15.3845 -6.09 <0.0001
## isotype=IgA -0.1086 0.2526 -0.43 0.6671
## isotype=IgG -0.4988 0.2009 -2.48 0.0130
## isotype=Light-chain -0.4693 0.3223 -1.46 0.1454
##
The four-knot model is the fit_pred_linear model shown in the first chapter
fit_pred_linear
## Logistic Regression Model
##
## lrm(formula = outcome ~ igg + iga + igm + rcs(m_protein, 4) +
## rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE)
##
##
## Frequencies of Responses
##
## 0-4% 5-9% 10-14% >15%
## 455 430 91 67
##
## Model Likelihood Discrimination Rank Discrim.
## Ratio Test Indexes Indexes
## Obs 1043 LR chi2 378.19 R2 0.341 C 0.760
## max |deriv| 4e-08 d.f. 12 R2(12,1043)0.296 Dxy 0.519
## Pr(> chi2) <0.0001 R2(12,882.4)0.340 gamma 0.519
## Brier 0.086 tau-a 0.326
##
## Coef S.E. Wald Z Pr(>|Z|)
## y>=5-9% 1.4481 0.4232 3.42 0.0006
## y>=10-14% -1.1724 0.4231 -2.77 0.0056
## y>=>15% -2.5540 0.4380 -5.83 <0.0001
## igg 0.0130 0.0230 0.56 0.5722
## iga 0.2854 0.0451 6.32 <0.0001
## igm -0.1745 0.0464 -3.76 0.0002
## m_protein -0.6690 0.1797 -3.72 0.0002
## m_protein' 48.8852 10.8537 4.50 <0.0001
## m_protein'' -61.5660 13.7741 -4.47 <0.0001
## flc_ratio -2.1699 0.3533 -6.14 <0.0001
## flc_ratio' 39.8168 6.4875 6.14 <0.0001
## flc_ratio'' -93.8202 15.3791 -6.10 <0.0001
## isotype=IgA -0.1051 0.2525 -0.42 0.6771
## isotype=IgG -0.4976 0.2008 -2.48 0.0132
## isotype=Light-chain -0.4562 0.3211 -1.42 0.1554
##
fit_pred_mprotein_3 <- lrm(
formula = outcome ~ igg + iga + igm + rcs(m_protein, 3) +
rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE
)
fit_pred_mprotein_3
## Logistic Regression Model
##
## lrm(formula = outcome ~ igg + iga + igm + rcs(m_protein, 3) +
## rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE)
##
##
## Frequencies of Responses
##
## 0-4% 5-9% 10-14% >15%
## 455 430 91 67
##
## Model Likelihood Discrimination Rank Discrim.
## Ratio Test Indexes Indexes
## Obs 1043 LR chi2 360.35 R2 0.327 C 0.752
## max |deriv| 2e-09 d.f. 11 R2(11,1043)0.285 Dxy 0.505
## Pr(> chi2) <0.0001 R2(11,882.4)0.327 gamma 0.505
## Brier 0.087 tau-a 0.317
##
## Coef S.E. Wald Z Pr(>|Z|)
## y>=5-9% 0.9978 0.4089 2.44 0.0147
## y>=10-14% -1.5869 0.4116 -3.86 0.0001
## y>=>15% -2.9723 0.4273 -6.96 <0.0001
## igg 0.0229 0.0228 1.01 0.3147
## iga 0.3060 0.0448 6.83 <0.0001
## igm -0.1649 0.0463 -3.56 0.0004
## m_protein 0.0485 0.0663 0.73 0.4648
## m_protein' 0.3274 0.1112 2.95 0.0032
## flc_ratio -2.3610 0.3510 -6.73 <0.0001
## flc_ratio' 44.0973 6.4129 6.88 <0.0001
## flc_ratio'' -104.1289 15.1938 -6.85 <0.0001
## isotype=IgA -0.1397 0.2516 -0.56 0.5787
## isotype=IgG -0.4458 0.1989 -2.24 0.0250
## isotype=Light-chain -0.1425 0.3121 -0.46 0.6478
##
fit_pred_mprotein_linear <- lrm(
formula = outcome ~ igg + iga + igm + m_protein +
rcs(flc_ratio, 4) + isotype, data = df_pred, x = TRUE, y = TRUE
)
fit_pred_mprotein_linear
## Logistic Regression Model
##
## lrm(formula = outcome ~ igg + iga + igm + m_protein + rcs(flc_ratio,
## 4) + isotype, data = df_pred, x = TRUE, y = TRUE)
##
##
## Frequencies of Responses
##
## 0-4% 5-9% 10-14% >15%
## 455 430 91 67
##
## Model Likelihood Discrimination Rank Discrim.
## Ratio Test Indexes Indexes
## Obs 1043 LR chi2 351.52 R2 0.320 C 0.747
## max |deriv| 1e-10 d.f. 10 R2(10,1043)0.279 Dxy 0.494
## Pr(> chi2) <0.0001 R2(10,882.4)0.321 gamma 0.494
## Brier 0.088 tau-a 0.310
##
## Coef S.E. Wald Z Pr(>|Z|)
## y>=5-9% 0.5858 0.3841 1.52 0.1273
## y>=10-14% -1.9852 0.3895 -5.10 <0.0001
## y>=>15% -3.3283 0.4093 -8.13 <0.0001
## igg 0.0364 0.0223 1.63 0.1024
## iga 0.2775 0.0434 6.39 <0.0001
## igm -0.1624 0.0463 -3.51 0.0004
## m_protein 0.2307 0.0257 8.99 <0.0001
## flc_ratio -2.2961 0.3485 -6.59 <0.0001
## flc_ratio' 42.9422 6.3675 6.74 <0.0001
## flc_ratio'' -101.4123 15.0865 -6.72 <0.0001
## isotype=IgA -0.0408 0.2495 -0.16 0.8702
## isotype=IgG -0.4615 0.1993 -2.32 0.0206
## isotype=Light-chain 0.1435 0.2969 0.48 0.6289
##
As in the first chapter, we argue that calibration is essential, and if one of the models is obviously poorly calibrated it should not be considered in the following steps. First we show the current four-knot model with linear IgG, IgA and IgM.
plot(calibrate(fit_pred_linear, B = 200))
##
## n=1043 Mean absolute error=0.016 Mean squared error=0.00029
## 0.9 Quantile of absolute error=0.025
Next we show the five-knot model, which is qualitatively identical.
plot(calibrate(fit_pred_mprotein_5, B = 200))
##
## Divergence or singularity in 1 samples
##
## n=1043 Mean absolute error=0.016 Mean squared error=0.00031
## 0.9 Quantile of absolute error=0.026
Next we show the three-knot model, which is qualitatively identical.
plot(calibrate(fit_pred_mprotein_3, B = 200))
##
## n=1043 Mean absolute error=0.017 Mean squared error=0.00034
## 0.9 Quantile of absolute error=0.024
And lastly, the linear M protein model, which is qualitatively identical.
plot(calibrate(fit_pred_mprotein_linear, B = 200))
##
## n=1043 Mean absolute error=0.016 Mean squared error=3e-04
## 0.9 Quantile of absolute error=0.021
All prior disclaimors about effect sizes and prediction models apply. First we examine M protein on the absolute predicted risk scale and show that the effect sizes are essentially similar.
Predict(fit_pred_mprotein_5,
kint = 2,
m_protein,
fun = plogis) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Five-knot") %>%
union_all(
Predict(fit_pred_linear,
kint = 2,
m_protein,
fun = plogis) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Four-knot")
) %>%
union_all(
Predict(fit_pred_mprotein_3,
kint = 2,
m_protein,
fun = plogis) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Three-knot")
) %>%
union_all(
Predict(fit_pred_mprotein_linear,
kint = 2,
m_protein,
fun = plogis) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = m_protein, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper, color = NULL), alpha = .2) +
coord_cartesian(ylim = c(0, NA)) +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(limits = c(5, 22)) +
labs(x = "M protein (g/L)", y = "Proportion (%) having \n≥10% BMPC", title = "M protein") +
theme_bw() +
theme(legend.position = "bottom")
Then we examine total IgG on the odds ratio scale
Predict(fit_pred_mprotein_5,
kint = 2,
m_protein,
ref.zero = TRUE, fun = exp) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Five-knot") %>%
union_all(
Predict(fit_pred_linear,
kint = 2,
m_protein,
ref.zero = TRUE, fun = exp) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Four-knot")
) %>%
union_all(
Predict(fit_pred_mprotein_3,
kint = 2,
m_protein,
ref.zero = TRUE, fun = exp) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Three-knot")
) %>%
union_all(
Predict(fit_pred_mprotein_linear,
kint = 2,
m_protein,
ref.zero = TRUE, fun = exp) %>%
select(m_protein, yhat, lower, upper) %>%
mutate(model = "Linear")
) %>%
as_data_frame() %>%
ggplot(aes(x = m_protein, y = yhat, color = model, fill = model)) +
geom_line() +
geom_ribbon(aes(ymin = lower, ymax = upper, color = NULL), alpha = .3) +
geom_hline(yintercept = 1, lty = 2) +
scale_y_continuous(
breaks = c(0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 4, 8, 16, 32, 64, 128),
limits = c(NA, NA)) +
coord_trans(y = "log10") +
labs(x = "M protein (g/L)", y = "OR of 10% BMPC or more", title = "M protein") +
theme_bw() +
theme(legend.position = "bottom")
We will do a series of comparisons. Our main aim is to decide whether the M protein can justifiably be modeled as a linear variable compared to the current prespecified four-knot restricted cubic spline. From the following, we can say that we have strong evidence that a four-knot restriced cubic spline transformation fits the data better than including M protein as a linear variable.
lrtest(fit_pred_mprotein_linear, fit_pred_linear)
##
## Model 1: outcome ~ igg + iga + igm + m_protein + rcs(flc_ratio, 4) + isotype
## Model 2: outcome ~ igg + iga + igm + rcs(m_protein, 4) + rcs(flc_ratio,
## 4) + isotype
##
## L.R. Chisq d.f. P
## 2.666601e+01 2.000000e+00 1.620133e-06
We further evaluate whether there is strong evidence that a five-knot restriced cubic spline fits the data better than four. There is no evidence of this.
lrtest(fit_pred_mprotein_5, fit_pred_linear)
##
## Model 1: outcome ~ igg + iga + igm + rcs(m_protein, 5) + rcs(flc_ratio,
## 4) + isotype
## Model 2: outcome ~ igg + iga + igm + rcs(m_protein, 4) + rcs(flc_ratio,
## 4) + isotype
##
## L.R. Chisq d.f. P
## 0.2733163 1.0000000 0.6011158
and lastly, whether the four-knot restricted cubic spline fits the data better than a three-knot restriced cubic spline. There is strong evidence for this.
lrtest(fit_pred_mprotein_3, fit_pred_linear)
##
## Model 1: outcome ~ igg + iga + igm + rcs(m_protein, 3) + rcs(flc_ratio,
## 4) + isotype
## Model 2: outcome ~ igg + iga + igm + rcs(m_protein, 4) + rcs(flc_ratio,
## 4) + isotype
##
## L.R. Chisq d.f. P
## 1.784498e+01 1.000000e+00 2.396509e-05
The degree to which the four-knot restricted cubic spline model fits the data better compared to the others, can be quantifiable by calculating the adequacy models using the models’ likelihood ratio Chi-squared statistics. First we compare the linear M protein model to the four-knot restricted cubic spline model.
pre_test_lr <- fit_pred_mprotein_linear$stats["Model L.R."] # linear likelihood chi-squared statistic
post_test_lr <- fit_pred_linear$stats["Model L.R."] # 4-knot likelihood chi-squared statistic
Below is a measure of the adequacy of the linear M protein model compared to the four-knot restricted cubic spline model, 0.93 out of a maximum of 1
add_base <- pre_test_lr/post_test_lr
add_base
## Model L.R.
## 0.9294905
In other words, the fraction of new information provided by the four-knot model by this metric is 0.070, which is substantial.
frac_new <- 1 - add_base
frac_new
## Model L.R.
## 0.07050946
Now we show the four-knot compared to the three-knot model.
pre_test_lr <- fit_pred_mprotein_3$stats["Model L.R."] # 3-knot likelihood chi-squared statistic
post_test_lr <- fit_pred_linear$stats["Model L.R."] # 4-knot likelihood chi-squared statistic
Below is a measure of the adequacy of the three-knot M protein model compared to the four-knot restricted cubic spline model, 0.929 out of a maximum of 1
add_base <- pre_test_lr/post_test_lr
add_base
## Model L.R.
## 0.9528148
In other words, the fraction of new information provided by the 4-knot model by this metric is 0.070, which is substantial.
frac_new <- 1 - add_base
frac_new
## Model L.R.
## 0.04718516
The question remains whether the increased fit of the non-linear model to the data compared to the linear model is worthwhile, given its added complexity. Akaike’s information criterion provides a method for penalizing the log likelihood achieved by a given model for its complexity to obtain a more unbiased assessment of the model’s worth. The penalty is to add twice the number of parameters to the −2 log likelihood. On this metric, the model with the lower AIC should generally be favored. The AIC of the four-knot restricted cubic spline is by far the lowest, after which the five-knot restricted cubic spline is the next lowest.
AIC(fit_pred_mprotein_5)
## [1] 1982.206
AIC(fit_pred_linear)
## [1] 1980.479
AIC(fit_pred_mprotein_3)
## [1] 1996.324
AIC(fit_pred_mprotein_linear)
## [1] 2003.145
Next we compare the variances of the predicted risks from the four-knot model and the linear M protein model. At this point, we do not feel that comparing the five-knot and three-knot is needed. In the prediction setting, increased variance in risk estimates is generally desirable. As shown below, the variance is slightly higher with the four-knot model.
pre <- predict(fit_pred_mprotein_linear, type='fitted')
pre <- pre[,2]
post <- predict(fit_pred_linear, type='fitted')
post <- post[,2]
par(mgp=c(4,1,0), mar=c(6,5,2,2))
xlab <- c(
paste0("Linear\nvariance =", round(var(pre), 3)),
paste0("Non-linear\nvariance=",round(var(post), 3))
)
histbackback(pre, post, brks=seq(0, 1, by=0.005), xlab=xlab, ylab= "Proportion (%) having \n≥10% BMPC")
We show that the relative explained variation of predicted risk of the linear M protein model compared to the four-knot model is 0.92 (theoretically out of a maximum of 1)
var_pre <- round(var(pre), 3) #linear
var_post <- round(var(post), 3) #non-linear
relativ_expl <- var_pre/var_post
relativ_expl
## [1] 0.9210526
In other words, the fraction of new information contained in the 4-knot M protein model by this metric is 0.079
frac_new2 <- 1 - relativ_expl
frac_new2
## [1] 0.07894737
Finally, as this is a prediction model that we intend for clinical use, we should be satisfied that the decision curves associated with the models are similar. If not, we should heavily favor the model that leads to increased net-benefit over plausible risk thresholds. Again, we feel that we no longer need to compare the 5-knot and 3-knot models and focus on the 4-knot model compared to the linear M protein model. First we extract the linear predictor from the 4-knot model. This has already been done in the chapter above and is saved in ‘df_linear_nonlinear$nonlinear_pred’. Next we extract the linear predictor from the linear M protein model.
Function(fit_pred_mprotein_linear) # next we extract the linear predictor from the linear model
## function(igg = 10.21,iga = 2.167,igm = 0.718,m_protein = 1.91,flc_ratio = 1.237794,isotype = "IgG") {+0.036413502*igg+0.27748825*iga-0.16241595*igm+0.23069192*m_protein-2.2960801* flc_ratio+0.82010851*pmax(flc_ratio-0.2041965,0)^3-1.9367662*pmax(flc_ratio-1.0186109,0)^3+1.1214264*pmax(flc_ratio-1.6415064,0)^3-0.0047686997*pmax(flc_ratio-7.440332,0)^3-0.040766989*(isotype=="IgA")-0.46147411*(isotype=="IgG")+0.14347237*(isotype=="Light-chain") }
## <environment: 0x7fe9eea3bd30>
df_linear_nonlinear$linear_mprotein_pred <- with(
df_linear_nonlinear,
expit(
-1.9852 +0.036413502 * igg + 0.27748825 * iga - 0.16241595 * igm + 0.23069192 * m_protein -
2.2960801 * flc_ratio + 0.82010851 * pmax(flc_ratio - 0.2041965, 0) ^ 3 -
1.9367662 * pmax(flc_ratio - 1.0186109, 0) ^ 3 + 1.1214264 *
pmax(flc_ratio - 1.6415064, 0) ^ 3 - 0.0047686997 * pmax(flc_ratio - 7.440332, 0) ^ 3
- 0.040766989 * (isotype == "IgA") - 0.46147411 * (isotype == "IgG")
+ 0.14347237 * (isotype == "Light-chain") )
)
We create the decision curve analysis models using the dca pacakge. We examine the net-benefit of the 4-knot and linear M protein models in a opt-out strategy, in which net benefit is expressed in the relative proportion of true negatives. As demonstrated by the resulting decision curve figure, the 4-knot model is superior to the linear M protein model over the majority of plausible low-risk thresholds.
linear_mprotein_model <- decision_curve(perc_10 ~ linear_mprotein_pred,
data = df_linear_nonlinear,
policy = 'opt-out',
fitted = TRUE,
thresholds = seq(0, 1, by = .001),
bootstraps = 1000)
linear_model$derived.data %>%
select(thresholds, NB, NB_lower, NB_upper, model) %>%
union_all(
linear_mprotein_model$derived.data %>%
select(thresholds, NB, NB_lower, NB_upper, model) %>%
filter(model == "perc_10 ~ linear_mprotein_pred")
) %>%
mutate(model = factor(model, levels = c("perc_10 ~ linear_pred", "perc_10 ~ linear_mprotein_pred", "All", "None"))) %>%
ggplot(aes(x = thresholds, y = NB, color = model, fill = model)) +
geom_line(size = 1.1, position = position_dodge(width = 0.005)) +
geom_vline(aes(xintercept = 0.05), lty = 2) +
geom_vline(aes(xintercept = 0.10), lty = 2) +
scale_color_manual(
name = "Follow-up strategy:",
labels = c(
"Defer based on 4-knot model",
"Defer based on linear M protein model",
"Obtain sample from all",
"Obtain sample from none"
),
values = c(nejm_palette[c(2, 3)], "black", nejm_palette[c(1)])
) +
scale_x_continuous(breaks = seq(0, 1, by = 0.02), labels = scales::percent_format(accuracy = 1)) +
coord_cartesian(xlim = c(0, 0.25), ylim = c(-0.2, 0.7)) +
labs(x = NULL, y = "Net Benefit") +
theme_bw() +
theme(
legend.title = element_blank(),
legend.background = element_rect(fill = NA),
panel.grid.minor.x = element_blank(),
legend.position = c(0.2, 0.82)
)
We have shown that the model that includes M protein as a non-linear variable compared to a linear variable i) fits the data significantly better on the likelihood ratio test, providing roughly a 7% increase in the fraction of new information on this metric compared to the linear M protein model. ii) has a lower Akaike’s information criterion than does the linear M protein model iii) significantly increases the variance of predicted risk, providing a roughly 7.8% increase in the fraction of new information on this metric compared to the linear M protein model. iv) provides superior net benefit to a model that includes M protein as a linear variable over the vast majority of plausible low-risk thresholds.
It is clear that the model containing M protein as a four knot restricted cubic spline transformation is superior to one that contains M protein as a linear variable. Furthermore, we found no evidence that models with three or five knot outperformed the four knot model, which should then be chosen by default, as the four knot model was pre-specified.