diff --git a/DESCRIPTION b/DESCRIPTION index a073924f..51d1ff67 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,7 +24,6 @@ LazyData: true Roxygen: list(markdown = TRUE) RoxygenNote: 7.2.2 Imports: - dplyr, WeightIt, marginaleffects, tidyr, diff --git a/R/assessBalance.R b/R/assessBalance.R index e7f27895..2072373e 100644 --- a/R/assessBalance.R +++ b/R/assessBalance.R @@ -124,6 +124,9 @@ assessBalance <- function(home_dir, data, exposure, exposure_time_pts, outcome, if (missing(home_dir)) { stop("Please supply a home directory.", call. = FALSE) } + else if(!is.character(home_dir)){ + stop("Please provide a valid home directory path as a string if you wish to save output locally.", call. = FALSE) + } else if(!dir.exists(home_dir)) { stop("Please provide a valid home directory path if you wish to save output locally.", call. = FALSE) } @@ -133,24 +136,45 @@ assessBalance <- function(home_dir, data, exposure, exposure_time_pts, outcome, stop("Please supply data as either a dataframe with no missing data or imputed data in the form of a mids object or path to folder with imputed csv datasets.", call. = FALSE) } + else if (!mice::is.mids(data) & !is.data.frame(data) & !inherits(data, "list")) { + stop("Please provide wide data as a 'mids' object, a data frame, or a list of imputed csv files in the 'data' field.", call. = FALSE) + } + + if (missing(exposure)){ stop("Please supply a single exposure.", call. = FALSE) } + else if(!is.character(exposure) | length(exposure) != 1){ + stop("Please supply a single exposure as a character.", call. = FALSE) + } + if (missing(outcome)){ stop("Please supply a single outcome.", call. = FALSE) } + else if(!is.character(outcome) | length(outcome) != 1){ + stop("Please supply a single outcome as a character.", call. = FALSE) + } + if (missing(exposure_time_pts)){ stop("Please supply the exposure time points at which you wish to create weights.", call. = FALSE) } - - if (missing(type)){ - stop("Please supply a 'weighted', 'prebalance' type", call. = FALSE) + else if(!is.numeric(exposure_time_pts)){ + stop("Please supply a list of exposure time points as integers.", call. = FALSE) } + if (missing(formulas)){ stop("Please supply a list of balancing formulas.", call. = FALSE) } + else if(!inherits(formulas, "list")){ + stop("Please provide a list of formulas for each exposure time point", call. = FALSE) + } + else if(length(formulas) != length(exposure_time_pts)){ + stop("Please provide a list of formulas for each exposure time point", call. = FALSE) + } - + if (missing(type)){ + stop("Please supply a 'weighted', 'prebalance' type", call. = FALSE) + } if (!inherits(type, "character") | length(type) != 1 ){ stop("Please provide a single type as a character string from the following list: 'prebalance', 'weighted'", call. = FALSE) } @@ -164,10 +188,6 @@ assessBalance <- function(home_dir, data, exposure, exposure_time_pts, outcome, stop("The 'weighted' mode of this function requires weights be supplied in the form of output from createWeights.", call. = FALSE) } - if (!mice::is.mids(data) & !is.data.frame(data) & !inherits(data, "list")) { - stop("Please provide either a 'mids' object, a data frame, or a list of imputed csv files in the 'data' field.", call. = FALSE) - } - if (!is.null(weights) & ! inherits(weights, "list")){ stop("Please supply a list of weights output from the createWeights function.", call. = FALSE) } @@ -186,8 +206,18 @@ assessBalance <- function(home_dir, data, exposure, exposure_time_pts, outcome, stop("Please provide a list variable names as characters that are important confounders.", call. = FALSE) } - if(!inherits(formulas, "list")){ - stop("Please provide a list of formulas for each exposure time point", call. = FALSE) + if(!is.logical(verbose)){ + stop("Please set verbose to either TRUE or FALSE.", call. = FALSE) + } + else if(length(verbose) != 1){ + stop("Please provide a single TRUE or FALSE value to verbose.", call. = FALSE) + } + + if(!is.logical(save.out)){ + stop("Please set save.out to either TRUE or FALSE.", call. = FALSE) + } + else if(length(save.out) != 1){ + stop("Please provide a single TRUE or FALSE value to save.out.", call. = FALSE) } diff --git a/R/calcBalStats.R b/R/calcBalStats.R index 27d34dae..d65c0268 100644 --- a/R/calcBalStats.R +++ b/R/calcBalStats.R @@ -5,22 +5,6 @@ #' approaches to assessing balance for time-varying exposures by weighting #' statistics based on sample distribution in exposure histories. #' -#' @importFrom ggplot2 ggplot -#' @importFrom ggplot2 theme -#' @importFrom ggplot2 geom_text -#' @importFrom ggplot2 geom_vline -#' @importFrom ggplot2 geom_point -#' @importFrom ggplot2 xlab -#' @importFrom ggplot2 aes -#' @importFrom ggplot2 xlim -#' @importFrom ggplot2 ggtitle -#' @importFrom ggplot2 scale_y_discrete -#' @importFrom ggplot2 element_text -#' @importFrom ggplot2 element_rect -#' @importFrom ggplot2 element_blank -#' @importFrom ggplot2 guide_axis -#' @importFrom ggplot2 ggsave -#' @importFrom stargazer stargazer #' @param home_dir (optional) path to home directory (required if save.out = #' TRUE) #' @param data data in wide format as: a data frame, path to folder of imputed @@ -111,7 +95,6 @@ calcBalStats <- function(home_dir = NA, data, formulas, exposure, exposure_time_ if (weighted == 1){ weights_method = weights$method w <- weights$weights #IPTW weights - # data <- data %>% dplyr::mutate(weights = as.numeric(w)) data$weights <- as.numeric(w) } else{ @@ -319,14 +302,6 @@ calcBalStats <- function(home_dir = NA, data, formulas, exposure, exposure_time_ bal_stats <- as.data.frame(cbind(bal_stats, weighted_bal_stats)) - # standardizing balance statistics after weighting by history - # bal_stats <- bal_stats %>% - # dplyr::mutate(std_bal_stats = weighted_bal_stats / - # (sapply(seq(ncol(data[, covars])), function(x) { #issue: looking in data for unweighted vals but factors have additional vars - # sd(as.numeric(data[, covars][, x]), na.rm = TRUE) }) *# unweighted covar sd - # sd(data[, paste0(exposure, ".", exposure_time_pt)], na.rm = TRUE))) # exposure SD at that time pt - - # bal_stats <- bal_stats %>% bal_stats$std_bal_stats <- weighted_bal_stats / (sapply(seq(nrow(bal_stats)), function(x) { #issue: looking in data for unweighted vals but factors have additional vars @@ -501,10 +476,6 @@ calcBalStats <- function(home_dir = NA, data, formulas, exposure, exposure_time_ bal_stats <- as.data.frame(bal_stats) bal_stats$covariate <- rownames(bal_stats) - # # Renames factor covariates - # bal_stats$covariate[sapply(strsplit(bal_stats$covariate, "_"), "[", 1) %in% factor_covariates] <- - # sapply(strsplit(bal_stats$covariate, "_"), "[", 1)[sapply(strsplit(bal_stats$covariate, "_"), "[", 1) %in% factor_covariates] - #averages across factor levels to create one bal stat per factor variable? data$ID <- as.numeric(data$ID) f_vars <- colnames(data)[sapply(data, is.factor)] diff --git a/R/compareHistories.R b/R/compareHistories.R index 09d60493..6628ec27 100644 --- a/R/compareHistories.R +++ b/R/compareHistories.R @@ -106,6 +106,9 @@ compareHistories <- function(home_dir, exposure, exposure_time_pts, outcome, mod if (missing(home_dir)) { stop("Please supply a home directory.", call. = FALSE) } + else if(!is.character(home_dir)){ + stop("Please provide a valid home directory path as a string if you wish to save output locally.", call. = FALSE) + } else if(!dir.exists(home_dir)) { stop("Please provide a valid home directory path if you wish to save output locally.", call. = FALSE) } @@ -114,20 +117,46 @@ compareHistories <- function(home_dir, exposure, exposure_time_pts, outcome, mod if (missing(exposure)){ stop("Please supply a single exposure.", call. = FALSE) } + else if(!is.character(exposure) | length(exposure) != 1){ + stop("Please supply a single exposure as a character.", call. = FALSE) + } + if (missing(outcome)){ stop("Please supply a single outcome.", call. = FALSE) } + else if(!is.character(outcome) | length(outcome) != 1){ + stop("Please supply a single outcome as a character.", call. = FALSE) + } + if (missing(exposure_time_pts)){ stop("Please supply the exposure time points at which you wish to create weights.", call. = FALSE) } + else if(!is.numeric(exposure_time_pts)){ + stop("Please supply a list of exposure time points as integers.", call. = FALSE) + } if (missing(model)){ stop("Please supply a list of model output", call. = FALSE) } - if(!inherits(model, "list")){ + else if(!inherits(model, "list")){ stop("Please provide a list of model output from the fitModel function.", call. = FALSE) } + if(!is.logical(verbose)){ + stop("Please set verbose to either TRUE or FALSE.", call. = FALSE) + } + else if(length(verbose) != 1){ + stop("Please provide a single TRUE or FALSE value to verbose.", call. = FALSE) + } + + if(!is.logical(save.out)){ + stop("Please set save.out to either TRUE or FALSE.", call. = FALSE) + } + else if(length(save.out) != 1){ + stop("Please provide a single TRUE or FALSE value to save.out.", call. = FALSE) + } + + if(save.out){ histories_dir <- file.path(home_dir, "histories") diff --git a/R/createFormulas.R b/R/createFormulas.R index e7042cf8..23e2be1c 100644 --- a/R/createFormulas.R +++ b/R/createFormulas.R @@ -95,6 +95,9 @@ createFormulas <- function(home_dir, exposure, exposure_time_pts, outcome, type, if (missing(home_dir)) { stop("Please supply a home directory.", call. = FALSE) } + else if(!is.character(home_dir)){ + stop("Please provide a valid home directory path as a string if you wish to save output locally.", call. = FALSE) + } else if(!dir.exists(home_dir)) { stop("Please provide a valid home directory path if you wish to save output locally.", call. = FALSE) } @@ -103,20 +106,37 @@ createFormulas <- function(home_dir, exposure, exposure_time_pts, outcome, type, if (missing(exposure)){ stop("Please supply a single exposure.", call. = FALSE) } + else if(!is.character(exposure) | length(exposure) != 1){ + stop("Please supply a single exposure as a character.", call. = FALSE) + } + if (missing(outcome)){ stop("Please supply a single outcome.", call. = FALSE) } + else if(!is.character(outcome) | length(outcome) != 1){ + stop("Please supply a single outcome as a character.", call. = FALSE) + } + if (missing(exposure_time_pts)){ stop("Please supply the exposure time points at which you wish to create weights.", call. = FALSE) } + else if(!is.numeric(exposure_time_pts)){ + stop("Please supply a list of exposure time points as integers.", call. = FALSE) + } + if (missing(tv_confounders)){ warning("You have not specified any time-varying confounders. If you have time-varying exposure, please list all wide exposure variables as tv_confounders.", call. = FALSE) tv_confounders <- character(0) } + else if(!is.character(tv_confounders)){ + stop("Please provide a list of time-varying confounders as character strings.") + } + if (missing(ti_confounders)){ stop("You have not specified time invariant confounders.", call. = FALSE) # ti_confounders <- NULL } + if (missing(type)){ stop("Please supply a 'full', 'short', or 'update' type", call. = FALSE) } @@ -135,6 +155,20 @@ createFormulas <- function(home_dir, exposure, exposure_time_pts, outcome, type, stop("Please provide a data frame of balance statistics from the assessBalance function.", call. = FALSE) } + if(!is.logical(verbose)){ + stop("Please set verbose to either TRUE or FALSE.", call. = FALSE) + } + else if(length(verbose) != 1){ + stop("Please provide a single TRUE or FALSE value to verbose.", call. = FALSE) + } + + if(!is.logical(save.out)){ + stop("Please set save.out to either TRUE or FALSE.", call. = FALSE) + } + else if(length(save.out) != 1){ + stop("Please provide a single TRUE or FALSE value to save.out.", call. = FALSE) + } + time_varying_covariates <- tv_confounders all_covars <- c(tv_confounders, ti_confounders) @@ -192,7 +226,11 @@ createFormulas <- function(home_dir, exposure, exposure_time_pts, outcome, type, else if (type == "update"){ if(is.null(bal_stats)){ - stop("Please provide balance statistics if you wish to run the update version of this function", call. = FALSE) + stop("Please provide balance statistics from the assessBalance() function if you wish to run the update version of this function", call. = FALSE) + } + else if (!is.data.frame(bal_stats)){ + stop("Please provide balance statistics from the assessBalance() function if you wish to run the update version of this function", call. = FALSE) + } if(verbose){ diff --git a/R/createWeights.R b/R/createWeights.R index 0a109c07..888fd3c9 100644 --- a/R/createWeights.R +++ b/R/createWeights.R @@ -5,10 +5,6 @@ #' relevant confounders. #' #' @export -#' @importFrom ggplot2 ggplot -#' @importFrom ggplot2 geom_histogram -#' @importFrom ggplot2 ggsave -#' @importFrom WeightIt weightitMSM #' @seealso {[WeightIt::weightitMSM()], #' } #' @param home_dir path to home directory (required if 'save.out' = TRUE) @@ -71,6 +67,9 @@ createWeights <- function(home_dir, data, exposure, outcome, formulas, method = if (missing(home_dir)) { stop("Please supply a home directory.", call. = FALSE) } + else if(!is.character(home_dir)){ + stop("Please provide a valid home directory path as a string if you wish to save output locally.", call. = FALSE) + } else if(!dir.exists(home_dir)) { stop("Please provide a valid home directory path if you wish to save output locally.", call. = FALSE) } @@ -80,34 +79,56 @@ createWeights <- function(home_dir, data, exposure, outcome, formulas, method = stop("Please supply data as either a dataframe with no missing data or imputed data in the form of a mids object or path to folder with imputed csv datasets.", call. = FALSE) } + else if (!mice::is.mids(data) & !is.data.frame(data) & !inherits(data, "list")) { + stop("Please provide either a 'mids' object, a data frame, or a list of imputed data frames in the 'data' field.", + call. = FALSE) + } + if (missing(exposure)){ stop("Please supply a single exposure.", call. = FALSE) } + else if(!is.character(exposure) | length(exposure) != 1){ + stop("Please supply a single exposure as a character.", call. = FALSE) + } + if (missing(outcome)){ stop("Please supply a single outcome.", call. = FALSE) } + else if(!is.character(outcome) | length(outcome) != 1){ + stop("Please supply a single outcome as a character.", call. = FALSE) + } if (missing(formulas)){ stop("Please supply a list of balancing formulas.", call. = FALSE) } + else if(!inherits(formulas, "list")){ + stop("Please provide a list of formulas for each exposure time point", call. = FALSE) + } if(!inherits(method, "character")){ stop("Please provide as a character string a weights method from this list: 'ps', 'glm', 'gbm', 'bart', 'super', 'cbps'.", call. = FALSE) } - if(! method %in% c("ps", "glm", "gbm", "bart", "super", "cbps")){ + else if(! method %in% c("ps", "glm", "gbm", "bart", "super", "cbps")){ stop("Please provide a weights method from this list: 'ps', 'glm', 'gbm', 'bart', 'super', 'cbps'.", call. = FALSE) } - if (!mice::is.mids(data) & !is.data.frame(data) & !inherits(data, "list")) { - stop("Please provide either a 'mids' object, a data frame, or a list of imputed data frames in the 'data' field.", - call. = FALSE) + if(!is.logical(verbose)){ + stop("Please set verbose to either TRUE or FALSE.", call. = FALSE) + } + else if(length(verbose) != 1){ + stop("Please provide a single TRUE or FALSE value to verbose.", call. = FALSE) } - if(!inherits(formulas, "list")){ - stop("Please provide a list of formulas for each exposure time point", call. = FALSE) + if(!is.logical(save.out)){ + stop("Please set save.out to either TRUE or FALSE.", call. = FALSE) } + else if(length(save.out) != 1){ + stop("Please provide a single TRUE or FALSE value to save.out.", call. = FALSE) + } + + weights_method <- method form_name <- sapply(strsplit(names(formulas[1]), "_form"), "[", 1) @@ -249,9 +270,9 @@ createWeights <- function(home_dir, data, exposure, outcome, formulas, method = if (verbose){ cat(paste0("For imputation", i, " and ", weights_method, - ", weighting method, the median weight value is ", round(median(fit$weights), 2) , - " (SD= ", round(sd(fit$weights), 2), "; range= ", round(min(fit$weights), 2), "-", - round(max(fit$weights), 2), ")."), "\n") + ", weighting method, the median weight value is ", round(median(fit$weights), 2) , + " (SD= ", round(sd(fit$weights), 2), "; range= ", round(min(fit$weights), 2), "-", + round(max(fit$weights), 2), ")."), "\n") cat('\n') } @@ -305,8 +326,8 @@ createWeights <- function(home_dir, data, exposure, outcome, formulas, method = if (verbose){ cat(paste0("For the ", weights_method, " weighting method, the median weight value is ", - round(median(data$weights), 2) , " (SD = ", round(sd(data$weights), 2), "; range = ", - round(min(data$weights), 2), "-", round(max(data$weights), 2), ")."), "\n") + round(median(data$weights), 2) , " (SD = ", round(sd(data$weights), 2), "; range = ", + round(min(data$weights), 2), "-", round(max(data$weights), 2), ")."), "\n") cat('\n') } diff --git a/R/fitModel.R b/R/fitModel.R index 27abfe9e..17f50b56 100644 --- a/R/fitModel.R +++ b/R/fitModel.R @@ -109,6 +109,9 @@ fitModel <- function(home_dir, data, weights, exposure, exposure_time_pts, outco if (missing(home_dir)) { stop("Please supply a home directory.", call. = FALSE) } + else if(!is.character(home_dir)){ + stop("Please provide a valid home directory path as a string if you wish to save output locally.", call. = FALSE) + } else if(!dir.exists(home_dir)) { stop("Please provide a valid home directory path if you wish to save output locally.", call. = FALSE) } @@ -118,27 +121,43 @@ fitModel <- function(home_dir, data, weights, exposure, exposure_time_pts, outco stop("Please supply data as either a dataframe with no missing data or imputed data in the form of a mids object or path to folder with imputed csv datasets.", call. = FALSE) } + else if (!mice::is.mids(data) & !is.data.frame(data) & !inherits(data, "list")) { + stop("Please provide either a 'mids' object, a data frame, or a list of imputed data frames in the 'data' field.", call. = FALSE) + } + if (missing(exposure)){ stop("Please supply a single exposure.", call. = FALSE) } + else if(!is.character(exposure) | length(exposure) != 1){ + stop("Please supply a single exposure as a character.", call. = FALSE) + } + if (missing(outcome)){ stop("Please supply a single outcome.", call. = FALSE) } + else if(!is.character(outcome) | length(outcome) != 1){ + stop("Please supply a single outcome as a character.", call. = FALSE) + } + if (missing(weights)){ stop("Please supply a list of IPTW weights.", call. = FALSE) } + else if (!inherits(weights, "list")){ + stop("Please supply a list of weights output from the createWeights function.", call. = FALSE) + } + if (missing(exposure_time_pts)){ stop("Please supply the exposure time points at which you wish to create weights.", call. = FALSE) } + else if(!is.numeric(exposure_time_pts)){ + stop("Please supply a list of exposure time points as integers.", call. = FALSE) + } + if (missing(model)){ stop('Please provide an outcome model selection "m" from 0-3 (e.g., "m1")', call. = FALSE) } - if (!mice::is.mids(data) & !is.data.frame(data) & !inherits(data, "list")) { - stop("Please provide either a 'mids' object, a data frame, or a list of imputed data frames in the 'data' field.", call. = FALSE) - } - - if (!is.character(model)){ - stop('Please provide as a character string a valid model "m" from 0-3 (e.g., "m1")', call. = FALSE) + else if (!is.character(model) | length(model) != 1){ + stop('Please provide a single outcome model selection "m" from 0-3 (e.g., "m1")', call. = FALSE) } if (!(model %in% c("m0", "m1", "m2", "m3"))) { stop('Please provide a valid model "m" from 0-3 (e.g., "m1")', call. = FALSE) @@ -153,16 +172,38 @@ fitModel <- function(home_dir, data, weights, exposure, exposure_time_pts, outco if(!inherits(family, "function")){ stop("Please provide a valid family in the form of a function (without quotations).", call. = FALSE) } + if(length(family) != 1){ + stop("Please provide a single valid family in the form of a function (without quotations).", call. = FALSE) + } + if(!inherits(link, "character")){ stop("Please provide as a character a valid link function.", call. = FALSE) } + else if(length(link) != 1){ + stop("Please provide as a character a valid link function.", call. = FALSE) + } + if (!is.null(covariates)){ + if(!is.character(covariates)){ + stop("Please provide a list of character strings for covariates.", call. = FALSE) + } if (sum(as.numeric(sapply(strsplit(covariates, "\\."), "[", 2)) > exposure_time_pts[1], na.rm = T) > 0){ warning("Please only include covariates that are time invariant or measured at the first exposure time point.") } } - if (!inherits(weights, "list")){ - stop("Please supply a list of weights output from the createWeights function.", call. = FALSE) + + if(!is.logical(verbose)){ + stop("Please set verbose to either TRUE or FALSE.", call. = FALSE) + } + else if(length(verbose) != 1){ + stop("Please provide a single TRUE or FALSE value to verbose.", call. = FALSE) + } + + if(!is.logical(save.out)){ + stop("Please set save.out to either TRUE or FALSE.", call. = FALSE) + } + else if(length(save.out) != 1){ + stop("Please provide a single TRUE or FALSE value to save.out.", call. = FALSE) } diff --git a/R/trimWeights.R b/R/trimWeights.R index 79906e9a..d3465f35 100644 --- a/R/trimWeights.R +++ b/R/trimWeights.R @@ -63,25 +63,57 @@ trimWeights <- function(home_dir, exposure, outcome, weights, quantile = 0.95, v if (missing(home_dir)) { stop("Please supply a home directory.", call. = FALSE) } + else if(!is.character(home_dir)){ + stop("Please provide a valid home directory path as a string if you wish to save output locally.", call. = FALSE) + } else if(!dir.exists(home_dir)) { stop("Please provide a valid home directory path if you wish to save output locally.", call. = FALSE) } } + if (missing(exposure)){ + stop("Please supply a single exposure.", call. = FALSE) + } + else if(!is.character(exposure) | length(exposure) != 1){ + stop("Please supply a single exposure as a character.", call. = FALSE) + } + if (missing(weights)){ stop("Please supply a list of IPTW weights to trim.", call. = FALSE) } + else if (!inherits(weights, "list")){ + stop("Please supply a list of weights output from the createWeights function.", call. = FALSE) + } + + if (missing(outcome)){ + stop("Please supply a single outcome.", call. = FALSE) + } + else if(!is.character(outcome) | length(outcome) != 1){ + stop("Please supply a single outcome as a character.", call. = FALSE) + } + if(!is.numeric(quantile)){ - stop('Please sprovide a numeric quantile value between 0 and 1.', call. = FALSE) + stop('Please provide a numeric quantile value between 0 and 1.', call. = FALSE) } else if (quantile > 1 || quantile < 0) { stop('Please provide a quantile value between 0 and 1.', call. = FALSE) } - if (!inherits(weights, "list")){ - stop("Please supply a list of weights output from the createWeights function.", call. = FALSE) + if(!is.logical(verbose)){ + stop("Please set verbose to either TRUE or FALSE.", call. = FALSE) + } + else if(length(verbose) != 1){ + stop("Please provide a single TRUE or FALSE value to verbose.", call. = FALSE) } + if(!is.logical(save.out)){ + stop("Please set save.out to either TRUE or FALSE.", call. = FALSE) + } + else if(length(save.out) != 1){ + stop("Please provide a single TRUE or FALSE value to save.out.", call. = FALSE) + } + + if(save.out){ weights_dir <- file.path(home_dir, "weights") if (!dir.exists(weights_dir)) {