############################################ ## Homogeneity of variance test ############################################ #' @export homo_variance_test <- function(dataset, var, group, method = c("levene", "bartlett", "fligner"), data_filter = "", envir = parent.frame()) { # 获取数据 df_name <- if (is_string(dataset)) dataset else deparse(substitute(dataset)) dataset <- get_data( dataset, vars = c(var, group), filt = data_filter, na.rm = FALSE, envir = envir ) # 校验变量存在性 if (!var %in% colnames(dataset)) { stop(paste("变量", var, "未在数据集中找到!"), call. = FALSE) } if (!group %in% colnames(dataset)) { stop(paste("分组变量", group, "未在数据集中找到!"), call. = FALSE) } # 提取变量 x <- dataset[[var]] g_raw <- dataset[[group]] # 校验数值变量类型 if (!is.numeric(x)) { stop(paste("变量", var, "必须是数值型!"), call. = FALSE) } # 计算有效样本 valid_indices <- !is.na(g_raw) & !is.na(x) valid_data <- dataset[valid_indices, ] # 保留有效样本的完整数据(用于绘图) valid_g <- g_raw[valid_indices] valid_levels <- length(unique(valid_g)) # 数据不足判断 if (valid_levels < 2) { return(structure( list( df_name = df_name, var = var, group = group, valid_data = valid_data, # 传递有效数据用于绘图提示 res = tibble( Test = "无法执行检验", Statistic = NA_real_, p.value = NA_character_ ) ), class = "homo_variance_test" )) } # 转换分组为因子 g <- factor(valid_g) # 检验计算 res <- tibble::tibble( Test = character(), Statistic = numeric(), p.value = numeric() ) # Levene检验 if ("levene" %in% method && requireNamespace("car", quietly = TRUE)) { tmp <- tryCatch(car::leveneTest(x[valid_indices] ~ g), error = function(e) NULL) if (!is.null(tmp) && nrow(tmp) > 0) { res <- tibble::add_row(res, Test = "Levene", Statistic = as.numeric(tmp[["F value"]][1]), p.value = as.numeric(tmp[["Pr(>F)"]][1])) } } # Bartlett检验 if ("bartlett" %in% method) { tmp <- tryCatch(stats::bartlett.test(x[valid_indices], g), error = function(e) NULL) if (!is.null(tmp)) { res <- tibble::add_row(res, Test = "Bartlett", Statistic = as.numeric(tmp$statistic), p.value = as.numeric(tmp$p.value)) } } # Fligner检验 if ("fligner" %in% method) { tmp <- tryCatch(stats::fligner.test(x[valid_indices], g), error = function(e) NULL) if (!is.null(tmp)) { res <- tibble::add_row(res, Test = "Fligner", Statistic = as.numeric(tmp$statistic), p.value = as.numeric(tmp$p.value)) } } # 返回结果(包含有效数据用于绘图) structure( list( df_name = df_name, var = var, group = group, valid_data = valid_data, # 新增:保存有效样本数据 res = res ), class = "homo_variance_test" ) } #' @export summary.homo_variance_test <- function(object, dec = 3, ...) { # 标准化说明文字(与正态性检验格式一致) cat("Homogeneity of variance tests\n") cat("Data :", object$df_name, "\n") cat("Variable :", object$var, "\n") cat("Group :", object$group, "\n\n") # 格式化结果表格 result_table <- object$res %>% dplyr::mutate( Statistic = round(Statistic, dec), p.value = dplyr::case_when( p.value < 0.001 ~ "<0.001", is.na(p.value) ~ "", TRUE ~ as.character(round(p.value, dec)) ) ) # 打印结果表格 print(as.data.frame(result_table), row.names = FALSE) invisible(object) } #' @export plot.homo_variance_test <- function(x, plots = c("boxplot", "density", "hist"), shiny = FALSE, custom = FALSE, ...) { # 1. 提取有效数据(用于绘图) valid_data <- x$valid_data if (nrow(valid_data) == 0) { return(ggplot2::ggplot() + ggplot2::annotate("text", x = 1, y = 1, label = i18n$t("No valid data for plotting")) + ggplot2::theme_void()) } # 2. 定义变量名(用于图表标签) var_name <- x$var group_name <- x$group # 3. 初始化图形列表 plot_list <- list() # 4. 生成箱线图(按分组展示数值变量分布) if ("boxplot" %in% plots) { p <- ggplot2::ggplot(valid_data, ggplot2::aes(x = .data[[group_name]], y = .data[[var_name]], fill = .data[[group_name]])) + ggplot2::geom_boxplot(alpha = 0.7, show.legend = FALSE) + ggplot2::labs(x = group_name, y = var_name, title = i18n$t("Boxplot by Group")) + ggplot2::theme_minimal() + ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5)) plot_list[["boxplot"]] <- p } # 5. 生成密度图(按分组展示数值变量分布) if ("density" %in% plots) { p <- ggplot2::ggplot(valid_data, ggplot2::aes(x = .data[[var_name]], fill = .data[[group_name]], color = .data[[group_name]])) + ggplot2::geom_density(alpha = 0.3) + ggplot2::labs(x = var_name, y = i18n$t("Density"), title = i18n$t("Density by Group"), fill = group_name, color = group_name) + ggplot2::theme_minimal() + ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5)) plot_list[["density"]] <- p } # 6. 生成直方图(按分组展示数值变量分布) if ("hist" %in% plots) { p <- ggplot2::ggplot(valid_data, ggplot2::aes(x = .data[[var_name]], fill = .data[[group_name]])) + ggplot2::geom_histogram(position = "identity", alpha = 0.5, bins = 30) + ggplot2::labs(x = var_name, y = i18n$t("Count"), title = i18n$t("Histogram by Group"), fill = group_name) + ggplot2::theme_minimal() + ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5)) plot_list[["hist"]] <- p } # 7. 处理未选择图表类型的情况 if (length(plot_list) == 0) { return(ggplot2::ggplot() + ggplot2::annotate("text", x = 1, y = 1, label = i18n$t("No plots selected")) + ggplot2::theme_void()) } # 8. 组合图表(按选择顺序排列) combined_plot <- patchwork::wrap_plots(plot_list[plots], ncol = 1) # 9. 在Shiny中显示或返回图表 if (shiny) { print(combined_plot) invisible(x) } else { combined_plot } }