diff --git a/DESCRIPTION b/DESCRIPTION index 8c94596fa..de25c80fe 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: parameters Title: Processing of Model Parameters -Version: 0.28.2.5 +Version: 0.28.2.7 Authors@R: c(person(given = "Daniel", family = "Lüdecke", diff --git a/NAMESPACE b/NAMESPACE index 159573895..7b593c795 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -243,11 +243,11 @@ S3method(model_parameters,draws) S3method(model_parameters,emmGrid) S3method(model_parameters,emm_list) S3method(model_parameters,epi.2by2) -S3method(model_parameters,externVar) -S3method(model_parameters,externX) S3method(model_parameters,estimate_contrasts) S3method(model_parameters,estimate_means) S3method(model_parameters,estimate_slopes) +S3method(model_parameters,externVar) +S3method(model_parameters,externX) S3method(model_parameters,fa) S3method(model_parameters,fa.ci) S3method(model_parameters,feglm) @@ -360,6 +360,7 @@ S3method(model_parameters,summary_emm) S3method(model_parameters,survfit) S3method(model_parameters,svy2lme) S3method(model_parameters,svyglm) +S3method(model_parameters,svyolr) S3method(model_parameters,svytable) S3method(model_parameters,systemfit) S3method(model_parameters,t1way) @@ -803,11 +804,11 @@ S3method(standard_error,draws) S3method(standard_error,effectsize_table) S3method(standard_error,emmGrid) S3method(standard_error,emm_list) -S3method(standard_error,externVar) -S3method(standard_error,externX) S3method(standard_error,estimate_contrasts) S3method(standard_error,estimate_means) S3method(standard_error,estimate_slopes) +S3method(standard_error,externVar) +S3method(standard_error,externX) S3method(standard_error,factor) S3method(standard_error,feglm) S3method(standard_error,fitdistr) diff --git a/R/1_model_parameters.R b/R/1_model_parameters.R index fc632fe92..ec9cdd4b9 100644 --- a/R/1_model_parameters.R +++ b/R/1_model_parameters.R @@ -396,7 +396,6 @@ model_parameters <- function(model, ...) { # DF naming convention -------------------- - # DF column naming # F has df, df_error # t has df_error @@ -404,7 +403,6 @@ model_parameters <- function(model, ...) { # Chisq has df # https://github.com/easystats/parameters/issues/455 - # Options ------------------------------------- # Add new options to the docs in "print.parameters_model" @@ -417,7 +415,6 @@ model_parameters <- function(model, ...) { # getOption("parameters_interaction"): separator char for interactions # getOption("parameters_select"): default for the `select` argument - #' @rdname model_parameters #' @export parameters <- model_parameters @@ -578,21 +575,23 @@ parameters <- model_parameters #' } #' @return A data frame of indices related to the model's parameters. #' @export -model_parameters.default <- function(model, - ci = 0.95, - ci_method = NULL, - bootstrap = FALSE, - iterations = 1000, - standardize = NULL, - exponentiate = FALSE, - p_adjust = NULL, - vcov = NULL, - vcov_args = NULL, - include_info = getOption("parameters_info", FALSE), - keep = NULL, - drop = NULL, - verbose = TRUE, - ...) { +model_parameters.default <- function( + model, + ci = 0.95, + ci_method = NULL, + bootstrap = FALSE, + iterations = 1000, + standardize = NULL, + exponentiate = FALSE, + p_adjust = NULL, + vcov = NULL, + vcov_args = NULL, + include_info = getOption("parameters_info", FALSE), + keep = NULL, + drop = NULL, + verbose = TRUE, + ... +) { # validation check for inputs .is_model_valid(model) @@ -642,13 +641,11 @@ model_parameters.default <- function(model, attr(out, "error") ) } else if (is.null(out)) { - insight::format_error( - paste0( - "Sorry, `model_parameters()` does not currently work for objects of class `", - class(model)[1], - "`." - ) - ) + insight::format_error(paste0( + "Sorry, `model_parameters()` does not currently work for objects of class `", + class(model)[1], + "`." + )) } } @@ -657,24 +654,26 @@ model_parameters.default <- function(model, # including a bunch of attributes required for further processing # (like printing etc.) -.model_parameters_generic <- function(model, - ci = 0.95, - bootstrap = FALSE, - iterations = 1000, - merge_by = "Parameter", - standardize = NULL, - exponentiate = FALSE, - effects = "fixed", - component = "conditional", - ci_method = NULL, - p_adjust = NULL, - include_info = FALSE, - keep_parameters = NULL, - drop_parameters = NULL, - vcov = NULL, - vcov_args = NULL, - verbose = TRUE, - ...) { +.model_parameters_generic <- function( + model, + ci = 0.95, + bootstrap = FALSE, + iterations = 1000, + merge_by = "Parameter", + standardize = NULL, + exponentiate = FALSE, + effects = "fixed", + component = "conditional", + ci_method = NULL, + p_adjust = NULL, + include_info = FALSE, + keep_parameters = NULL, + drop_parameters = NULL, + vcov = NULL, + vcov_args = NULL, + verbose = TRUE, + ... +) { dots <- list(...) out <- tryCatch( @@ -688,12 +687,7 @@ model_parameters.default <- function(model, ci_method <- "quantile" } - fun_args <- list( - model, - iterations = iterations, - ci = ci, - ci_method = ci_method - ) + fun_args <- list(model, iterations = iterations, ci = ci, ci_method = ci_method) fun_args <- c(fun_args, dots) params <- do.call("bootstrap_parameters", fun_args) @@ -723,13 +717,11 @@ model_parameters.default <- function(model, params <- do.call(".extract_parameters_generic", fun_args) } - # ==== 2. second step, exponentiate ------- # exponentiate coefficients and SE/CI, if requested params <- .exponentiate_parameters(params, model, exponentiate) - # ==== 3. third step, add information as attributes ------- # add further information as attributes @@ -766,30 +758,31 @@ model_parameters.default <- function(model, #################### .glm ---------------------- - #' @export -model_parameters.glm <- function(model, - ci = 0.95, - ci_method = NULL, - bootstrap = FALSE, - iterations = 1000, - standardize = NULL, - exponentiate = FALSE, - p_adjust = NULL, - vcov = NULL, - vcov_args = NULL, - include_info = getOption("parameters_info", FALSE), - keep = NULL, - drop = NULL, - verbose = TRUE, - ...) { +model_parameters.glm <- function( + model, + ci = 0.95, + ci_method = NULL, + bootstrap = FALSE, + iterations = 1000, + standardize = NULL, + exponentiate = FALSE, + p_adjust = NULL, + vcov = NULL, + vcov_args = NULL, + include_info = getOption("parameters_info", FALSE), + keep = NULL, + drop = NULL, + verbose = TRUE, + ... +) { dots <- list(...) # set default if (is.null(ci_method)) { if (isTRUE(bootstrap)) { ci_method <- "quantile" - } else if (!is.null(vcov) || !is.null(vcov_args)) { + } else if (!is.null(vcov) || !is.null(vcov_args) || inherits(model, "svyolr")) { ci_method <- "wald" } else { ci_method <- "profile" @@ -805,7 +798,11 @@ model_parameters.glm <- function(model, } # tell user that profiled CIs don't respect vcov-args - if (identical(ci_method, "profile") && (!is.null(vcov) || !is.null(vcov_args)) && isTRUE(verbose)) { + if ( + identical(ci_method, "profile") && + (!is.null(vcov) || !is.null(vcov_args)) && + isTRUE(verbose) + ) { insight::format_alert( "When `ci_method=\"profile\"`, `vcov` only modifies standard errors, test-statistic and p-values, but not confidence intervals.", # nolint "Use `ci_method=\"wald\"` to return confidence intervals based on robust standard errors." diff --git a/R/dof.R b/R/dof.R index fb777adde..cb84690aa 100644 --- a/R/dof.R +++ b/R/dof.R @@ -80,7 +80,8 @@ dof <- degrees_of_freedom method <- tolower(method) # exceptions 1 - if (inherits(model, c("polr", "glm", "svyglm"))) { + if (inherits(model, c("polr", "glm", "svyglm", "svyolr"))) { + # fmt: skip if (method %in% c( "analytical", "any", "fit", "profile", "residual", "wald", "nokr", "likelihood", "normal" @@ -96,11 +97,17 @@ dof <- degrees_of_freedom # exceptions 2 if (inherits(model, c("phylolm", "phyloglm"))) { - if (method %in% c("analytical", "any", "fit", "residual", "wald", "nokr", "normal", "boot")) { + if ( + method %in% + c("analytical", "any", "fit", "residual", "wald", "nokr", "normal", "boot") + ) { return(TRUE) } else { if (verbose) { - insight::format_alert(sprintf("`%s` must be one of \"wald\", \"normal\" or \"boot\". Using \"wald\" now.", type)) # nolint + insight::format_alert(sprintf( + "`%s` must be one of \"wald\", \"normal\" or \"boot\". Using \"wald\" now.", + type + )) } return(FALSE) } @@ -109,31 +116,52 @@ dof <- degrees_of_freedom info <- insight::model_info(model, verbose = FALSE) if (!is.null(info) && isFALSE(info$is_mixed) && method == "boot") { if (verbose) { - insight::format_alert(sprintf("`%s=boot` only works for mixed models of class `merMod`. To bootstrap this model, use `bootstrap=TRUE, ci_method=\"bcai\"`.", type)) # nolint + insight::format_alert(sprintf( + "`%s=boot` only works for mixed models of class `merMod`. To bootstrap this model, use `bootstrap=TRUE, ci_method=\"bcai\"`.", + type + )) } return(TRUE) } + # fmt: skip if (is.null(info) || !info$is_mixed) { - if (!(method %in% c("analytical", "any", "fit", "betwithin", "nokr", "wald", "ml1", "profile", "boot", "uniroot", "residual", "normal"))) { # nolint + if (!(method %in% c( + "analytical", "any", "fit", "betwithin", "nokr", "wald", "ml1", + "profile", "boot", "uniroot", "residual", "normal" + ))) { if (verbose) { - insight::format_alert(sprintf("`%s` must be one of \"residual\", \"wald\", \"normal\", \"profile\", \"boot\", \"uniroot\", \"betwithin\" or \"ml1\". Using \"wald\" now.", type)) # nolint + insight::format_alert(sprintf( + "`%s` must be one of \"residual\", \"wald\", \"normal\", \"profile\", \"boot\", \"uniroot\", \"betwithin\" or \"ml1\". Using \"wald\" now.", + type + )) } return(FALSE) } return(TRUE) } - if (!(method %in% c("analytical", "any", "fit", "satterthwaite", "betwithin", "kenward", "kr", "nokr", "wald", "ml1", "profile", "boot", "uniroot", "residual", "normal"))) { # nolint + # fmt: skip + if (!(method %in% c( + "analytical", "any", "fit", "satterthwaite", "betwithin", "kenward", + "kr", "nokr", "wald", "ml1", "profile", "boot", "uniroot", "residual", + "normal" + ))) { if (verbose) { - insight::format_alert(sprintf("`%s` must be one of \"residual\", \"wald\", \"normal\", \"profile\", \"boot\", \"uniroot\", \"kenward\", \"satterthwaite\", \"betwithin\" or \"ml1\". Using \"wald\" now.", type)) # nolint + insight::format_alert(sprintf( + "`%s` must be one of \"residual\", \"wald\", \"normal\", \"profile\", \"boot\", \"uniroot\", \"kenward\", \"satterthwaite\", \"betwithin\" or \"ml1\". Using \"wald\" now.", + type + )) } return(FALSE) - } +} if (!info$is_linear && method %in% c("satterthwaite", "kenward", "kr")) { if (verbose) { - insight::format_alert(sprintf("`%s`-degrees of freedoms are only available for linear mixed models.", method)) + insight::format_alert(sprintf( + "`%s`-degrees of freedoms are only available for linear mixed models.", + method + )) } return(FALSE) } diff --git a/R/extract_parameters.R b/R/extract_parameters.R index 347203320..cb4855446 100644 --- a/R/extract_parameters.R +++ b/R/extract_parameters.R @@ -80,9 +80,10 @@ # ==== for ordinal models, first, clean parameter names and then indicate # intercepts (alpha-coefficients) in the component column - if (inherits(model, "polr")) { + if (inherits(model, c("polr", "svyolr"))) { intercept_groups <- grep("Intercept:", parameters$Parameter, fixed = TRUE) parameters$Parameter <- gsub("Intercept: ", "", parameters$Parameter, fixed = TRUE) + statistic$Parameter <- gsub("Intercept: ", "", statistic$Parameter, fixed = TRUE) } else if (inherits(model, "clm") && !is.null(model$alpha)) { intercept_groups <- rep( c("intercept", "location", "scale"), @@ -190,7 +191,9 @@ } else { df_error <- insight::get_df(x = model, type = ci_method, verbose = FALSE) } - if (!is.null(df_error) && (length(df_error) == 1 || length(df_error) == nrow(parameters))) { + if ( + !is.null(df_error) && (length(df_error) == 1 || length(df_error) == nrow(parameters)) + ) { if (length(df_error) == 1) { parameters$df_error <- df_error } else { @@ -215,18 +218,30 @@ colnames(parameters), fixed = TRUE ) - colnames(parameters) <- gsub("chi-squared", "Chi2", colnames(parameters), fixed = TRUE) + colnames(parameters) <- gsub( + "chi-squared", + "Chi2", + colnames(parameters), + fixed = TRUE + ) } } colnames(parameters) <- gsub("(c|C)hisq", "Chi2", colnames(parameters)) - colnames(parameters) <- gsub("Estimate", "Coefficient", colnames(parameters), fixed = TRUE) + colnames(parameters) <- gsub( + "Estimate", + "Coefficient", + colnames(parameters), + fixed = TRUE + ) # ==== add intercept groups for ordinal models - if (inherits(model, "polr") && !is.null(intercept_groups)) { + if (inherits(model, c("polr", "svyolr")) && !is.null(intercept_groups)) { parameters$Component <- "beta" parameters$Component[intercept_groups] <- "alpha" - } else if (inherits(model, c("clm", "clm2", "ordinal_weightit")) && !is.null(intercept_groups)) { + } else if ( + inherits(model, c("clm", "clm2", "ordinal_weightit")) && !is.null(intercept_groups) + ) { parameters$Component <- intercept_groups } @@ -318,7 +333,6 @@ # helper ---------------- - .add_sigma_residual_df <- function(params, model) { if (is.null(params$Component) || !"sigma" %in% params$Component) { sig <- .safe(suppressWarnings(insight::get_sigma(model, ci = NULL, verbose = FALSE))) @@ -334,7 +348,8 @@ .filter_parameters <- function(params, keep = NULL, drop = NULL, verbose = TRUE) { if (!is.null(keep) && is.list(keep)) { for (i in names(keep)) { - params <- .filter_parameters_vector(params, + params <- .filter_parameters_vector( + params, keep[[i]], drop = NULL, column = i, @@ -342,7 +357,8 @@ ) } } else { - params <- .filter_parameters_vector(params, + params <- .filter_parameters_vector( + params, keep, drop, column = NULL, @@ -353,18 +369,21 @@ } -.filter_parameters_vector <- function(params, - keep = NULL, - drop = NULL, - column = NULL, - verbose = TRUE) { +.filter_parameters_vector <- function( + params, + keep = NULL, + drop = NULL, + column = NULL, + verbose = TRUE +) { # check pattern if (!is.null(keep) && length(keep) > 1) { keep <- paste0("(", paste(keep, collapse = "|"), ")") if (verbose) { - insight::format_alert( - sprintf("The `keep` argument has more than 1 element. Merging into following regular expression: `%s`.", keep) - ) + insight::format_alert(sprintf( + "The `keep` argument has more than 1 element. Merging into following regular expression: `%s`.", + keep + )) } } @@ -372,9 +391,10 @@ if (!is.null(drop) && length(drop) > 1) { drop <- paste0("(", paste(drop, collapse = "|"), ")") if (verbose) { - insight::format_alert( - sprintf("The `drop` argument has more than 1 element. Merging into following regular expression: `%s`.", drop) - ) + insight::format_alert(sprintf( + "The `drop` argument has more than 1 element. Merging into following regular expression: `%s`.", + drop + )) } } @@ -399,7 +419,6 @@ rows_to_drop <- !grepl(drop, params[[column]], perl = TRUE) } - out <- params[rows_to_keep & rows_to_drop, ] if (nrow(out) == 0) { @@ -417,28 +436,34 @@ # mixed models function ------------------------------------------------------ - #' @keywords internal -.extract_parameters_mixed <- function(model, - ci = 0.95, - ci_method = "wald", - standardize = NULL, - p_adjust = NULL, - wb_component = FALSE, - keep_parameters = NULL, - drop_parameters = NULL, - include_sigma = FALSE, - include_info = FALSE, - vcov = NULL, - vcov_args = NULL, - verbose = TRUE, - ...) { +.extract_parameters_mixed <- function( + model, + ci = 0.95, + ci_method = "wald", + standardize = NULL, + p_adjust = NULL, + wb_component = FALSE, + keep_parameters = NULL, + drop_parameters = NULL, + include_sigma = FALSE, + include_info = FALSE, + vcov = NULL, + vcov_args = NULL, + verbose = TRUE, + ... +) { dots <- list(...) special_ci_methods <- c("betwithin", "satterthwaite", "ml1", "kenward", "kr") # get parameters and statistic - parameters <- insight::get_parameters(model, effects = "fixed", component = "all", verbose = FALSE) + parameters <- insight::get_parameters( + model, + effects = "fixed", + component = "all", + verbose = FALSE + ) statistic <- insight::get_statistic(model, component = "all") # check if all estimates are non-NA @@ -448,12 +473,15 @@ original_order <- parameters$.id <- seq_len(nrow(parameters)) # remove SE column - parameters <- datawizard::data_remove(parameters, c("SE", "Std. Error"), verbose = FALSE) + parameters <- datawizard::data_remove( + parameters, + c("SE", "Std. Error"), + verbose = FALSE + ) # column name for coefficients, non-standardized coef_col <- "Coefficient" - # Degrees of freedom if (.dof_method_ok(model, ci_method)) { dof <- insight::get_df(x = model, type = ci_method, verbose = FALSE) @@ -469,14 +497,14 @@ # for KR-dof, we have the SE as well, to save computation time df_error$SE <- attr(dof, "se", exact = TRUE) - # CI - only if we don't already have CI for std. parameters ci_cols <- NULL if (!is.null(ci)) { # HC vcov? if (!is.null(vcov)) { - fun_args <- list(model, + fun_args <- list( + model, ci = ci, vcov = vcov, vcov_args = vcov_args, @@ -497,15 +525,10 @@ parameters <- merge(parameters, ci_df, by = "Parameter", sort = FALSE) } - # standard error - only if we don't already have SE for std. parameters if (!"SE" %in% colnames(parameters)) { if (!is.null(vcov)) { - fun_args <- list(model, - vcov = vcov, - vcov_args = vcov_args, - verbose = verbose - ) + fun_args <- list(model, vcov = vcov, vcov_args = vcov_args, verbose = verbose) fun_args <- c(fun_args, dots) parameters <- merge( parameters, @@ -528,14 +551,9 @@ } } - # p value if (!is.null(vcov)) { - fun_args <- list(model, - vcov = vcov, - vcov_args = vcov_args, - verbose = verbose - ) + fun_args <- list(model, vcov = vcov, vcov_args = vcov_args, verbose = verbose) fun_args <- c(fun_args, dots) parameters <- merge( parameters, @@ -562,7 +580,6 @@ ) } - # adjust standard errors and test-statistic as well if ((!is.null(vcov) || ci_method %in% special_ci_methods)) { parameters$Statistic <- parameters$Estimate / parameters$SE @@ -570,7 +587,6 @@ parameters <- merge(parameters, statistic, by = "Parameter", sort = FALSE) } - # dof if (!"df" %in% names(parameters)) { if (!ci_method %in% special_ci_methods) { @@ -589,7 +605,6 @@ } } - # Rematch order after merging parameters <- parameters[match(original_order, parameters$.id), ] @@ -601,13 +616,19 @@ fixed = TRUE ) colnames(parameters) <- gsub("Std. Error", "SE", colnames(parameters), fixed = TRUE) - colnames(parameters) <- gsub("Estimate", "Coefficient", colnames(parameters), fixed = TRUE) + colnames(parameters) <- gsub( + "Estimate", + "Coefficient", + colnames(parameters), + fixed = TRUE + ) colnames(parameters) <- gsub("t value", "t", colnames(parameters), fixed = TRUE) colnames(parameters) <- gsub("z value", "z", colnames(parameters), fixed = TRUE) # filter parameters, if requested if (!is.null(keep_parameters) || !is.null(drop_parameters)) { - parameters <- .filter_parameters(parameters, + parameters <- .filter_parameters( + parameters, keep = keep_parameters, drop = drop_parameters, verbose = verbose @@ -645,19 +666,18 @@ } # Reorder + # fmt: skip col_order <- c( "Parameter", coef_col, "SE", ci_cols, "t", "z", "df", "df_error", "p", "Component" ) parameters <- parameters[col_order[col_order %in% colnames(parameters)]] - # add sigma if (isTRUE(include_sigma) || isTRUE(include_info)) { parameters <- .add_sigma_residual_df(parameters, model) } - rownames(parameters) <- NULL parameters } @@ -684,22 +704,18 @@ } if (!is.null(within_effects)) { - index <- unique(unlist(sapply( - within_effects, - grep, - x = parameters$Parameter, - fixed = TRUE - ), use.names = FALSE)) + index <- unique(unlist( + sapply(within_effects, grep, x = parameters$Parameter, fixed = TRUE), + use.names = FALSE + )) parameters$Component[index] <- "within" } if (!is.null(between_effects)) { - index <- unique(unlist(sapply( - between_effects, - grep, - x = parameters$Parameter, - fixed = TRUE - ), use.names = FALSE)) + index <- unique(unlist( + sapply(between_effects, grep, x = parameters$Parameter, fixed = TRUE), + use.names = FALSE + )) parameters$Component[index] <- "between" } @@ -708,8 +724,11 @@ parameters$Component[interactions] <- "interactions" } - if (((!all(c("within", "between") %in% parameters$Component)) && inherits(model, "merMod")) || - all(parameters$Component == "rewb-contextual")) { + if ( + ((!all(c("within", "between") %in% parameters$Component)) && + inherits(model, "merMod")) || + all(parameters$Component == "rewb-contextual") + ) { parameters$Component <- NULL } @@ -719,34 +738,38 @@ .find_within_between <- function(model, which_effect) { mf <- stats::model.frame(model) - unlist(sapply(names(mf), function(i) { - if (!is.null(attr(mf[[i]], which_effect, exact = TRUE))) { - i - } - }), use.names = FALSE) + unlist( + sapply(names(mf), function(i) { + if (!is.null(attr(mf[[i]], which_effect, exact = TRUE))) { + i + } + }), + use.names = FALSE + ) } # Bayes function ------------------------------------------------------ - #' @keywords internal -.extract_parameters_bayesian <- function(model, - centrality = "median", - dispersion = FALSE, - ci = 0.95, - ci_method = "eti", - test = "pd", - rope_range = "default", - rope_ci = 0.95, - bf_prior = NULL, - diagnostic = c("ESS", "Rhat"), - priors = FALSE, - standardize = NULL, - keep_parameters = NULL, - drop_parameters = NULL, - verbose = TRUE, - ...) { +.extract_parameters_bayesian <- function( + model, + centrality = "median", + dispersion = FALSE, + ci = 0.95, + ci_method = "eti", + test = "pd", + rope_range = "default", + rope_ci = 0.95, + bf_prior = NULL, + diagnostic = c("ESS", "Rhat"), + priors = FALSE, + standardize = NULL, + keep_parameters = NULL, + drop_parameters = NULL, + verbose = TRUE, + ... +) { # no ROPE for multi-response models if (insight::is_multivariate(model) && any(c("rope", "p_rope") %in% test)) { test <- setdiff(test, c("rope", "p_rope")) @@ -811,7 +834,10 @@ parameters <- merge( std_parameters, - parameters[c("Parameter", setdiff(colnames(parameters), colnames(std_parameters)))], + parameters[c( + "Parameter", + setdiff(colnames(parameters), colnames(std_parameters)) + )], sort = FALSE ) } @@ -822,10 +848,18 @@ } # Remove unnecessary columns - if ("CI" %in% names(parameters) && insight::has_single_value(parameters$CI, remove_na = TRUE)) { + if ( + "CI" %in% + names(parameters) && + insight::has_single_value(parameters$CI, remove_na = TRUE) + ) { parameters$CI <- NULL } - if ("ROPE_CI" %in% names(parameters) && insight::has_single_value(parameters$ROPE_CI, remove_na = TRUE)) { + if ( + "ROPE_CI" %in% + names(parameters) && + insight::has_single_value(parameters$ROPE_CI, remove_na = TRUE) + ) { parameters$ROPE_CI <- NULL } if ("ROPE_low" %in% names(parameters) && "ROPE_high" %in% names(parameters)) { @@ -835,7 +869,8 @@ # filter parameters, if requested if (!is.null(keep_parameters) || !is.null(drop_parameters)) { - parameters <- .filter_parameters(parameters, + parameters <- .filter_parameters( + parameters, keep = keep_parameters, drop = drop_parameters, verbose = verbose @@ -852,18 +887,18 @@ # SEM function ------------------------------------------------------ - #' @keywords internal -.extract_parameters_lavaan <- function(model, - ci = 0.95, - standardize = FALSE, - keep_parameters = NULL, - drop_parameters = NULL, - verbose = TRUE, - ...) { +.extract_parameters_lavaan <- function( + model, + ci = 0.95, + standardize = FALSE, + keep_parameters = NULL, + drop_parameters = NULL, + verbose = TRUE, + ... +) { insight::check_if_installed("lavaan") - # lavaan::parameterEstimates does not accept NULL `level`, but a lot of our # other methods do. It is often useful to pass `NULL` to speed things up, # but it doesn't work here. @@ -892,9 +927,11 @@ if (length(ci) > 1L) { ci <- ci[1] if (verbose) { - insight::format_alert( - paste0("lavaan models only accept one level of CI. Keeping the first one: `ci = ", ci, "`.") - ) + insight::format_alert(paste0( + "lavaan models only accept one level of CI. Keeping the first one: `ci = ", + ci, + "`." + )) } } @@ -902,6 +939,7 @@ dot_args <- list(...) # list all argument names from the `lavaan` function + # fmt: skip dot_args <- dot_args[names(dot_args) %in% c( "zstat", "pvalue", "standardized", "fmi", "level", "boot.ci.type", "cov.std", "fmi.options", "rsquare", "remove.system.eq", "remove.eq", "remove.ineq", @@ -911,10 +949,7 @@ # Get estimates sem_data <- do.call( lavaan::parameterEstimates, - c( - list(object = model, se = TRUE, ci = TRUE, level = ci), - dot_args - ) + c(list(object = model, se = TRUE, ci = TRUE, level = ci), dot_args) ) label <- sem_data$label @@ -925,7 +960,8 @@ standardize <- "all" } - type <- switch(standardize, + type <- switch( + standardize, all = , std.all = "std.all", latent = , @@ -945,7 +981,6 @@ names(sem_data)[names(sem_data) == "est.std"] <- "est" } - params <- data.frame( To = sem_data$lhs, Operator = sem_data$op, @@ -982,7 +1017,8 @@ # filter parameters, if requested if (!is.null(keep_parameters) || !is.null(drop_parameters)) { - params <- .filter_parameters(params, + params <- .filter_parameters( + params, keep = keep_parameters, drop = drop_parameters, verbose = verbose @@ -995,7 +1031,6 @@ # tools ------------------------- - .check_rank_deficiency <- function(model, p, verbose = TRUE) { # for cox-panel models, we have non-linear parameters with NA coefficient, # but test statistic and p-value - don't check for NA estimates in this case @@ -1004,12 +1039,10 @@ } if (anyNA(p$Estimate)) { if (isTRUE(verbose)) { - insight::format_alert( - sprintf( - "Model matrix is rank deficient. Parameters `%s` were not estimable.", - toString(p$Parameter[is.na(p$Estimate)]) - ) - ) + insight::format_alert(sprintf( + "Model matrix is rank deficient. Parameters `%s` were not estimable.", + toString(p$Parameter[is.na(p$Estimate)]) + )) } p <- p[!is.na(p$Estimate), ] } diff --git a/R/format_parameters.R b/R/format_parameters.R index 84a80766b..9e21d599f 100644 --- a/R/format_parameters.R +++ b/R/format_parameters.R @@ -64,9 +64,17 @@ format_parameters.parameters_model <- function(model, ...) { # Utilities --------------------------------------------------------------- - -.format_parameter_default <- function(model, effects = "fixed", brackets = c("[", "]"), ...) { - original_names <- parameter_names <- insight::find_parameters(model, effects = effects, flatten = TRUE) +.format_parameter_default <- function( + model, + effects = "fixed", + brackets = c("[", "]"), + ... +) { + original_names <- parameter_names <- insight::find_parameters( + model, + effects = effects, + flatten = TRUE + ) # save some time, if model info is passed as argument dot_args <- list(...) @@ -83,11 +91,14 @@ format_parameters.parameters_model <- function(model, ...) { # quick fix, for multivariate response models, we use # info from first model only - if (insight::is_multivariate(model) && !"is_zero_inflated" %in% names(info) && !inherits(model, c("vgam", "vglm"))) { + if ( + insight::is_multivariate(model) && + !"is_zero_inflated" %in% names(info) && + !inherits(model, c("vgam", "vglm")) + ) { info <- info[[1]] } - # Type-specific changes types <- parameters_type(model) if (is.null(types)) { @@ -95,7 +106,6 @@ format_parameters.parameters_model <- function(model, ...) { } types$Parameter <- .clean_parameter_names(types$Parameter, full = TRUE) - # special handling hurdle- and zeroinfl-models --------------------- if (isTRUE(info$is_zero_inflated) || isTRUE(info$is_hurdle)) { parameter_names <- gsub("^(count_|zero_)", "", parameter_names) @@ -103,7 +113,7 @@ format_parameters.parameters_model <- function(model, ...) { } # special handling polr --------------------- - if (inherits(model, "polr")) { + if (inherits(model, c("polr", "svyolr"))) { original_names <- gsub("Intercept: ", "", original_names, fixed = TRUE) parameter_names <- gsub("Intercept: ", "", parameter_names, fixed = TRUE) } @@ -121,7 +131,11 @@ format_parameters.parameters_model <- function(model, ...) { pattern <- paste0("(", paste(model$varnames, collapse = "|"), ")\\.(.*)") dirich_names <- parameter_names <- gsub(pattern, "\\2", names(unlist(cf))) } else { - dirich_names <- parameter_names <- gsub("(.*)\\.(.*)\\.(.*)", "\\3", names(unlist(cf))) + dirich_names <- parameter_names <- gsub( + "(.*)\\.(.*)\\.(.*)", + "\\3", + names(unlist(cf)) + ) } original_names <- parameter_names if (!is.null(dirich_names)) { @@ -129,11 +143,9 @@ format_parameters.parameters_model <- function(model, ...) { } } - # remove "as.factor()", "log()" etc. from parameter names parameter_names <- .clean_parameter_names(parameter_names) - for (i in seq_len(nrow(types))) { name <- types$Parameter[i] @@ -161,7 +173,9 @@ format_parameters.parameters_model <- function(model, ...) { # variable for each response, thus we have multiple rows here, # where only one row is required. - if (nrow(type) > 1) type <- type[1, ] + if (nrow(type) > 1) { + type <- type[1, ] + } components[j] <- .format_parameter( components[j], @@ -171,7 +185,10 @@ format_parameters.parameters_model <- function(model, ...) { brackets = brackets ) } else if (components[j] %in% types$Secondary_Parameter) { - type <- types[!is.na(types$Secondary_Parameter) & types$Secondary_Parameter == components[j], ] + type <- types[ + !is.na(types$Secondary_Parameter) & + types$Secondary_Parameter == components[j], + ] components[j] <- .format_parameter( components[j], variable = type[1, ]$Secondary_Variable, @@ -227,27 +244,54 @@ format_parameters.parameters_model <- function(model, ...) { # Polynomials if (type %in% c("poly", "poly_raw")) { - name <- .format_poly(name = name, variable = variable, type = type, degree = level, brackets = brackets) + name <- .format_poly( + name = name, + variable = variable, + type = type, + degree = level, + brackets = brackets + ) } # Splines if (type == "spline") { - name <- .format_poly(name = name, variable = variable, type = type, degree = level, brackets = brackets) + name <- .format_poly( + name = name, + variable = variable, + type = type, + degree = level, + brackets = brackets + ) } # log-transformation if (type == "logarithm") { - name <- .format_log(name = name, variable = variable, type = type, brackets = brackets) + name <- .format_log( + name = name, + variable = variable, + type = type, + brackets = brackets + ) } # exp-transformation if (type == "exponentiation") { - name <- .format_log(name = name, variable = variable, type = type, brackets = brackets) + name <- .format_log( + name = name, + variable = variable, + type = type, + brackets = brackets + ) } # log-transformation if (type == "squareroot") { - name <- .format_log(name = name, variable = variable, type = type, brackets = brackets) + name <- .format_log( + name = name, + variable = variable, + type = type, + brackets = brackets + ) } # As Is @@ -271,12 +315,14 @@ format_parameters.parameters_model <- function(model, ...) { #' @keywords internal -.format_interaction <- function(components, - type, - is_nested = FALSE, - is_simple = FALSE, - interaction_mark = NULL, - ...) { +.format_interaction <- function( + components, + type, + is_nested = FALSE, + is_simple = FALSE, + interaction_mark = NULL, + ... +) { # sep <- ifelse(is_nested | is_simple, " : ", " * ") # sep <- ifelse(is_nested, " / ", " * ") # sep <- ifelse(is_simple, " : ", ifelse(is_nested, " / ", " * ")) @@ -337,7 +383,14 @@ format_parameters.parameters_model <- function(model, ...) { #' @keywords internal .format_poly <- function(name, variable, type, degree, brackets = c("[", "]")) { - paste0(variable, " ", brackets[1], format_order(as.numeric(degree), textual = FALSE), " degree", brackets[2]) + paste0( + variable, + " ", + brackets[1], + format_order(as.numeric(degree), textual = FALSE), + " degree", + brackets[2] + ) } @@ -349,13 +402,17 @@ format_parameters.parameters_model <- function(model, ...) { #' @keywords internal .format_ordered <- function(degree, brackets = c("[", "]")) { - switch(degree, + switch( + degree, .L = paste0(brackets[1], "linear", brackets[2]), .Q = paste0(brackets[1], "quadratic", brackets[2]), .C = paste0(brackets[1], "cubic", brackets[2]), paste0( brackets[1], - parameters::format_order(as.numeric(gsub("^", "", degree, fixed = TRUE)), textual = FALSE), + parameters::format_order( + as.numeric(gsub("^", "", degree, fixed = TRUE)), + textual = FALSE + ), " degree", brackets[2] ) @@ -376,7 +433,11 @@ format_parameters.parameters_model <- function(model, ...) { # get data, but exclude response - we have no need for that label mf <- insight::get_data(model, source = "mf", verbose = FALSE) # sanity check - any labels? - has_labels <- vapply(mf, function(i) !is.null(attr(i, "labels", exact = TRUE)), logical(1)) + has_labels <- vapply( + mf, + function(i) !is.null(attr(i, "labels", exact = TRUE)), + logical(1) + ) # if we don't have labels, we try to get data from environment if (!any(has_labels)) { mf <- insight::get_data(model, source = "environment", verbose = FALSE) @@ -447,7 +508,10 @@ format_parameters.parameters_model <- function(model, ...) { # extract single coefficient names from interaction term out <- unlist(strsplit(i, ":", fixed = TRUE)) # combine labels - labs <- c(labs, paste(sapply(out, function(l) pretty_labels[l]), collapse = " * ")) + labs <- c( + labs, + paste(sapply(out, function(l) pretty_labels[l]), collapse = " * ") + ) } # add interaction terms to labels string names(labs) <- names(interactions) @@ -486,5 +550,6 @@ format_parameters.parameters_model <- function(model, ...) { TRUE } ) - l10n_info()[["UTF-8"]] && ((win_os && getRversion() >= "4.2") || (!win_os && getRversion() >= "4.0")) + l10n_info()[["UTF-8"]] && + ((win_os && getRversion() >= "4.2") || (!win_os && getRversion() >= "4.0")) } diff --git a/R/methods_survey.R b/R/methods_survey.R index 4e431b533..c16b6ba66 100644 --- a/R/methods_survey.R +++ b/R/methods_survey.R @@ -1,17 +1,19 @@ # model_parameters ----------------------------------------- #' @export -model_parameters.svyglm <- function(model, - ci = 0.95, - ci_method = "wald", - standardize = NULL, - exponentiate = FALSE, - p_adjust = NULL, - include_info = getOption("parameters_info", FALSE), - keep = NULL, - drop = NULL, - verbose = TRUE, - ...) { +model_parameters.svyglm <- function( + model, + ci = 0.95, + ci_method = "wald", + standardize = NULL, + exponentiate = FALSE, + p_adjust = NULL, + include_info = getOption("parameters_info", FALSE), + keep = NULL, + drop = NULL, + verbose = TRUE, + ... +) { if (insight::n_obs(model) > 1e4 && ci_method == "likelihood") { insight::format_alert( "Likelihood confidence intervals may take longer time to compute. Use 'ci_method=\"wald\"' for faster computation of CIs." # nolint @@ -45,6 +47,9 @@ model_parameters.svyglm <- function(model, out } +#' @export +model_parameters.svyolr <- model_parameters.glm + # simulate_model ----------------------------------------- @@ -64,10 +69,7 @@ standard_error.svyglm.nb <- function(model, ...) { requireNamespace("survey", quietly = TRUE) } se <- sqrt(diag(stats::vcov(model, stderr = "robust"))) - .data_frame( - Parameter = .remove_backticks_from_string(names(se)), - SE = as.vector(se) - ) + .data_frame(Parameter = .remove_backticks_from_string(names(se)), SE = as.vector(se)) } @@ -86,7 +88,7 @@ standard_error.svyglm <- function(model, ...) { #' @export -standard_error.svyolr <- standard_error.svyglm +standard_error.svyolr <- standard_error.polr # confidence intervals ----------------------------------- @@ -106,7 +108,7 @@ ci.svyglm <- function(x, ci = 0.95, method = "wald", ...) { } #' @export -ci.svyolr <- ci.svyglm +ci.svyolr <- ci.polr # p values ----------------------------------------------- @@ -118,15 +120,12 @@ p_value.svyglm <- function(model, verbose = TRUE, ...) { statistic <- insight::get_statistic(model) dof <- insight::get_df(model, type = "residual") p <- 2 * stats::pt(-abs(statistic$Statistic), df = dof) - .data_frame( - Parameter = statistic$Parameter, - p = as.vector(p) - ) + .data_frame(Parameter = statistic$Parameter, p = as.vector(p)) } #' @export -p_value.svyolr <- p_value.svyglm +p_value.svyolr <- p_value.polr #' @export @@ -137,12 +136,14 @@ p_value.svyglm.nb <- function(model, ...) { est <- stats::coef(model) se <- sqrt(diag(stats::vcov(model, stderr = "robust"))) - p <- 2 * stats::pt(abs(est / se), df = insight::get_df(model, type = "wald"), lower.tail = FALSE) + p <- 2 * + stats::pt( + abs(est / se), + df = insight::get_df(model, type = "wald"), + lower.tail = FALSE + ) - .data_frame( - Parameter = .remove_backticks_from_string(names(p)), - p = as.vector(p) - ) + .data_frame(Parameter = .remove_backticks_from_string(names(p)), p = as.vector(p)) } @@ -155,11 +156,18 @@ p_value.svyglm.zip <- p_value.svyglm.nb .ci_likelihood <- function(model, ci) { glm_ci <- tryCatch( { - out <- as.data.frame(stats::confint(model, level = ci, method = "likelihood"), stringsAsFactors = FALSE) + out <- as.data.frame( + stats::confint(model, level = ci, method = "likelihood"), + stringsAsFactors = FALSE + ) names(out) <- c("CI_low", "CI_high") out$CI <- ci - out$Parameter <- insight::get_parameters(model, effects = "fixed", component = "conditional")$Parameter + out$Parameter <- insight::get_parameters( + model, + effects = "fixed", + component = "conditional" + )$Parameter out <- out[c("Parameter", "CI", "CI_low", "CI_high")] rownames(out) <- NULL diff --git a/R/parameters_type.R b/R/parameters_type.R index 0024459b9..334157cd8 100644 --- a/R/parameters_type.R +++ b/R/parameters_type.R @@ -61,7 +61,7 @@ parameters_type <- function(model, ...) { ) # Special case - if (inherits(model, "polr")) { + if (inherits(model, c("polr", "svyolr"))) { params$Parameter <- gsub("Intercept: ", "", params$Parameter, fixed = TRUE) } @@ -81,7 +81,6 @@ parameters_type <- function(model, ...) { } } - # Remove "as.factor()", "log()" etc. from parameter names but save original parameter before original_parameter <- params$Parameter params$Parameter <- .clean_parameter_names(params$Parameter, full = TRUE) @@ -91,7 +90,6 @@ parameters_type <- function(model, ...) { params$Parameter <- gsub("^(count_|zero_)", "", params$Parameter) } - data <- insight::get_data(model, source = "mf", verbose = FALSE) if (is.null(data) || inherits(data, "ts") || nrow(data) == 0) { return(NULL) @@ -112,12 +110,17 @@ parameters_type <- function(model, ...) { main <- .parameters_type_table(names = params$Parameter, data, reference) secondary <- .parameters_type_table(names = main$Secondary_Parameter, data, reference) names(secondary) <- paste0("Secondary_", names(secondary)) - names(secondary)[names(secondary) == "Secondary_Secondary_Parameter"] <- "Tertiary_Parameter" + names(secondary)[ + names(secondary) == "Secondary_Secondary_Parameter" + ] <- "Tertiary_Parameter" out <- cbind(params, main, secondary) # Deal with nested interactions - for (i in unique(paste0(out[out$Type == "interaction", "Variable"], out[out$Type == "interaction", "Secondary_Variable"]))) { + for (i in unique(paste0( + out[out$Type == "interaction", "Variable"], + out[out$Type == "interaction", "Secondary_Variable"] + ))) { interac <- out[paste0(out$Variable, out$Secondary_Variable) == i, ] if (!all(interac$Term %in% out$Parameter)) { out[paste0(out$Variable, out$Secondary_Variable) == i, "Type"] <- "nested" @@ -133,9 +136,17 @@ parameters_type <- function(model, ...) { for (i in unique(out$Secondary_Parameter)) { if (!is.na(i) && i %in% out$Parameter) { .param_type <- out[!is.na(out$Parameter) & out$Parameter == i, "Type"] - .param_secondary_type <- out[!is.na(out$Secondary_Parameter) & out$Secondary_Parameter == i, "Secondary_Type"] - if (length(.param_type) == length(.param_secondary_type) || length(.param_type) == 1) { - out[!is.na(out$Secondary_Parameter) & out$Secondary_Parameter == i, "Secondary_Type"] <- .param_type + .param_secondary_type <- out[ + !is.na(out$Secondary_Parameter) & out$Secondary_Parameter == i, + "Secondary_Type" + ] + if ( + length(.param_type) == length(.param_secondary_type) || length(.param_type) == 1 + ) { + out[ + !is.na(out$Secondary_Parameter) & out$Secondary_Parameter == i, + "Secondary_Type" + ] <- .param_type } } } @@ -166,7 +177,12 @@ parameters_type <- function(model, ...) { } # Check if any is factor - types <- unlist(lapply(var, function(x, data, reference) .parameters_type_basic(x, data, reference)[1], data = data, reference = reference)) + types <- unlist(lapply( + var, + function(x, data, reference) .parameters_type_basic(x, data, reference)[1], + data = data, + reference = reference + )) link <- ifelse(any("factor" %in% types), "Difference", "Association") # Get type main <- .parameters_type_basic(var[1], data, reference) @@ -214,14 +230,7 @@ parameters_type <- function(model, ...) { # Factors } else if (cleaned_name %in% reference$levels) { fac <- reference$levels_parent[match(cleaned_name, reference$levels)] - return(c( - "factor", - "Difference", - name, - fac, - gsub(fac, "", name, fixed = TRUE), - NA - )) + return(c("factor", "Difference", name, fac, gsub(fac, "", name, fixed = TRUE), NA)) # Polynomials } else if (grepl("poly(", name, fixed = TRUE)) { @@ -284,7 +293,14 @@ parameters_type <- function(model, ...) { # Smooth } else if (startsWith(name, "smooth_")) { - return(c("smooth", "Association", gsub("^smooth_(.*)\\[(.*)\\]", "\\2", name), NA, NA, NA)) + return(c( + "smooth", + "Association", + gsub("^smooth_(.*)\\[(.*)\\]", "\\2", name), + NA, + NA, + NA + )) } else { return(c("unknown", NA, NA, NA, NA, NA)) } @@ -355,7 +371,9 @@ parameters_type <- function(model, ...) { out$ordered <- names(data[vapply(data, is.ordered, TRUE)]) # Factors - out$factor <- names(data[vapply(data, is.factor, TRUE) | vapply(data, is.character, TRUE)]) + out$factor <- names(data[ + vapply(data, is.factor, TRUE) | vapply(data, is.character, TRUE) + ]) out$levels <- NA out$levels_parent <- NA @@ -370,15 +388,35 @@ parameters_type <- function(model, ...) { } for (fac in out$factor) { - if ((fac %in% out$ordered && is.null(contrast_coding[[fac]])) || (!is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% "contr.poly"))) { - levels <- paste0(fac, c(".L", ".Q", ".C", paste0("^", 4:1000))[seq_along(unique(data[[fac]]))]) - } else if (!is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% c("contr.SAS2", "contr.sum", "contr.bayes", "contr.helmert"))) { + if ( + (fac %in% out$ordered && is.null(contrast_coding[[fac]])) || + (!is.null(contrast_coding[[fac]]) && + any(contrast_coding[[fac]] %in% "contr.poly")) + ) { + levels <- paste0( + fac, + c(".L", ".Q", ".C", paste0("^", 4:1000))[seq_along(unique(data[[fac]]))] + ) + } else if ( + !is.null(contrast_coding[[fac]]) && + any( + contrast_coding[[fac]] %in% + c("contr.SAS2", "contr.sum", "contr.bayes", "contr.helmert") + ) + ) { levels <- paste0(fac, seq_along(unique(data[[fac]]))) - } else if (!is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% "contr.treatment2")) { + } else if ( + !is.null(contrast_coding[[fac]]) && + any(contrast_coding[[fac]] %in% "contr.treatment2") + ) { levels <- paste0(fac, 2:length(unique(data[[fac]]))) - } else if (!is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% "contr.SAS")) { + } else if ( + !is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% "contr.SAS") + ) { levels <- paste0(fac, rev(unique(data[[fac]]))) - } else if (!is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% "contr.custom")) { + } else if ( + !is.null(contrast_coding[[fac]]) && any(contrast_coding[[fac]] %in% "contr.custom") + ) { levels <- paste0(fac, attributes(contrast_coding[[fac]])$column_names) } else { levels <- paste0(fac, unique(data[[fac]])) diff --git a/R/pool_parameters.R b/R/pool_parameters.R index cd5c0fb35..9a01fd419 100644 --- a/R/pool_parameters.R +++ b/R/pool_parameters.R @@ -63,26 +63,34 @@ #' pool_parameters(models, ci_method = "residual")$df_error #' @return A data frame of indices related to the model's parameters. #' @export -pool_parameters <- function(x, - exponentiate = FALSE, - effects = "fixed", - component = "all", - verbose = TRUE, - ...) { +pool_parameters <- function( + x, + exponentiate = FALSE, + effects = "fixed", + component = "all", + verbose = TRUE, + ... +) { # check input, save original model ----- original_model <- random_params <- NULL obj_name <- insight::safe_deparse_symbol(substitute(x)) - if (all(vapply(x, insight::is_model, TRUE)) && all(vapply(x, insight::is_model_supported, TRUE))) { + if ( + all(vapply(x, insight::is_model, TRUE)) && + all(vapply(x, insight::is_model_supported, TRUE)) + ) { original_model <- x[[1]] # Add exceptions for models with uncommon components here --------------- - exception_model_class <- "polr" + exception_model_class <- c("polr", "svyolr") # exceptions for "component" argument. Eg, MASS::polr has components # "alpha" and "beta", and "component" needs to be set to all by default - if (identical(component, "conditional") && inherits(original_model, exception_model_class)) { + if ( + identical(component, "conditional") && + inherits(original_model, exception_model_class) + ) { component <- "all" } @@ -105,29 +113,38 @@ pool_parameters <- function(x, ) } - # only pool for specific component ----- original_x <- x - if ("Component" %in% colnames(x[[1]]) && !insight::is_empty_object(component) && component != "all") { + if ( + "Component" %in% + colnames(x[[1]]) && + !insight::is_empty_object(component) && + component != "all" + ) { x <- lapply(x, function(i) { i <- i[i$Component == component, ] i$Component <- NULL i }) if (verbose) { - insight::format_alert(paste0("Pooling applied to the ", component, " model component.")) + insight::format_alert(paste0( + "Pooling applied to the ", + component, + " model component." + )) } } - # preparation ---- params <- do.call(rbind, x) len <- length(x) ci <- attributes(original_x[[1]])$ci - if (is.null(ci)) ci <- 0.95 + if (is.null(ci)) { + ci <- 0.95 + } parameter_values <- x[[1]]$Parameter # exceptions ---- @@ -156,7 +173,9 @@ pool_parameters <- function(x, # but only for fixed effects. Filter random effects, # and save parameter names from fixed effects for later use... - if (effects == "all" && "Effects" %in% colnames(params) && "random" %in% params$Effects) { + if ( + effects == "all" && "Effects" %in% colnames(params) && "random" %in% params$Effects + ) { random_params <- params[params$Effects == "random", ] params <- params[params$Effects != "random", ] parameter_values <- x[[1]]$Parameter[x[[1]]$Effects != "random"] @@ -182,98 +201,123 @@ pool_parameters <- function(x, # pool estimates etc. ----- - pooled_params <- do.call(rbind, lapply(estimates, function(i) { - # if we split by "component", some of the data frames might be empty - # in this case, just skip... - if (nrow(i) > 0) { - # pooled estimate - pooled_estimate <- mean(i$Coefficient) + pooled_params <- do.call( + rbind, + lapply(estimates, function(i) { + # if we split by "component", some of the data frames might be empty + # in this case, just skip... + if (nrow(i) > 0) { + # pooled estimate + pooled_estimate <- mean(i$Coefficient) + + # special models that have no standard errors (like "htest" objects) + if (is.null(i$SE) || all(is.na(i$SE))) { + out <- data.frame( + Coefficient = pooled_estimate, + SE = NA, + CI_low = NA, + CI_high = NA, + Statistic = NA, + df_error = NA, + p = NA, + stringsAsFactors = FALSE + ) + + if (verbose) { + insight::format_alert( + "Model objects had no standard errors. Cannot compute pooled confidence intervals and p-values." + ) + } - # special models that have no standard errors (like "htest" objects) - if (is.null(i$SE) || all(is.na(i$SE))) { - out <- data.frame( - Coefficient = pooled_estimate, - SE = NA, - CI_low = NA, - CI_high = NA, - Statistic = NA, - df_error = NA, - p = NA, - stringsAsFactors = FALSE - ) + # regular models that have coefficients and standard errors + } else { + # pooled standard error + ubar <- mean(i$SE^2) + tmp <- ubar + (1 + 1 / len) * stats::var(i$Coefficient) + pooled_se <- sqrt(tmp) + + # pooled degrees of freedom, Barnard-Rubin adjustment for small samples + df_column <- grep("(\\bdf\\b|\\bdf_error\\b)", colnames(i), value = TRUE)[1] + if (length(df_column)) { + pooled_df <- .barnad_rubin( + m = nrow(i), + b = stats::var(i$Coefficient), + t = tmp, + dfcom = unique(i[[df_column]]) + ) + # validation check length + if (length(pooled_df) > 1 && length(pooled_se) == 1) { + pooled_df <- round(mean(pooled_df, na.rm = TRUE)) + } + } else { + pooled_df <- Inf + } - if (verbose) { - insight::format_alert("Model objects had no standard errors. Cannot compute pooled confidence intervals and p-values.") + # pooled statistic + pooled_statistic <- pooled_estimate / pooled_se + + # confidence intervals + alpha <- (1 + ci) / 2 + fac <- suppressWarnings(stats::qt(alpha, df = pooled_df)) + + out <- data.frame( + Coefficient = pooled_estimate, + SE = pooled_se, + CI_low = pooled_estimate - pooled_se * fac, + CI_high = pooled_estimate + pooled_se * fac, + Statistic = pooled_statistic, + df_error = pooled_df, + p = 2 * stats::pt(abs(pooled_statistic), df = pooled_df, lower.tail = FALSE), + stringsAsFactors = FALSE + ) } - - # regular models that have coefficients and standard errors + out } else { - # pooled standard error - ubar <- mean(i$SE^2) - tmp <- ubar + (1 + 1 / len) * stats::var(i$Coefficient) - pooled_se <- sqrt(tmp) - - # pooled degrees of freedom, Barnard-Rubin adjustment for small samples - df_column <- grep("(\\bdf\\b|\\bdf_error\\b)", colnames(i), value = TRUE)[1] - if (length(df_column)) { - pooled_df <- .barnad_rubin(m = nrow(i), b = stats::var(i$Coefficient), t = tmp, dfcom = unique(i[[df_column]])) - # validation check length - if (length(pooled_df) > 1 && length(pooled_se) == 1) { - pooled_df <- round(mean(pooled_df, na.rm = TRUE)) - } - } else { - pooled_df <- Inf - } - - # pooled statistic - pooled_statistic <- pooled_estimate / pooled_se - - # confidence intervals - alpha <- (1 + ci) / 2 - fac <- suppressWarnings(stats::qt(alpha, df = pooled_df)) - - out <- data.frame( - Coefficient = pooled_estimate, - SE = pooled_se, - CI_low = pooled_estimate - pooled_se * fac, - CI_high = pooled_estimate + pooled_se * fac, - Statistic = pooled_statistic, - df_error = pooled_df, - p = 2 * stats::pt(abs(pooled_statistic), df = pooled_df, lower.tail = FALSE), - stringsAsFactors = FALSE - ) + NULL } - out - } else { - NULL - } - })) - + }) + ) # pool random effect variances ----- pooled_random <- NULL if (!is.null(random_params)) { - estimates <- split(random_params, factor(random_params$Parameter, levels = unique(random_params$Parameter))) - pooled_random <- do.call(rbind, lapply(estimates, function(i) { - pooled_estimate <- mean(i$Coefficient, na.rm = TRUE) - data.frame( - Parameter = unique(i$Parameter), - Coefficient = pooled_estimate, - Effects = "random", - stringsAsFactors = FALSE - ) - })) + estimates <- split( + random_params, + factor(random_params$Parameter, levels = unique(random_params$Parameter)) + ) + pooled_random <- do.call( + rbind, + lapply(estimates, function(i) { + pooled_estimate <- mean(i$Coefficient, na.rm = TRUE) + data.frame( + Parameter = unique(i$Parameter), + Coefficient = pooled_estimate, + Effects = "random", + stringsAsFactors = FALSE + ) + }) + ) pooled_params$Effects <- "fixed" } # reorder ------ pooled_params$Parameter <- parameter_values - columns <- c("Parameter", "Coefficient", "SE", "CI_low", "CI_high", "Statistic", "df_error", "p", "Effects", "Component") + columns <- c( + "Parameter", + "Coefficient", + "SE", + "CI_low", + "CI_high", + "Statistic", + "df_error", + "p", + "Effects", + "Component" + ) pooled_params <- pooled_params[intersect(columns, colnames(pooled_params))] - # final attributes ----- # exponentiate coefficients and SE/CI, if requested @@ -299,7 +343,6 @@ pool_parameters <- function(x, ) attr(pooled_params, "object_name") <- obj_name - # pool sigma ---- sig <- unlist(insight::compact_list(lapply(original_x, function(i) { @@ -310,15 +353,17 @@ pool_parameters <- function(x, attr(pooled_params, "sigma") <- mean(sig, na.rm = TRUE) } - - class(pooled_params) <- c("parameters_model", "see_parameters_model", class(pooled_params)) + class(pooled_params) <- c( + "parameters_model", + "see_parameters_model", + class(pooled_params) + ) pooled_params } # helper ------ - .barnad_rubin <- function(m, b, t, dfcom = 999999) { # fix for z-statistic if (is.null(dfcom) || all(is.na(dfcom)) || all(is.infinite(dfcom))) { @@ -332,11 +377,21 @@ pool_parameters <- function(x, } -.add_pooled_params_attributes <- function(pooled_params, model_params, model, ci, exponentiate, verbose = TRUE) { +.add_pooled_params_attributes <- function( + pooled_params, + model_params, + model, + ci, + exponentiate, + verbose = TRUE +) { info <- insight::model_info(model, verbose = FALSE) pretty_names <- attributes(model_params)$pretty_names if (length(pretty_names) < nrow(model_params)) { - pretty_names <- c(pretty_names, model_params$Parameter[(length(pretty_names) + 1):nrow(model_params)]) + pretty_names <- c( + pretty_names, + model_params$Parameter[(length(pretty_names) + 1):nrow(model_params)] + ) } attr(pooled_params, "ci") <- ci attr(pooled_params, "exponentiate") <- exponentiate diff --git a/tests/testthat/test-svyolr.R b/tests/testthat/test-svyolr.R new file mode 100644 index 000000000..5de420a8c --- /dev/null +++ b/tests/testthat/test-svyolr.R @@ -0,0 +1,44 @@ +skip_if_not_installed("MASS") +skip_if_not_installed("survey") +skip_on_cran() + +test_that("robust-se polr", { + data(api, package = "survey") + dclus1 <- survey::svydesign(id = ~dnum, weights = ~pw, data = apiclus1, fpc = ~fpc) + dclus1 <- update(dclus1, mealcat = cut(meals, c(0, 25, 50, 75, 100))) + + m <- survey::svyolr(mealcat ~ avg.ed + mobility + stype, design = dclus1) + out <- model_parameters(m) + expect_identical(attributes(out)$coefficient_name, "Log-Odds") + expect_identical( + out$Component, + c("alpha", "alpha", "alpha", "beta", "beta", "beta", "beta") + ) + expect_identical( + out$Parameter, + c( + "(0,25]|(25,50]", + "(25,50]|(50,75]", + "(50,75]|(75,100]", + "avg.ed", + "mobility", + "stypeH", + "stypeM" + ) + ) + expect_named( + out, + c( + "Parameter", + "Coefficient", + "SE", + "CI", + "CI_low", + "CI_high", + "t", + "df_error", + "p", + "Component" + ) + ) +})