Skip to content

Commit

Permalink
change CITL wording for validating logistic models
Browse files Browse the repository at this point in the history
* adds observed:expected ratio for logistic models
  • Loading branch information
GlenMartin31 committed Sep 18, 2023
1 parent f5aaab7 commit b4b8616
Show file tree
Hide file tree
Showing 15 changed files with 72 additions and 34 deletions.
9 changes: 8 additions & 1 deletion R/map_newdata.R
Expand Up @@ -9,7 +9,8 @@
#' @param new_data data.frame upon which the prediction model should be applied
#' (for subsequent validation/model updating/model aggregation).
#' @param binary_outcome Character variable giving the name of the column in
#' \code{new_data} that represents the observed outcomes. Only relevant for
#' \code{new_data} that represents the observed binary outcomes (should be
#' coded 0 and 1 for non-event and event, respectively). Only relevant for
#' \code{model_type}="logistic"; leave as \code{NULL} otherwise. Leave as
#' \code{NULL} if \code{new_data} does not contain any outcomes.
#' @param survival_time Character variable giving the name of the column in
Expand Down Expand Up @@ -119,6 +120,9 @@ map_newdata.predinfo_logistic <- function(x,
if(binary_outcome %in% names(new_data) == FALSE) {
stop("'binary_outcome' not found in 'new_data'")
}
if(all(unique(new_data[[binary_outcome]]) %in% c(0,1)) == FALSE){
stop("The 'binary_outcome' column of 'new_data' should only contain 0 and 1s")
}
}

# Check that all predictor variables specified in the pminfo object are also
Expand Down Expand Up @@ -206,6 +210,9 @@ map_newdata.predinfo_survival <- function(x,
event_indicator %in% names(new_data) == FALSE) {
stop("'survival_time' and/or 'event_indicator' not found in 'new_data'")
}
if(all(unique(new_data[[event_indicator]]) %in% c(0,1)) == FALSE){
stop("The 'event_indicator' column of 'new_data' should only contain 0 and 1s")
}
}

# Check that all predictor variables specified in the pminfo object are also
Expand Down
3 changes: 2 additions & 1 deletion R/pred_predict.R
Expand Up @@ -8,7 +8,8 @@
#' @param new_data data.frame upon which predictions are obtained using the
#' prediction model.
#' @param binary_outcome Character variable giving the name of the column in
#' \code{new_data} that represents the observed outcomes. Only relevant for
#' \code{new_data} that represents the observed binary outcomes (should be
#' coded 0 and 1 for non-event and event, respectively). Only relevant for
#' \code{model_type}="logistic"; leave as \code{NULL} otherwise. Leave as
#' \code{NULL} if \code{new_data} does not contain any outcomes.
#' @param survival_time Character variable giving the name of the column in
Expand Down
6 changes: 4 additions & 2 deletions R/pred_stacked_regression.R
Expand Up @@ -13,8 +13,10 @@
#' @param new_data data.frame upon which the prediction models should be
#' aggregated.
#' @param binary_outcome Character variable giving the name of the column in
#' \code{new_data} that represents the observed outcomes. Only relevant for
#' \code{x$model_type}="logistic"; leave as \code{NULL} otherwise.
#' \code{new_data} that represents the observed binary outcomes (should be
#' coded 0 and 1 for non-event and event, respectively). Only relevant for
#' \code{model_type}="logistic"; leave as \code{NULL} otherwise. Leave as
#' \code{NULL} if \code{new_data} does not contain any outcomes.
#' @param survival_time Character variable giving the name of the column in
#' \code{new_data} that represents the observed survival times. Only relevant
#' for \code{x$model_type}="survival"; leave as \code{NULL} otherwise.
Expand Down
6 changes: 4 additions & 2 deletions R/pred_update.R
Expand Up @@ -13,8 +13,10 @@
#' @param new_data data.frame upon which the prediction models should be
#' updated.
#' @param binary_outcome Character variable giving the name of the column in
#' \code{new_data} that represents the observed outcomes. Only relevant for
#' \code{x$model_type}="logistic"; leave as \code{NULL} otherwise.
#' \code{new_data} that represents the observed binary outcomes (should be
#' coded 0 and 1 for non-event and event, respectively). Only relevant for
#' \code{model_type}="logistic"; leave as \code{NULL} otherwise. Leave as
#' \code{NULL} if \code{new_data} does not contain any outcomes.
#' @param survival_time Character variable giving the name of the column in
#' \code{new_data} that represents the observed survival times. Only relevant
#' for \code{x$model_type}="survival"; leave as \code{NULL} otherwise.
Expand Down
22 changes: 14 additions & 8 deletions R/pred_validate.R
Expand Up @@ -8,8 +8,10 @@
#' @param new_data data.frame upon which the prediction model should be
#' evaluated.
#' @param binary_outcome Character variable giving the name of the column in
#' \code{new_data} that represents the observed outcomes. Only relevant for
#' \code{x$model_type}="logistic"; leave as \code{NULL} otherwise.
#' \code{new_data} that represents the observed binary outcomes (should be
#' coded 0 and 1 for non-event and event, respectively). Only relevant for
#' \code{model_type}="logistic"; leave as \code{NULL} otherwise. Leave as
#' \code{NULL} if \code{new_data} does not contain any outcomes.
#' @param survival_time Character variable giving the name of the column in
#' \code{new_data} that represents the observed survival times. Only relevant
#' for \code{x$model_type}="survival"; leave as \code{NULL} otherwise.
Expand Down Expand Up @@ -282,18 +284,23 @@ predvalidatesummary.fnc <- function(object, model_type) {

cat("Calibration Measures \n",
"--------------------------------- \n", sep = "")
results <- matrix(NA, ncol = 4, nrow = 2)
results <- matrix(NA, ncol = 4, nrow = 3)
colnames(results) <- c("Estimate",
"Std. Err",
"Lower 95% Confidence Interval",
"Upper 95% Confidence Interval")
rownames(results) <- c("Calibration-in-the-large",
rownames(results) <- c("Observed:Expected Ratio",
"Calibration Intercept",
"Calibration Slope")
results[1,] <- c(round(object$CITL, 4),
results[1,] <- c(round(object$OE_ratio, 4),
round(object$OE_ratio_SE, 4),
round((object$OE_ratio * exp(-stats::qnorm(0.975) * object$OE_ratio_SE)), 4),
round((object$OE_ratio * exp(stats::qnorm(0.975) * object$OE_ratio_SE)), 4))
results[2,] <- c(round(object$CITL, 4),
round(object$CITL_SE, 4),
round((object$CITL - (stats::qnorm(0.975)*object$CITL_SE)), 4),
round((object$CITL + (stats::qnorm(0.975)*object$CITL_SE)), 4))
results[2,] <- c(round(object$CalSlope, 4),
results[3,] <- c(round(object$CalSlope, 4),
round(object$CalSlope_SE, 4),
round((object$CalSlope - (stats::qnorm(0.975)*object$CalSlope_SE)), 4),
round((object$CalSlope + (stats::qnorm(0.975)*object$CalSlope_SE)), 4))
Expand Down Expand Up @@ -334,8 +341,7 @@ predvalidatesummary.fnc <- function(object, model_type) {
results[1,] <- c(round(object$OE_ratio, 4),
round(object$OE_ratio_SE, 4),
round((object$OE_ratio * exp(-stats::qnorm(0.975) * object$OE_ratio_SE)), 4),
round((object$OE_ratio * exp(stats::qnorm(0.975) * object$OE_ratio_SE)), 4),
round((object$CITL + (stats::qnorm(0.975)*object$CITL_SE)), 4))
round((object$OE_ratio * exp(stats::qnorm(0.975) * object$OE_ratio_SE)), 4))
results[2,] <- c(round(object$CalSlope, 4),
round(object$CalSlope_SE, 4),
round((object$CalSlope - (stats::qnorm(0.975)*object$CalSlope_SE)), 4),
Expand Down
9 changes: 7 additions & 2 deletions R/validate_logistic.R
Expand Up @@ -19,8 +19,11 @@ validate_logistic <- function(ObservedOutcome,
'observations deleted due to predicted risks being 0 and 1'))
}

#Estimate observed:expected ratio
OE_ratio <- mean(ObservedOutcome) / mean(Prob)
OE_ratio_SE <- sqrt(1 / sum(ObservedOutcome))

#Estimate calibration intercept (i.e. calibration-in-the-large)
#Estimate calibration intercept
CITL_mod <- stats::glm(ObservedOutcome ~ 1,
family = stats::binomial(link = "logit"),
offset = LP)
Expand Down Expand Up @@ -91,7 +94,9 @@ validate_logistic <- function(ObservedOutcome,
}

#Return results
out <- list("CITL" = CITL,
out <- list("OE_ratio" = OE_ratio,
"OE_ratio_SE" = OE_ratio_SE,
"CITL" = CITL,
"CITL_SE" = CITLSE,
"CalSlope" = CalSlope,
"CalSlope_SE" = CalSlopeSE,
Expand Down
1 change: 1 addition & 0 deletions README.Rmd
Expand Up @@ -19,6 +19,7 @@ knitr::opts_chunk$set(
[![R-CMD-check](https://github.com/GlenMartin31/predRupdate/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/GlenMartin31/predRupdate/actions/workflows/R-CMD-check.yaml)
[![Codecov test coverage](https://codecov.io/gh/GlenMartin31/predRupdate/branch/master/graph/badge.svg)](https://app.codecov.io/gh/GlenMartin31/predRupdate?branch=master)
[![CRAN status](https://www.r-pkg.org/badges/version/predRupdate)](https://CRAN.R-project.org/package=predRupdate)
"[![metacran downloads](https://cranlogs.r-pkg.org/badges/grand-total/dplyr)](https://cran.r-project.org/package=dplyr)"
<!-- badges: end -->

The goal of predRupdate is to provide a suite of functions for validating a existing (i.e. previously developed) prediction/ prognostic model, and for applying model updating methods to said model, according to an available dataset.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -10,6 +10,8 @@
coverage](https://codecov.io/gh/GlenMartin31/predRupdate/branch/master/graph/badge.svg)](https://app.codecov.io/gh/GlenMartin31/predRupdate?branch=master)
[![CRAN
status](https://www.r-pkg.org/badges/version/predRupdate)](https://CRAN.R-project.org/package=predRupdate)
[![metacran
downloads](https://cranlogs.r-pkg.org/badges/grand-total/dplyr)](https://cran.r-project.org/package=dplyr)
<!-- badges: end -->

The goal of predRupdate is to provide a suite of functions for
Expand Down
3 changes: 2 additions & 1 deletion man/map_newdata.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion man/pred_predict.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions man/pred_stacked_regression.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions man/pred_update.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions man/pred_validate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions tests/testthat/_snaps/pred_validate_logistic.md
Expand Up @@ -5,12 +5,14 @@
Output
Calibration Measures
---------------------------------
Estimate Std. Err Lower 95% Confidence Interval
Calibration-in-the-large 0.7323 0.0206 0.6921
Calibration Slope 0.6484 0.0463 0.5576
Upper 95% Confidence Interval
Calibration-in-the-large 0.7726
Calibration Slope 0.7392
Estimate Std. Err Lower 95% Confidence Interval
Observed:Expected Ratio 1.9006 0.0188 1.8319
Calibration Intercept 0.7323 0.0206 0.6921
Calibration Slope 0.6484 0.0463 0.5576
Upper 95% Confidence Interval
Observed:Expected Ratio 1.9719
Calibration Intercept 0.7726
Calibration Slope 0.7392
Also examine the calibration plot, if produced.
Expand Down
10 changes: 6 additions & 4 deletions tests/testthat/test-pred_validate_logistic.R
Expand Up @@ -15,8 +15,9 @@ test_that("output of pred_validate is as expected - single models", {
expect_s3_class(val_results, c("predvalidate_logistic", "predvalidate"))
expect_type(val_results, type = "list")
expect_equal(names(val_results),
c("CITL", "CITL_SE", "CalSlope", "CalSlope_SE", "AUC",
"AUC_SE", "R2_CoxSnell", "R2_Nagelkerke", "BrierScore", "M"))
c("OE_ratio", "OE_ratio_SE", "CITL", "CITL_SE", "CalSlope",
"CalSlope_SE", "AUC", "AUC_SE", "R2_CoxSnell",
"R2_Nagelkerke", "BrierScore", "M"))

expect_snapshot(summary(val_results))

Expand Down Expand Up @@ -48,7 +49,8 @@ test_that("output of pred_validate is as expected - multiple models", {
for(m in 1:model2$M) {
expect_type(val_results[[m]], type = "list")
expect_equal(names(val_results[[m]]),
c("CITL", "CITL_SE", "CalSlope", "CalSlope_SE", "AUC",
"AUC_SE", "R2_CoxSnell", "R2_Nagelkerke", "BrierScore"))
c("OE_ratio", "OE_ratio_SE", "CITL", "CITL_SE", "CalSlope",
"CalSlope_SE", "AUC", "AUC_SE", "R2_CoxSnell",
"R2_Nagelkerke", "BrierScore"))
}
})

0 comments on commit b4b8616

Please sign in to comment.