flowchart TD subgraph "Data Input Layer" A[Statistical Results] --> B[Assumption Test Data] A --> C[Effect Size Metrics] A --> D[Diagnostic Information] end subgraph "Visualization Engine" B --> E[Diagnostic Plot Suite] C --> F[Statistical Summary Plots] D --> G[Interactive Exploration Tools] A --> H[Publication-Ready Outputs] end subgraph "Rendering Systems" E --> I[Static ggplot2 Graphics] F --> J[Interactive plotly Displays] G --> K[Dynamic ggiraph Elements] H --> L[Export-Ready Formats] end subgraph "Output Destinations" I --> M[Regulatory Reports] J --> N[Interactive Dashboards] K --> O[Clinical Presentations] L --> P[Publication Submissions] end subgraph "Quality Control" Q[Style Validation] --> I R[Accessibility Compliance] --> J S[Format Standards] --> K T[Resolution Control] --> L end style A fill:#ffebee style M fill:#e8f5e8 style N fill:#e8f5e8 style O fill:#e8f5e8 style P fill:#e8f5e8
Key Takeaways
- Publication Standards: Create professional visualizations that meet peer-review and regulatory submission requirements with precise styling, appropriate statistical elements, and publication-ready formatting
- Interactive Excellence: Implement sophisticated plotly integration with context-aware tooltips, dynamic filtering, and cross-plot linking that enhances analytical insight without sacrificing statistical rigor
- Diagnostic Sophistication: Build comprehensive diagnostic plot suites including assumption testing visualizations, residual analysis, and sensitivity assessment that support robust statistical interpretation
- Export Flexibility: Generate high-resolution outputs in multiple formats (PNG, SVG, PDF) with precise control over dimensions, resolution, and styling for diverse publication and presentation needs
- Clinical Integration: Design visualization systems that communicate statistical findings effectively to clinical teams while maintaining technical accuracy and regulatory compliance documentation
Introduction
Professional statistical visualization transforms complex analytical results into compelling, interpretable displays that drive informed decision-making across clinical, regulatory, and research contexts. Enterprise applications require visualizations that exceed basic plotting capabilities to deliver publication-ready graphics, interactive exploration tools, and comprehensive diagnostic displays that meet the demanding standards of regulated industries.
This tutorial elevates your enterprise t-test application to include sophisticated visualization capabilities that seamlessly integrate with the statistical rigor framework you’ve implemented. You’ll master advanced ggplot2 customization techniques, implement intelligent plotly interactivity, and create comprehensive diagnostic visualization suites that support robust statistical interpretation while meeting regulatory documentation requirements.
The visualization patterns you’ll develop extend far beyond t-tests to encompass any statistical analysis requiring professional presentation, from simple comparative studies to complex multi-factorial designs, while maintaining the accessibility and interpretability that stakeholders expect from modern analytical platforms.
Enterprise Visualization Architecture
Comprehensive Visualization Framework
Professional statistical applications require systematic approaches to visualization that ensure consistency, quality, and regulatory compliance:
Professional Visualization Standards
Enterprise statistical visualization requires adherence to multiple professional standards:
Publication Standards:
- Journal-specific formatting requirements and style guidelines
- High-resolution output capabilities for print and digital distribution
- Precise color schemes that reproduce accurately across media
- Statistical element placement following established conventions
Regulatory Compliance:
- FDA guidance-compliant statistical graphics for submissions
- ICH E3 documentation standards for clinical study reports
- Audit trail requirements for visualization generation and modification
- Version control and change documentation for regulatory review
Clinical Communication:
- Clear, interpretable displays that communicate statistical findings effectively
- Context-aware annotations that explain clinical significance
- Progressive disclosure from summary to detailed diagnostic views
- Integration with clinical workflow and decision-making processes
Technical Excellence:
- Cross-platform compatibility and consistent rendering
- Accessibility compliance including color-blind friendly palettes
- Performance optimization for interactive elements and large datasets
- Extensible architecture supporting custom visualization requirements
Professional ggplot2 Framework
Enterprise-Grade Static Visualization System
Create a comprehensive ggplot2 framework that generates publication-ready statistical graphics:
# File: R/professional_plotting_engine.R
#' Professional Statistical Plotting Engine
#'
#' @description Enterprise-grade plotting system with publication standards,
#' regulatory compliance, and comprehensive customization capabilities for
#' clinical and research applications.
#'
#' @export
<- R6::R6Class(
ProfessionalPlottingEngine "ProfessionalPlottingEngine",
public = list(
#' @field config plotting configuration and style settings
config = NULL,
#' @field theme_manager professional theme management system
theme_manager = NULL,
#' @field export_manager high-quality export capabilities
export_manager = NULL,
#' @field audit_logger audit logging for plot generation
audit_logger = NULL,
#' Initialize professional plotting engine
#'
#' @param config list. Plotting configuration
#' @param audit_logger AuditLogger. Audit logging instance
#'
initialize = function(config = list(), audit_logger = NULL) {
$config <- private$default_plotting_config(config)
self$theme_manager <- PlotThemeManager$new(self$config$themes)
self$export_manager <- PlotExportManager$new(self$config$export)
self$audit_logger <- audit_logger
self
# Initialize ggplot2 extensions
$load_required_packages()
private
if (!is.null(self$audit_logger)) {
$audit_logger$log_event(
self"ProfessionalPlottingEngine initialized",
level = "INFO",
category = "VISUALIZATION"
)
}
},
#' Create comprehensive statistical plot suite
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param plot_types character vector. Types of plots to generate
#' @param context list. Plotting context and customization
#'
#' @return StatisticalPlotSuite. Comprehensive plot collection
#'
create_plot_suite = function(analysis_result, plot_types = "all", context = list()) {
<- uuid::UUIDgenerate()
plot_id <- Sys.time()
start_time
tryCatch({
# Log plot suite creation
if (!is.null(self$audit_logger)) {
$audit_logger$log_event(
self"Statistical plot suite creation initiated",
level = "INFO",
category = "VISUALIZATION",
details = list(
plot_id = plot_id,
analysis_id = analysis_result$analysis_id,
plot_types = plot_types,
context = context
)
)
}
# Determine plot types to generate
if ("all" %in% plot_types) {
<- private$get_default_plot_types(analysis_result)
plot_types
}
# Generate individual plots
<- list()
plots
for (plot_type in plot_types) {
<- switch(plot_type,
plot_result "summary_comparison" = self$create_summary_comparison_plot(analysis_result, context),
"diagnostic_suite" = self$create_diagnostic_plot_suite(analysis_result, context),
"effect_size_forest" = self$create_effect_size_forest_plot(analysis_result, context),
"assumption_dashboard" = self$create_assumption_dashboard(analysis_result, context),
"distribution_analysis" = self$create_distribution_analysis_plot(analysis_result, context),
"confidence_interval_plot" = self$create_confidence_interval_plot(analysis_result, context),
"sensitivity_analysis_plot" = self$create_sensitivity_analysis_plot(analysis_result, context),
# Default: skip unknown plot types
NULL
)
if (!is.null(plot_result)) {
<- plot_result
plots[[plot_type]]
}
}
# Create plot suite object
<- StatisticalPlotSuite$new(
plot_suite plot_id = plot_id,
analysis_id = analysis_result$analysis_id,
plots = plots,
generation_time = as.numeric(Sys.time() - start_time, units = "secs"),
context = context,
config = self$config
)
# Log successful completion
if (!is.null(self$audit_logger)) {
$audit_logger$log_event(
self"Statistical plot suite created successfully",
level = "INFO",
category = "VISUALIZATION",
details = list(
plot_id = plot_id,
n_plots = length(plots),
generation_time = plot_suite$generation_time
)
)
}
return(plot_suite)
error = function(e) {
},
# Log plot generation error
if (!is.null(self$audit_logger)) {
$audit_logger$log_event(
selfpaste("Plot suite creation failed:", e$message),
level = "ERROR",
category = "VISUALIZATION",
details = list(
plot_id = plot_id,
error_message = e$message,
analysis_id = analysis_result$analysis_id
)
)
}
stop(paste("Plot suite creation failed:", e$message))
})
},
#' Create summary comparison plot
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return ggplot. Professional summary comparison plot
#'
create_summary_comparison_plot = function(analysis_result, context = list()) {
# Extract group statistics
<- analysis_result$primary_analysis
primary_analysis <- primary_analysis$group_statistics
group_stats
if (length(group_stats) != 2) {
stop("Summary comparison plot requires exactly 2 groups")
}
# Prepare data for plotting
<- names(group_stats)
group_names <- data.frame(
plot_data Group = factor(group_names, levels = group_names),
Mean = sapply(group_stats, function(x) x$mean),
SD = sapply(group_stats, function(x) x$sd),
N = sapply(group_stats, function(x) x$n),
SE = sapply(group_stats, function(x) x$se)
)
# Calculate confidence intervals
<- analysis_result$parameters$conf_level %||% 0.95
conf_level <- 1 - conf_level
alpha
$CI_lower <- plot_data$Mean - qt(1 - alpha/2, plot_data$N - 1) * plot_data$SE
plot_data$CI_upper <- plot_data$Mean + qt(1 - alpha/2, plot_data$N - 1) * plot_data$SE
plot_data
# Create professional plot
<- ggplot(plot_data, aes(x = Group, y = Mean, fill = Group)) +
p
# Main elements
geom_col(alpha = 0.7, width = 0.6, color = "white", size = 0.5) +
geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
width = 0.2, size = 0.8, color = "black") +
# Individual data points (if available in context)
if ("show_points" %in% names(context) && context$show_points &&
{"raw_data" %in% names(context)) {
geom_jitter(data = context$raw_data,
aes(x = group, y = response, fill = group),
shape = 21, size = 2, alpha = 0.6, width = 0.15,
inherit.aes = FALSE)
+
}}
# Statistical annotation
if (primary_analysis$p_value < 0.05) {
{annotate("text",
x = 1.5,
y = max(plot_data$CI_upper) * 1.1,
label = paste0("p = ", format.pval(primary_analysis$p_value, digits = 3),
"\nCohen's d = ", round(primary_analysis$effect_size$cohens_d, 3)),
hjust = 0.5, vjust = 0,
size = 3.5, fontface = "italic")
+
}}
# Professional styling
scale_fill_manual(values = self$config$themes$color_palette$primary) +
scale_y_continuous(expand = expansion(mult = c(0.02, 0.15))) +
# Professional theme
$theme_manager$get_publication_theme() +
self
# Labels and title
labs(
title = context$title %||% "Group Comparison",
subtitle = paste0(primary_analysis$method_name, " • ",
* 100, "% Confidence Intervals"),
conf_level x = context$x_label %||% "Group",
y = context$y_label %||% "Mean ± 95% CI",
caption = private$generate_plot_caption(analysis_result, context)
+
)
# Remove legend (redundant with x-axis)
guides(fill = "none")
# Add sample size annotations
<- p +
p geom_text(aes(label = paste0("n = ", N)),
vjust = -0.5, y = plot_data$CI_upper +
max(plot_data$CI_upper) - min(plot_data$CI_lower)) * 0.03,
(size = 3, color = "gray40")
return(p)
},
#' Create comprehensive diagnostic plot suite
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return list. Collection of diagnostic plots
#'
create_diagnostic_plot_suite = function(analysis_result, context = list()) {
<- analysis_result$assumption_tests
assumption_tests <- list()
diagnostic_plots
# Q-Q plots for normality assessment
if ("normality" %in% names(assumption_tests$assumption_results)) {
$qq_plots <- private$create_qq_plot_panel(
diagnostic_plots$assumption_results$normality, context
assumption_tests
)
}
# Residual plots for homogeneity assessment
if ("homogeneity" %in% names(assumption_tests$assumption_results)) {
$residual_plots <- private$create_residual_analysis_plot(
diagnostic_plots
analysis_result, context
)
}
# Outlier detection visualization
if ("outliers" %in% names(assumption_tests$assumption_results)) {
$outlier_plots <- private$create_outlier_detection_plot(
diagnostic_plots$assumption_results$outliers, context
assumption_tests
)
}
# Distribution comparison plots
$distribution_plots <- private$create_distribution_comparison_plot(
diagnostic_plots
analysis_result, context
)
return(diagnostic_plots)
},
#' Create effect size forest plot
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return ggplot. Professional forest plot
#'
create_effect_size_forest_plot = function(analysis_result, context = list()) {
<- analysis_result$effect_analysis
effect_analysis <- effect_analysis$primary_effect_size
primary_effect
# Prepare forest plot data
<- data.frame(
forest_data Method = c("Primary Analysis", "Sensitivity Analysis"),
Effect_Size = c(
$value,
primary_effectif (!is.null(analysis_result$sensitivity_analysis)) {
mean(sapply(analysis_result$sensitivity_analysis$detailed_results,
function(x) if ("effect_size" %in% names(x)) x$effect_size$cohens_d else NA),
na.rm = TRUE)
else NA
}
),CI_Lower = c(
if (!is.null(effect_analysis$confidence_intervals)) {
$confidence_intervals$lower
effect_analysiselse primary_effect$value - 0.1,
} NA # Simplified for sensitivity
),CI_Upper = c(
if (!is.null(effect_analysis$confidence_intervals)) {
$confidence_intervals$upper
effect_analysiselse primary_effect$value + 0.1,
} NA # Simplified for sensitivity
),Interpretation = c(
$interpretation,
primary_effect"Sensitivity Range"
)
)
# Remove rows with missing data
<- forest_data[!is.na(forest_data$Effect_Size), ]
forest_data
# Create forest plot
<- ggplot(forest_data, aes(x = Effect_Size, y = Method)) +
p
# Confidence intervals
if (!any(is.na(forest_data$CI_Lower))) {
{geom_errorbarh(aes(xmin = CI_Lower, xmax = CI_Upper),
height = 0.2, size = 1.2, color = "steelblue")
+
}}
# Effect size points
geom_point(size = 4, color = "steelblue", shape = 18) +
# Reference line at zero
geom_vline(xintercept = 0, linetype = "dashed", color = "gray60", size = 0.8) +
# Effect size magnitude lines
geom_vline(xintercept = c(-0.8, -0.5, -0.2, 0.2, 0.5, 0.8),
linetype = "dotted", color = "gray80", alpha = 0.7) +
# Professional styling
$theme_manager$get_publication_theme() +
selftheme(
panel.grid.major.y = element_line(color = "gray95", size = 0.5),
panel.grid.minor = element_blank()
+
)
# Labels and annotations
labs(
title = context$title %||% "Effect Size Analysis",
subtitle = paste0("Cohen's d with ",
$parameters$conf_level %||% 0.95) * 100,
(analysis_result"% Confidence Intervals"),
x = "Effect Size (Cohen's d)",
y = "",
caption = private$generate_plot_caption(analysis_result, context)
+
)
# Effect size interpretation annotations
annotate("text", x = c(-0.8, -0.5, -0.2, 0.2, 0.5, 0.8),
y = 0.5,
label = c("Large", "Medium", "Small", "Small", "Medium", "Large"),
size = 2.5, color = "gray60", angle = 90, vjust = 0.5)
return(p)
},
#' Create assumption testing dashboard
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return ggplot. Assumption testing dashboard
#'
create_assumption_dashboard = function(analysis_result, context = list()) {
<- analysis_result$assumption_tests
assumption_tests
# Create summary data for assumption status
<- data.frame(
assumption_summary Assumption = c("Normality", "Homogeneity", "Independence", "Outliers"),
Status = c(
if ("normality" %in% names(assumption_tests$assumption_results)) {
$assess_assumption_status(assumption_tests$assumption_results$normality)
privateelse "Not Tested",
}
if ("homogeneity" %in% names(assumption_tests$assumption_results)) {
$assess_assumption_status(assumption_tests$assumption_results$homogeneity)
privateelse "Not Tested",
}
if ("independence" %in% names(assumption_tests$assumption_results)) {
$assess_assumption_status(assumption_tests$assumption_results$independence)
privateelse "Not Tested",
}
if ("outliers" %in% names(assumption_tests$assumption_results)) {
$assess_outlier_status(assumption_tests$assumption_results$outliers)
privateelse "Not Tested"
}
),Test_Details = c(
"Shapiro-Wilk, Anderson-Darling",
"Levene's Test, Brown-Forsythe",
"Runs Test, Design Assessment",
"IQR, Modified Z-score"
)
)
# Color coding for status
<- c(
status_colors "Met" = "#28a745",
"Violated" = "#dc3545",
"Borderline" = "#ffc107",
"Not Tested" = "#6c757d"
)
$Status <- factor(assumption_summary$Status,
assumption_summarylevels = names(status_colors))
# Create dashboard plot
<- ggplot(assumption_summary, aes(x = Assumption, y = 1, fill = Status)) +
p
# Status tiles
geom_tile(color = "white", size = 1.5, width = 0.9, height = 0.8) +
# Status labels
geom_text(aes(label = Status), color = "white", fontface = "bold", size = 4) +
# Test details
geom_text(aes(label = Test_Details, y = 0.3),
color = "gray40", size = 2.8, lineheight = 0.9) +
# Color scheme
scale_fill_manual(values = status_colors, drop = FALSE) +
# Professional theme
$theme_manager$get_minimal_theme() +
selftheme(
axis.text.y = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank(),
legend.position = "bottom",
legend.title = element_text(size = 10),
plot.title = element_text(hjust = 0.5, size = 14, fontface = "bold")
+
)
# Labels
labs(
title = context$title %||% "Statistical Assumption Assessment",
subtitle = paste("Method Selected:",
$method_selection$method_info$name),
analysis_resultx = "",
y = "",
fill = "Assumption Status",
caption = private$generate_plot_caption(analysis_result, context)
+
)
# Coordinate system
coord_cartesian(ylim = c(0, 1.5))
return(p)
}
),
private = list(
#' Default plotting configuration
#'
#' @param config list. User configuration
#'
#' @return list. Complete configuration
#'
default_plotting_config = function(config) {
<- list(
default_config
themes = list(
base_family = "Arial",
base_size = 11,
color_palette = list(
primary = c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd"),
clinical = c("#2E86AB", "#A23B72", "#F18F01", "#C73E1D"),
grayscale = c("#2b2b2b", "#545454", "#737373", "#969696", "#bdbdbd"),
colorblind_friendly = c("#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2")
),
publication_standards = list(
figure_width = 7,
figure_height = 5,
dpi = 300,
text_size_base = 11,
line_size_base = 0.5
)
),
export = list(
formats = c("png", "pdf", "svg"),
high_res_dpi = 300,
print_dpi = 600,
web_dpi = 150,
dimensions = list(
single_column = list(width = 3.5, height = 2.5),
double_column = list(width = 7, height = 5),
full_page = list(width = 8.5, height = 11),
presentation = list(width = 10, height = 7.5)
)
),
statistical_elements = list(
show_confidence_intervals = TRUE,
show_sample_sizes = TRUE,
show_effect_sizes = TRUE,
show_p_values = TRUE,
p_value_threshold = 0.05,
annotation_styles = list(
p_value_format = "p = {value}",
effect_size_format = "d = {value}",
ci_format = "{level}% CI"
)
),
accessibility = list(
colorblind_safe = TRUE,
high_contrast_mode = FALSE,
large_text_mode = FALSE,
screen_reader_compatible = TRUE
)
)
modifyList(default_config, config)
},
#' Load required plotting packages
#'
load_required_packages = function() {
<- c("ggplot2", "scales", "grid", "gridExtra", "ggrepel")
required_packages
for (pkg in required_packages) {
if (!requireNamespace(pkg, quietly = TRUE)) {
warning(paste("Package", pkg, "not available. Some plotting features may be limited."))
}
}
},
#' Get default plot types for analysis
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#'
#' @return character vector. Default plot types
#'
get_default_plot_types = function(analysis_result) {
<- c("summary_comparison", "assumption_dashboard")
base_plots
# Add diagnostic plots if assumption testing was performed
if (!is.null(analysis_result$assumption_tests)) {
<- c(base_plots, "diagnostic_suite")
base_plots
}
# Add effect size plot if effect analysis available
if (!is.null(analysis_result$effect_analysis)) {
<- c(base_plots, "effect_size_forest")
base_plots
}
# Add sensitivity plot if sensitivity analysis performed
if (!is.null(analysis_result$sensitivity_analysis) &&
$sensitivity_analysis$methods_tested > 0) {
analysis_result<- c(base_plots, "sensitivity_analysis_plot")
base_plots
}
return(base_plots)
},
#' Assess assumption status for dashboard
#'
#' @param assumption_result list. Assumption test result
#'
#' @return character. Status assessment
#'
assess_assumption_status = function(assumption_result) {
if ("overall_conclusion" %in% names(assumption_result)) {
<- assumption_result$overall_conclusion
conclusion if ("assumes_normal" %in% names(conclusion)) {
return(if (conclusion$assumes_normal) "Met" else "Violated")
}if ("assumes_homogeneity" %in% names(conclusion)) {
return(if (conclusion$assumes_homogeneity) "Met" else "Violated")
}if ("statistical_evidence_for_independence" %in% names(conclusion)) {
return(if (conclusion$statistical_evidence_for_independence) "Met" else "Violated")
}
}
# Check individual test results for overall assessment
if (is.list(assumption_result)) {
<- sapply(assumption_result, function(x) {
test_results if ("conclusion" %in% names(x)) x$conclusion else NA
})
if (any(!is.na(test_results))) {
<- mean(test_results, na.rm = TRUE)
prop_met if (prop_met >= 0.8) return("Met")
if (prop_met >= 0.4) return("Borderline")
return("Violated")
}
}
return("Not Tested")
},
#' Assess outlier status
#'
#' @param outlier_result list. Outlier detection result
#'
#' @return character. Outlier status
#'
assess_outlier_status = function(outlier_result) {
<- 0
total_outliers <- 0
total_observations
for (var_result in outlier_result) {
if ("consensus" %in% names(var_result)) {
<- total_outliers + length(var_result$consensus$high_confidence_outliers)
total_outliers
# Estimate total observations
if ("iqr_method" %in% names(var_result)) {
<- var_result$iqr_method$outlier_count +
n_obs length(var_result$iqr_method$outlier_values)
<- total_observations + n_obs
total_observations
}
}
}
if (total_observations == 0) return("Not Tested")
<- total_outliers / total_observations
outlier_proportion
if (outlier_proportion > 0.10) return("Violated")
if (outlier_proportion > 0.05) return("Borderline")
return("Met")
},
#' Generate plot caption with analysis metadata
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return character. Plot caption
#'
generate_plot_caption = function(analysis_result, context) {
<- c()
caption_parts
# Analysis method
if (!is.null(analysis_result$method_selection)) {
<- c(caption_parts,
caption_parts paste("Method:", analysis_result$method_selection$method_info$name))
}
# Sample size
if (!is.null(analysis_result$primary_analysis$group_statistics)) {
<- analysis_result$primary_analysis$group_statistics
group_stats <- sum(sapply(group_stats, function(x) x$n))
total_n <- c(caption_parts, paste("n =", total_n))
caption_parts
}
# Generation timestamp
<- c(caption_parts,
caption_parts paste("Generated:", format(Sys.time(), "%Y-%m-%d %H:%M")))
# Custom caption from context
if ("caption" %in% names(context)) {
<- c(caption_parts, context$caption)
caption_parts
}
paste(caption_parts, collapse = " • ")
},
#' Create Q-Q plot panel for normality assessment
#'
#' @param normality_result list. Normality test results
#' @param context list. Plot context
#'
#' @return ggplot. Q-Q plot panel
#'
create_qq_plot_panel = function(normality_result, context) {
<- list()
qq_plots
for (var_name in names(normality_result)) {
if ("error" %in% names(normality_result[[var_name]])) next
# Extract variable data (would need to be passed in context)
if ("raw_data" %in% names(context)) {
<- context$raw_data[[var_name]]
var_data if (!is.null(var_data) && length(var_data) > 3) {
# Create Q-Q plot data
<- data.frame(
qq_data theoretical = qnorm(ppoints(length(var_data))),
sample = sort(var_data)
)
# Create Q-Q plot
<- ggplot(qq_data, aes(x = theoretical, y = sample)) +
p geom_point(alpha = 0.6, color = "steelblue", size = 2) +
geom_qq_line(color = "red", size = 1, linetype = "dashed") +
$theme_manager$get_publication_theme() +
selflabs(
title = paste("Q-Q Plot:", var_name),
x = "Theoretical Quantiles",
y = "Sample Quantiles",
subtitle = if ("shapiro_wilk" %in% names(normality_result[[var_name]])) {
<- normality_result[[var_name]]$shapiro_wilk
sw paste0("Shapiro-Wilk: W = ", round(sw$statistic, 4),
", p = ", format.pval(sw$p_value, digits = 3))
else NULL
}
)
<- p
qq_plots[[var_name]]
}
}
}
return(qq_plots)
},
#' Create residual analysis plot
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return ggplot. Residual analysis plot
#'
create_residual_analysis_plot = function(analysis_result, context) {
# This would require residual data from the analysis
# For t-test, we can create group-wise residual plots
if (!"raw_data" %in% names(context)) {
return(NULL)
}
<- context$raw_data
raw_data <- analysis_result$primary_analysis$group_statistics
group_stats
# Calculate residuals (deviations from group means)
<- raw_data
residual_data for (group_name in names(group_stats)) {
<- residual_data$group == group_name
group_indices <- group_stats[[group_name]]$mean
group_mean $residuals[group_indices] <-
residual_data$response[group_indices] - group_mean
residual_data$fitted[group_indices] <- group_mean
residual_data
}
# Create residual plot
<- ggplot(residual_data, aes(x = fitted, y = residuals, color = group)) +
p geom_point(size = 2.5, alpha = 0.7) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray60", size = 1) +
geom_smooth(method = "loess", se = FALSE, size = 1, alpha = 0.8) +
scale_color_manual(values = self$config$themes$color_palette$primary) +
$theme_manager$get_publication_theme() +
selffacet_wrap(~ group, scales = "free_x") +
labs(
title = "Residual Analysis",
subtitle = "Checking homogeneity of variance assumption",
x = "Fitted Values (Group Means)",
y = "Residuals",
color = "Group"
+
) theme(legend.position = "none") # Redundant with facets
return(p)
},
#' Create outlier detection plot
#'
#' @param outlier_result list. Outlier detection results
#' @param context list. Plot context
#'
#' @return ggplot. Outlier detection visualization
#'
create_outlier_detection_plot = function(outlier_result, context) {
<- list()
outlier_plots
for (var_name in names(outlier_result)) {
<- outlier_result[[var_name]]
var_result
if ("consensus" %in% names(var_result) && "raw_data" %in% names(context)) {
# Get variable data
<- context$raw_data[[var_name]]
var_data if (is.null(var_data)) next
# Prepare plot data
<- data.frame(
plot_data Index = seq_along(var_data),
Value = var_data,
Outlier_Status = "Normal"
)
# Mark outliers
<- var_result$consensus$high_confidence_outliers
consensus_outliers if (length(consensus_outliers) > 0) {
$Outlier_Status[consensus_outliers] <- "High Confidence Outlier"
plot_data
}
<- var_result$consensus$all_potential_outliers
all_outliers <- setdiff(all_outliers, consensus_outliers)
potential_only if (length(potential_only) > 0) {
$Outlier_Status[potential_only] <- "Potential Outlier"
plot_data
}
$Outlier_Status <- factor(plot_data$Outlier_Status,
plot_datalevels = c("Normal", "Potential Outlier", "High Confidence Outlier"))
# Create outlier plot
<- ggplot(plot_data, aes(x = Index, y = Value, color = Outlier_Status)) +
p geom_point(size = 2.5, alpha = 0.8) +
scale_color_manual(values = c("Normal" = "steelblue",
"Potential Outlier" = "orange",
"High Confidence Outlier" = "red")) +
$theme_manager$get_publication_theme() +
selflabs(
title = paste("Outlier Detection:", var_name),
subtitle = paste(length(consensus_outliers), "high-confidence outliers detected"),
x = "Observation Index",
y = "Value",
color = "Outlier Status"
+
) theme(legend.position = "bottom")
<- p
outlier_plots[[var_name]]
}
}
return(outlier_plots)
},
#' Create distribution comparison plot
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return ggplot. Distribution comparison
#'
create_distribution_comparison_plot = function(analysis_result, context) {
if (!"raw_data" %in% names(context)) {
return(NULL)
}
<- context$raw_data
raw_data
# Create density plot with overlaid distributions
<- ggplot(raw_data, aes(x = response, fill = group, color = group)) +
p geom_density(alpha = 0.3, size = 1.2) +
geom_rug(aes(color = group), alpha = 0.6, sides = "b") +
scale_fill_manual(values = self$config$themes$color_palette$primary) +
scale_color_manual(values = self$config$themes$color_palette$primary) +
$theme_manager$get_publication_theme() +
selflabs(
title = "Distribution Comparison",
subtitle = paste("Comparing group distributions •",
$primary_analysis$method_name),
analysis_resultx = "Response Value",
y = "Density",
fill = "Group",
color = "Group"
+
) theme(legend.position = "top")
# Add group means as vertical lines
<- analysis_result$primary_analysis$group_statistics
group_stats <- data.frame(
mean_data Group = names(group_stats),
Mean = sapply(group_stats, function(x) x$mean)
)
<- p +
p geom_vline(data = mean_data, aes(xintercept = Mean, color = Group),
linetype = "dashed", size = 1, alpha = 0.8) +
geom_text(data = mean_data,
aes(x = Mean, label = paste("Mean =", round(Mean, 2)), color = Group),
y = Inf, vjust = 1.2, hjust = -0.1, size = 3.5, angle = 90)
return(p)
}
)
)
#' Plot Theme Manager
#'
#' @description Manages professional themes and styling for enterprise plots
#'
#' @export
<- R6::R6Class(
PlotThemeManager "PlotThemeManager",
public = list(
#' @field config theme configuration
config = NULL,
#' Initialize theme manager
#'
#' @param config list. Theme configuration
#'
initialize = function(config) {
$config <- config
self
},
#' Get publication-ready theme
#'
#' @return theme. ggplot2 theme object
#'
get_publication_theme = function() {
theme_minimal(base_family = self$config$base_family,
base_size = self$config$base_size) +
theme(
# Text elements
plot.title = element_text(size = rel(1.3), face = "bold",
margin = margin(b = 20)),
plot.subtitle = element_text(size = rel(1.1), color = "gray40",
margin = margin(b = 15)),
plot.caption = element_text(size = rel(0.8), color = "gray50",
hjust = 0),
# Axis elements
axis.title = element_text(size = rel(1.1), face = "bold"),
axis.text = element_text(size = rel(0.9)),
axis.ticks = element_line(color = "gray80", size = 0.5),
# Grid elements
panel.grid.major = element_line(color = "gray95", size = 0.5),
panel.grid.minor = element_line(color = "gray98", size = 0.3),
# Legend elements
legend.position = "top",
legend.title = element_text(size = rel(1.0), face = "bold"),
legend.text = element_text(size = rel(0.9)),
legend.margin = margin(t = 10, b = 10),
# Strip elements (for facets)
strip.text = element_text(size = rel(1.0), face = "bold",
margin = margin(t = 5, b = 5)),
strip.background = element_rect(fill = "gray95", color = NA),
# Panel elements
panel.border = element_rect(color = "gray80", fill = NA, size = 0.5),
panel.spacing = unit(1, "lines"),
# Overall plot elements
plot.margin = margin(t = 20, r = 20, b = 20, l = 20)
)
},
#' Get minimal theme for dashboards
#'
#' @return theme. ggplot2 theme object
#'
get_minimal_theme = function() {
theme_void(base_family = self$config$base_family,
base_size = self$config$base_size) +
theme(
plot.title = element_text(size = rel(1.2), face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = rel(1.0), color = "gray40", hjust = 0.5),
axis.text.x = element_text(size = rel(0.9)),
legend.position = "bottom",
plot.margin = margin(t = 10, r = 10, b = 10, l = 10)
)
}
) )
Interactive plotly Integration
Advanced Interactive Visualization System
Enhance static plots with sophisticated interactivity using plotly:
# File: R/interactive_plotting_engine.R
#' Interactive Plotting Engine with plotly Integration
#'
#' @description Advanced interactive visualization system with plotly integration,
#' cross-plot linking, and context-aware tooltips for enterprise applications.
#'
#' @export
<- R6::R6Class(
InteractivePlottingEngine "InteractivePlottingEngine",
public = list(
#' @field config interactive plotting configuration
config = NULL,
#' @field static_engine static plotting engine for base plots
static_engine = NULL,
#' Initialize interactive plotting engine
#'
#' @param config list. Interactive plotting configuration
#' @param static_engine ProfessionalPlottingEngine. Static plotting engine
#'
initialize = function(config = list(), static_engine = NULL) {
$config <- private$default_interactive_config(config)
self$static_engine <- static_engine
self
# Load plotly
if (!requireNamespace("plotly", quietly = TRUE)) {
stop("plotly package required for interactive plotting")
}
},
#' Create interactive plot suite
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param plot_types character vector. Plot types to create
#' @param context list. Interactive context
#'
#' @return list. Interactive plotly objects
#'
create_interactive_suite = function(analysis_result, plot_types = "all", context = list()) {
<- list()
interactive_plots
# Available interactive plot types
<- c(
available_types "interactive_comparison",
"diagnostic_explorer",
"assumption_inspector",
"effect_size_explorer",
"data_explorer"
)
if ("all" %in% plot_types) {
<- available_types
plot_types
}
for (plot_type in plot_types) {
<- switch(plot_type,
plot_result "interactive_comparison" = self$create_interactive_comparison(analysis_result, context),
"diagnostic_explorer" = self$create_diagnostic_explorer(analysis_result, context),
"assumption_inspector" = self$create_assumption_inspector(analysis_result, context),
"effect_size_explorer" = self$create_effect_size_explorer(analysis_result, context),
"data_explorer" = self$create_data_explorer(analysis_result, context),
NULL
)
if (!is.null(plot_result)) {
<- plot_result
interactive_plots[[plot_type]]
}
}
return(interactive_plots)
},
#' Create interactive comparison plot
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return plotly object. Interactive comparison plot
#'
create_interactive_comparison = function(analysis_result, context = list()) {
# Get base static plot
<- self$static_engine$create_summary_comparison_plot(analysis_result, context)
base_plot
# Convert to interactive with enhanced tooltips
<- plotly::ggplotly(base_plot, tooltip = "text") %>%
p_interactive ::layout(
plotlytitle = list(
text = paste0(context$title %||% "Interactive Group Comparison",
"<br><sub>",
$primary_analysis$method_name,
analysis_result" • Click and drag to zoom • Double-click to reset</sub>"),
font = list(size = 16)
),
# Interactive controls
dragmode = "zoom",
showlegend = TRUE,
# Professional layout
margin = list(t = 80, r = 50, b = 60, l = 60),
# Annotations
annotations = list(
list(
x = 0.5, y = 1.15,
xref = "paper", yref = "paper",
text = paste0("p = ", format.pval(analysis_result$primary_analysis$p_value, digits = 4),
" • Cohen's d = ", round(analysis_result$primary_analysis$effect_size$cohens_d, 3)),
showarrow = FALSE,
font = list(size = 12, color = "gray60")
)
)%>%
)
# Add custom hover information
::add_annotations(
plotlyx = ~Group, y = ~Mean,
text = ~paste0("Group: ", Group, "<br>",
"Mean: ", round(Mean, 3), "<br>",
"SD: ", round(SD, 3), "<br>",
"n = ", N, "<br>",
"95% CI: [", round(CI_lower, 3), ", ", round(CI_upper, 3), "]"),
showarrow = FALSE,
bgcolor = "rgba(255,255,255,0.9)",
bordercolor = "gray",
borderwidth = 1
%>%
)
# Configuration options
::config(
plotlydisplayModeBar = TRUE,
modeBarButtons = list(list("zoom2d", "pan2d", "resetScale2d", "toImage")),
toImageButtonOptions = list(
format = "png",
filename = "group_comparison",
height = 600,
width = 800,
scale = 2
)
)
return(p_interactive)
},
#' Create diagnostic explorer
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return plotly object. Interactive diagnostic explorer
#'
create_diagnostic_explorer = function(analysis_result, context = list()) {
if (!"raw_data" %in% names(context)) {
return(NULL)
}
<- context$raw_data
raw_data <- analysis_result$assumption_tests
assumption_tests
# Create comprehensive diagnostic data
<- raw_data
diagnostic_data
# Add fitted values and residuals
<- analysis_result$primary_analysis$group_statistics
group_stats for (group_name in names(group_stats)) {
<- diagnostic_data$group == group_name
group_indices <- group_stats[[group_name]]$mean
group_mean $fitted[group_indices] <- group_mean
diagnostic_data$residuals[group_indices] <-
diagnostic_data$response[group_indices] - group_mean
diagnostic_data
}
# Add standardized residuals
$std_residuals <- scale(diagnostic_data$residuals)[,1]
diagnostic_data
# Add theoretical quantiles for Q-Q plot
<- diagnostic_data[order(diagnostic_data$residuals), ]
diagnostic_data $theoretical_quantiles <- qnorm(ppoints(nrow(diagnostic_data)))
diagnostic_data$sample_quantiles <- sort(diagnostic_data$residuals)
diagnostic_data
# Create subplot layout
<- plotly::plot_ly(diagnostic_data, x = ~fitted, y = ~residuals, color = ~group,
p1 type = "scatter", mode = "markers",
hovertemplate = "<b>Residual Analysis</b><br>" %+%
"Group: %{color}<br>" %+%
"Fitted: %{x:.3f}<br>" %+%
"Residual: %{y:.3f}<br>" %+%
"<extra></extra>") %>%
::layout(title = "Residuals vs Fitted",
plotlyxaxis = list(title = "Fitted Values"),
yaxis = list(title = "Residuals"))
<- plotly::plot_ly(diagnostic_data, x = ~theoretical_quantiles, y = ~sample_quantiles,
p2 type = "scatter", mode = "markers",
marker = list(color = "steelblue", opacity = 0.7),
hovertemplate = "<b>Q-Q Plot</b><br>" %+%
"Theoretical: %{x:.3f}<br>" %+%
"Sample: %{y:.3f}<br>" %+%
"<extra></extra>") %>%
::add_trace(x = ~theoretical_quantiles, y = ~theoretical_quantiles,
plotlytype = "scatter", mode = "lines",
line = list(color = "red", dash = "dash"),
name = "Reference Line",
hoverinfo = "skip") %>%
::layout(title = "Normal Q-Q Plot",
plotlyxaxis = list(title = "Theoretical Quantiles"),
yaxis = list(title = "Sample Quantiles"))
# Combine plots
<- plotly::subplot(p1, p2, nrows = 1, shareY = FALSE) %>%
combined_plot ::layout(
plotlytitle = list(
text = "Interactive Diagnostic Explorer",
font = list(size = 16)
),showlegend = TRUE,
margin = list(t = 80)
%>%
) ::config(
plotlydisplayModeBar = TRUE,
modeBarButtons = list(list("zoom2d", "pan2d", "resetScale2d", "toImage"))
)
return(combined_plot)
},
#' Create assumption inspector
#'
#' @param analysis_result StatisticalAnalysisResult. Analysis results
#' @param context list. Plot context
#'
#' @return plotly object. Interactive assumption inspector
#'
create_assumption_inspector = function(analysis_result, context = list()) {
<- analysis_result$assumption_tests
assumption_tests
# Create assumption summary data with detailed information
<- c("Normality", "Homogeneity", "Independence", "Outliers")
assumptions
<- data.frame(
assumption_data Assumption = factor(assumptions, levels = assumptions),
Status = sapply(assumptions, function(x) {
switch(tolower(x),
"normality" = if ("normality" %in% names(assumption_tests$assumption_results)) {
$get_assumption_detail(assumption_tests$assumption_results$normality, "normality")
privateelse list(status = "Not Tested", details = "No normality testing performed"),
}
"homogeneity" = if ("homogeneity" %in% names(assumption_tests$assumption_results)) {
$get_assumption_detail(assumption_tests$assumption_results$homogeneity, "homogeneity")
privateelse list(status = "Not Tested", details = "No homogeneity testing performed"),
}
"independence" = if ("independence" %in% names(assumption_tests$assumption_results)) {
$get_assumption_detail(assumption_tests$assumption_results$independence, "independence")
privateelse list(status = "Not Tested", details = "No independence testing performed"),
}
"outliers" = if ("outliers" %in% names(assumption_tests$assumption_results)) {
$get_assumption_detail(assumption_tests$assumption_results$outliers, "outliers")
privateelse list(status = "Not Tested", details = "No outlier detection performed")
}
)simplify = FALSE)
},
)
# Extract status and details
$StatusValue <- sapply(assumption_data$Status, function(x) x$status)
assumption_data$Details <- sapply(assumption_data$Status, function(x) x$details)
assumption_data
# Color mapping
<- c(
status_colors "Met" = "#28a745",
"Violated" = "#dc3545",
"Borderline" = "#ffc107",
"Not Tested" = "#6c757d"
)
$Color <- status_colors[assumption_data$StatusValue]
assumption_data
# Create interactive heatmap-style plot
<- plotly::plot_ly(
p
assumption_data,x = ~Assumption,
y = rep(1, nrow(assumption_data)),
z = ~as.numeric(factor(StatusValue, levels = names(status_colors))),
type = "heatmap",
colorscale = list(
c(0, status_colors["Not Tested"]),
c(0.33, status_colors["Violated"]),
c(0.66, status_colors["Borderline"]),
c(1, status_colors["Met"])
),hovertemplate = "<b>%{x}</b><br>" %+%
"Status: %{customdata[0]}<br>" %+%
"%{customdata[1]}<br>" %+%
"<extra></extra>",
customdata = ~cbind(StatusValue, Details),
showscale = FALSE
%>%
) ::layout(
plotlytitle = list(
text = "Interactive Assumption Assessment<br><sub>Hover for detailed test results</sub>",
font = list(size = 16)
),xaxis = list(title = "", tickfont = list(size = 12)),
yaxis = list(title = "", showticklabels = FALSE, showgrid = FALSE),
margin = list(t = 80, b = 50)
%>%
) ::config(displayModeBar = FALSE)
plotly
return(p)
}
),
private = list(
#' Default interactive configuration
#'
#' @param config list. User configuration
#'
#' @return list. Complete configuration
#'
default_interactive_config = function(config) {
<- list(
default_config
plotly_options = list(
default_height = 500,
default_width = 800,
show_modebar = TRUE,
responsive = TRUE
),
tooltip_config = list(
show_statistical_details = TRUE,
show_sample_info = TRUE,
show_confidence_intervals = TRUE,
precision = 3
),
interaction_features = list(
enable_zoom = TRUE,
enable_pan = TRUE,
enable_select = TRUE,
enable_crossfilter = FALSE,
enable_brush_linking = FALSE
),
export_options = list(
enable_download = TRUE,
default_format = "png",
high_res_scale = 2,
include_plotly_logo = FALSE
)
)
modifyList(default_config, config)
},
#' Get detailed assumption information
#'
#' @param assumption_result list. Assumption test result
#' @param assumption_type character. Type of assumption
#'
#' @return list. Status and details
#'
get_assumption_detail = function(assumption_result, assumption_type) {
switch(assumption_type,
"normality" = {
if ("overall_conclusion" %in% names(assumption_result)) {
<- if (assumption_result$overall_conclusion$assumes_normal) "Met" else "Violated"
status <- paste("Tests performed:",
details $overall_conclusion$n_tests,
assumption_result"• Consensus:",
round(assumption_result$overall_conclusion$consensus * 100, 1), "%")
else {
} <- "Borderline"
status <- "Partial testing completed"
details
}
},
"homogeneity" = {
if ("overall_conclusion" %in% names(assumption_result)) {
<- if (assumption_result$overall_conclusion$assumes_homogeneity) "Met" else "Violated"
status <- assumption_result$overall_conclusion$recommendation
details else {
} <- "Not Tested"
status <- "Homogeneity testing unavailable"
details
}
},
"independence" = {
if ("overall_conclusion" %in% names(assumption_result)) {
<- if (assumption_result$overall_conclusion$statistical_evidence_for_independence) "Met" else "Violated"
status <- "Primary concern: Study design • Statistical tests supplementary"
details else {
} <- "Met"
status <- "Assumed based on study design"
details
}
},
"outliers" = {
<- 0
total_outliers <- 0
total_observations
for (var_result in assumption_result) {
if ("consensus" %in% names(var_result)) {
<- total_outliers + length(var_result$consensus$high_confidence_outliers)
total_outliers # Estimate total observations
if ("iqr_method" %in% names(var_result)) {
<- var_result$iqr_method$outlier_count +
n_obs length(var_result$iqr_method$outlier_values)
<- total_observations + n_obs
total_observations
}
}
}
if (total_observations == 0) {
list(status = "Not Tested", details = "No outlier detection performed")
else {
} <- total_outliers / total_observations
outlier_proportion <- if (outlier_proportion > 0.10) "Violated" else if (outlier_proportion > 0.05) "Borderline" else "Met"
status <- paste0(total_outliers, " outliers detected (",
details round(outlier_proportion * 100, 1), "% of observations)")
}
}
)
list(status = status, details = details)
}
) )
Export and Documentation System
Professional Export Management
Create a comprehensive export system for high-quality outputs:
# File: R/plot_export_manager.R
#' Plot Export Manager
#'
#' @description Professional export system with multiple format support,
#' resolution control, and regulatory compliance features.
#'
#' @export
<- R6::R6Class(
PlotExportManager "PlotExportManager",
public = list(
#' @field config export configuration
config = NULL,
#' Initialize export manager
#'
#' @param config list. Export configuration
#'
initialize = function(config) {
$config <- config
self
},
#' Export plot suite in multiple formats
#'
#' @param plot_suite StatisticalPlotSuite. Plot suite to export
#' @param export_specs list. Export specifications
#' @param output_dir character. Output directory
#'
#' @return list. Export results and file paths
#'
export_plot_suite = function(plot_suite, export_specs = list(), output_dir = "plots") {
# Create output directory if it doesn't exist
if (!dir.exists(output_dir)) {
dir.create(output_dir, recursive = TRUE)
}
<- list()
export_results
# Default export specifications
<- list(
default_specs formats = c("png", "pdf", "svg"),
resolution = "publication", # publication, web, print
size_preset = "double_column", # single_column, double_column, full_page, presentation
include_metadata = TRUE,
create_summary_document = TRUE
)
<- modifyList(default_specs, export_specs)
export_specs
# Export individual plots
for (plot_name in names(plot_suite$plots)) {
<- plot_suite$plots[[plot_name]]
plot_obj
<- private$export_single_plot(
plot_exports plot = plot_obj,
plot_name = plot_name,
analysis_id = plot_suite$analysis_id,
export_specs = export_specs,
output_dir = output_dir
)
<- plot_exports
export_results[[plot_name]]
}
# Create summary document if requested
if (export_specs$create_summary_document) {
<- private$create_export_summary(
summary_doc plot_suite = plot_suite,
export_results = export_results,
output_dir = output_dir
)$summary_document <- summary_doc
export_results
}
return(export_results)
},
#' Export interactive plots
#'
#' @param interactive_plots list. Interactive plotly objects
#' @param export_specs list. Export specifications
#' @param output_dir character. Output directory
#'
#' @return list. Export results
#'
export_interactive_plots = function(interactive_plots, export_specs = list(), output_dir = "interactive_plots") {
if (!dir.exists(output_dir)) {
dir.create(output_dir, recursive = TRUE)
}
<- list()
export_results
for (plot_name in names(interactive_plots)) {
<- interactive_plots[[plot_name]]
plot_obj
# Export as HTML widget
<- file.path(output_dir, paste0(plot_name, ".html"))
html_file
tryCatch({
::saveWidget(
htmlwidgetswidget = plot_obj,
file = html_file,
selfcontained = TRUE,
title = paste("Interactive Plot:", plot_name)
)
<- list(
export_results[[plot_name]] format = "html",
file_path = html_file,
success = TRUE,
file_size = file.info(html_file)$size
)
# Also export static version for compatibility
<- file.path(output_dir, paste0(plot_name, "_static.png"))
static_file if (requireNamespace("webshot2", quietly = TRUE)) {
::webshot(html_file, static_file,
webshot2width = 1200, height = 800, zoom = 2)
$static_version <- static_file
export_results[[plot_name]]
}
error = function(e) {
}, <- list(
export_results[[plot_name]] format = "html",
file_path = NA,
success = FALSE,
error = e$message
)
})
}
return(export_results)
}
),
private = list(
#' Export single plot in multiple formats
#'
#' @param plot ggplot. Plot object
#' @param plot_name character. Plot name
#' @param analysis_id character. Analysis identifier
#' @param export_specs list. Export specifications
#' @param output_dir character. Output directory
#'
#' @return list. Export results for single plot
#'
export_single_plot = function(plot, plot_name, analysis_id, export_specs, output_dir) {
<- list()
plot_exports
# Get dimensions and resolution
<- private$get_plot_dimensions(export_specs$size_preset)
dimensions <- private$get_plot_resolution(export_specs$resolution)
resolution
# Create base filename
<- paste0(analysis_id, "_", plot_name)
base_filename
for (format in export_specs$formats) {
<- paste0(base_filename, ".", format)
filename <- file.path(output_dir, filename)
filepath
tryCatch({
switch(format,
"png" = {
::ggsave(
ggplot2filename = filepath,
plot = plot,
width = dimensions$width,
height = dimensions$height,
dpi = resolution,
bg = "white"
)
},
"pdf" = {
::ggsave(
ggplot2filename = filepath,
plot = plot,
width = dimensions$width,
height = dimensions$height,
device = "pdf",
useDingbats = FALSE # For better compatibility
)
},
"svg" = {
::ggsave(
ggplot2filename = filepath,
plot = plot,
width = dimensions$width,
height = dimensions$height,
device = "svg"
)
},
"eps" = {
::ggsave(
ggplot2filename = filepath,
plot = plot,
width = dimensions$width,
height = dimensions$height,
device = "eps"
)
}
)
# Add metadata if requested
if (export_specs$include_metadata) {
$add_plot_metadata(filepath, plot_name, analysis_id, format)
private
}
<- list(
plot_exports[[format]] file_path = filepath,
success = TRUE,
file_size = file.info(filepath)$size,
dimensions = dimensions,
resolution = resolution
)
error = function(e) {
}, <- list(
plot_exports[[format]] file_path = filepath,
success = FALSE,
error = e$message
)
})
}
return(plot_exports)
},
#' Get plot dimensions based on preset
#'
#' @param size_preset character. Size preset name
#'
#' @return list. Width and height in inches
#'
get_plot_dimensions = function(size_preset) {
<- switch(size_preset,
dimensions "single_column" = list(width = 3.5, height = 2.5),
"double_column" = list(width = 7, height = 5),
"full_page" = list(width = 8.5, height = 6.5),
"presentation" = list(width = 10, height = 7.5),
"square" = list(width = 6, height = 6),
# Default to double column
list(width = 7, height = 5)
)
return(dimensions)
},
#' Get plot resolution based on use case
#'
#' @param resolution_preset character. Resolution preset
#'
#' @return numeric. DPI value
#'
get_plot_resolution = function(resolution_preset) {
<- switch(resolution_preset,
dpi "web" = 150,
"publication" = 300,
"print" = 600,
"presentation" = 200,
# Default to publication quality
300
)
return(dpi)
},
#' Add metadata to exported plot file
#'
#' @param filepath character. File path
#' @param plot_name character. Plot name
#' @param analysis_id character. Analysis ID
#' @param format character. File format
#'
add_plot_metadata = function(filepath, plot_name, analysis_id, format) {
# Create metadata file
<- paste0(tools::file_path_sans_ext(filepath), "_metadata.txt")
metadata_file
<- c(
metadata paste("Plot Name:", plot_name),
paste("Analysis ID:", analysis_id),
paste("Export Format:", format),
paste("Export Date:", Sys.time()),
paste("R Version:", R.version.string),
paste("ggplot2 Version:", utils::packageVersion("ggplot2")),
paste("File Size:", file.info(filepath)$size, "bytes")
)
writeLines(metadata, metadata_file)
},
#' Create comprehensive export summary document
#'
#' @param plot_suite StatisticalPlotSuite. Plot suite
#' @param export_results list. Export results
#' @param output_dir character. Output directory
#'
#' @return character. Summary document path
#'
create_export_summary = function(plot_suite, export_results, output_dir) {
<- file.path(output_dir, "export_summary.md")
summary_file
# Create markdown summary
<- c(
summary_content "# Statistical Plot Export Summary",
"",
paste("**Analysis ID:**", plot_suite$analysis_id),
paste("**Export Date:**", Sys.time()),
paste("**Total Plots:**", length(plot_suite$plots)),
paste("**Generation Time:**", round(plot_suite$generation_time, 3), "seconds"),
"",
"## Exported Files",
""
)
for (plot_name in names(export_results)) {
if (plot_name == "summary_document") next
<- c(summary_content,
summary_content paste("### ", plot_name),
""
)
<- export_results[[plot_name]]
plot_exports for (format in names(plot_exports)) {
<- plot_exports[[format]]
export_info if (export_info$success) {
<- c(summary_content,
summary_content paste("- **", toupper(format), ":**", basename(export_info$file_path),
"(" , round(export_info$file_size / 1024, 1), "KB)")
)else {
} <- c(summary_content,
summary_content paste("- **", toupper(format), ":** Export failed -", export_info$error)
)
}
}
<- c(summary_content, "")
summary_content
}
# Add technical details
<- c(summary_content,
summary_content "## Technical Details",
"",
paste("- **R Version:**", R.version.string),
paste("- **ggplot2 Version:**", utils::packageVersion("ggplot2")),
paste("- **Export Configuration:**", plot_suite$config$export$formats),
""
)
writeLines(summary_content, summary_file)
return(summary_file)
}
)
)
#' Statistical Plot Suite Class
#'
#' @description Container for comprehensive statistical plot collections
#' with metadata and export capabilities.
#'
#' @export
<- R6::R6Class(
StatisticalPlotSuite "StatisticalPlotSuite",
public = list(
#' @field plot_id character. Unique plot suite identifier
plot_id = NULL,
#' @field analysis_id character. Associated analysis identifier
analysis_id = NULL,
#' @field plots list. Collection of ggplot objects
plots = NULL,
#' @field generation_time numeric. Time taken to generate plots
generation_time = NULL,
#' @field context list. Plot generation context
context = NULL,
#' @field config list. Plot configuration used
config = NULL,
#' Initialize plot suite
#'
#' @param plot_id character. Plot suite ID
#' @param analysis_id character. Analysis ID
#' @param plots list. Plot objects
#' @param generation_time numeric. Generation time
#' @param context list. Context information
#' @param config list. Configuration used
#'
initialize = function(plot_id, analysis_id, plots, generation_time, context, config) {
$plot_id <- plot_id
self$analysis_id <- analysis_id
self$plots <- plots
self$generation_time <- generation_time
self$context <- context
self$config <- config
self
},
#' Get plot suite summary
#'
#' @return character. Summary information
#'
get_summary = function() {
paste0(
"Statistical Plot Suite\n",
"======================\n",
"Plot ID: ", self$plot_id, "\n",
"Analysis ID: ", self$analysis_id, "\n",
"Number of Plots: ", length(self$plots), "\n",
"Plot Types: ", paste(names(self$plots), collapse = ", "), "\n",
"Generation Time: ", round(self$generation_time, 3), " seconds\n"
)
},
#' Display all plots
#'
#' @param ncol numeric. Number of columns for display
#'
display_all = function(ncol = 2) {
if (requireNamespace("gridExtra", quietly = TRUE)) {
::grid.arrange(grobs = self$plots, ncol = ncol)
gridExtraelse {
} for (plot_name in names(self$plots)) {
print(self$plots[[plot_name]])
cat("Plot:", plot_name, "\n\n")
}
}
}
) )
Integration with Enhanced t-Test Application
Complete Visualization Integration
Update the enhanced t-test application to use the advanced visualization system:
# Enhanced t-test server with advanced visualization integration
<- function(id, audit_logger = NULL) {
enhanced_ttest_visualization_server moduleServer(id, function(input, output, session) {
# Initialize visualization engines
<- ProfessionalPlottingEngine$new(
plotting_engine config = list(
themes = list(
color_palette = list(
primary = c("#2E86AB", "#A23B72", "#F18F01", "#C73E1D"),
clinical = c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728")
)
)
),audit_logger = audit_logger
)
<- InteractivePlottingEngine$new(
interactive_engine config = list(
plotly_options = list(
default_height = 500,
responsive = TRUE
)
),static_engine = plotting_engine
)
# Reactive values for plots
<- reactiveValues(
values plot_suite = NULL,
interactive_plots = NULL
)
# Generate comprehensive plot suite when analysis completes
observeEvent(input$run_analysis, {
req(values$analysis_result)
if (!values$analysis_result$success) return()
showNotification("Generating professional visualizations...",
type = "message", id = "plotting")
tryCatch({
# Prepare visualization context
<- list(
plot_context title = "Statistical Analysis Results",
x_label = input$x_label %||% "Group",
y_label = input$y_label %||% "Response",
show_points = input$show_individual_points %||% TRUE,
raw_data = dataset(), # Include raw data for diagnostic plots
clinical_context = TRUE
)
# Generate static plot suite
<- plotting_engine$create_plot_suite(
plot_suite analysis_result = values$analysis_result,
plot_types = c("summary_comparison", "diagnostic_suite",
"effect_size_forest", "assumption_dashboard"),
context = plot_context
)
$plot_suite <- plot_suite
values
# Generate interactive plots
<- interactive_engine$create_interactive_suite(
interactive_plots analysis_result = values$analysis_result,
plot_types = c("interactive_comparison", "diagnostic_explorer",
"assumption_inspector"),
context = plot_context
)
$interactive_plots <- interactive_plots
values
removeNotification("plotting")
showNotification("Visualizations generated successfully",
type = "success", duration = 3)
error = function(e) {
}, removeNotification("plotting")
showNotification(paste("Visualization error:", e$message),
type = "error", duration = 10)
})
})
# Render static plots
$summary_comparison_plot <- renderPlot({
outputreq(values$plot_suite)
if ("summary_comparison" %in% names(values$plot_suite$plots)) {
$plot_suite$plots$summary_comparison
values
}height = 500)
},
$effect_size_plot <- renderPlot({
outputreq(values$plot_suite)
if ("effect_size_forest" %in% names(values$plot_suite$plots)) {
$plot_suite$plots$effect_size_forest
values
}height = 400)
},
$assumption_dashboard_plot <- renderPlot({
outputreq(values$plot_suite)
if ("assumption_dashboard" %in% names(values$plot_suite$plots)) {
$plot_suite$plots$assumption_dashboard
values
}height = 300)
},
# Render interactive plots
$interactive_comparison <- plotly::renderPlotly({
outputreq(values$interactive_plots)
if ("interactive_comparison" %in% names(values$interactive_plots)) {
$interactive_plots$interactive_comparison
values
}
})
$diagnostic_explorer <- plotly::renderPlotly({
outputreq(values$interactive_plots)
if ("diagnostic_explorer" %in% names(values$interactive_plots)) {
$interactive_plots$diagnostic_explorer
values
}
})
$assumption_inspector <- plotly::renderPlotly({
outputreq(values$interactive_plots)
if ("assumption_inspector" %in% names(values$interactive_plots)) {
$interactive_plots$assumption_inspector
values
}
})
# Export functionality
$download_plots <- downloadHandler(
outputfilename = function() {
paste0("statistical_plots_", Sys.Date(), ".zip")
},content = function(file) {
req(values$plot_suite)
# Create temporary directory
<- tempdir()
temp_dir <- file.path(temp_dir, "statistical_plots")
plot_dir
# Export plot suite
<- PlotExportManager$new(plotting_engine$config$export)
export_manager
<- export_manager$export_plot_suite(
export_results plot_suite = values$plot_suite,
export_specs = list(
formats = c("png", "pdf"),
resolution = "publication",
size_preset = "double_column",
include_metadata = TRUE
),output_dir = plot_dir
)
# Export interactive plots if available
if (!is.null(values$interactive_plots)) {
<- file.path(plot_dir, "interactive")
interactive_dir $export_interactive_plots(
export_managerinteractive_plots = values$interactive_plots,
output_dir = interactive_dir
)
}
# Create zip file
<- list.files(plot_dir, recursive = TRUE, full.names = TRUE)
zip_files <- list.files(plot_dir, recursive = TRUE)
zip_names
::zip(file, files = zip_files, root = plot_dir)
zip
},contentType = "application/zip"
)
# Plot customization UI
$plot_controls <- renderUI({
output
tagList(
card(
card_header("Plot Customization"),
card_body(
fluidRow(
column(6,
selectInput(ns("color_palette"), "Color Palette:",
choices = list(
"Clinical" = "clinical",
"Professional" = "primary",
"Colorblind Safe" = "colorblind_friendly",
"Grayscale" = "grayscale"
),selected = "clinical")
),column(6,
selectInput(ns("plot_theme"), "Plot Theme:",
choices = list(
"Publication" = "publication",
"Minimal" = "minimal",
"Classic" = "classic"
),selected = "publication")
)
),
fluidRow(
column(6,
checkboxInput(ns("show_individual_points"),
"Show Individual Data Points", value = TRUE)
),column(6,
checkboxInput(ns("show_confidence_intervals"),
"Show Confidence Intervals", value = TRUE)
)
),
hr(),
h6("Export Options"),
checkboxGroupInput(ns("export_formats"), "Export Formats:",
choices = list("PNG" = "png", "PDF" = "pdf", "SVG" = "svg"),
selected = c("png", "pdf")),
selectInput(ns("export_resolution"), "Export Quality:",
choices = list(
"Web (150 DPI)" = "web",
"Publication (300 DPI)" = "publication",
"Print (600 DPI)" = "print"
),selected = "publication"),
downloadButton(ns("download_plots"), "Download All Plots",
class = "btn-primary w-100")
)
)
)
})
}) }
Common Questions About Advanced Visualizations
Advanced visualizations transform statistical results from technical outputs into compelling communication tools that drive decision-making across diverse stakeholder groups. Professional visualizations provide immediate visual assessment of statistical significance, effect sizes, and assumption validity, enabling non-technical stakeholders to understand analytical conclusions quickly. Interactive elements allow exploration of underlying data patterns and diagnostic information without requiring statistical expertise. The combination of publication-ready static plots and interactive exploration tools ensures that statistical findings can be effectively communicated in regulatory submissions, peer-reviewed publications, executive presentations, and clinical decision-making contexts.
Plotly integration adds sophisticated interactivity that enhances analytical insight while maintaining statistical rigor. Interactive tooltips provide context-aware information including confidence intervals, sample sizes, and statistical test details that would clutter static plots. Zoom and pan capabilities allow detailed examination of data patterns and outliers that might be missed in fixed-scale displays. Cross-plot linking enables comprehensive exploration of relationships between different analytical perspectives. The export capabilities ensure that interactive insights can be preserved and shared while maintaining compatibility with traditional publication workflows. This interactivity is particularly valuable for diagnostic plots where pattern recognition and assumption assessment benefit from dynamic exploration.
The export system addresses the diverse documentation requirements of regulated industries and academic publishing through multiple format support, resolution control, and comprehensive metadata tracking. High-resolution outputs meet journal specifications for print reproduction while web-optimized versions support digital distribution. PDF and SVG formats ensure vector graphics quality for scalable reproduction. Metadata documentation provides audit trails including analysis identifiers, generation timestamps, and software versions required for regulatory validation. The batch export capabilities maintain consistent formatting across plot suites while automated summary documentation provides comprehensive records of visualization generation for regulatory review and publication supplementary materials.
Comprehensive diagnostic visualization suites provide visual assessment of statistical assumptions that complement formal statistical tests. Q-Q plots enable visual assessment of normality assumptions with reference lines and confidence bands for interpretation guidance. Residual plots reveal patterns indicating assumption violations including heteroscedasticity, non-linearity, and influential observations. Interactive outlier detection visualizations allow examination of extreme values with multiple detection methods and consensus indicators. Distribution comparison plots provide direct visual assessment of group differences and distributional assumptions. The combination of static diagnostic plots for documentation and interactive exploration tools for detailed investigation ensures thorough assumption assessment while maintaining audit trail requirements for regulatory compliance.
Effective visualization customization systems provide flexibility within professionally constrained parameters to ensure both usability and regulatory compliance. Color palette options include colorblind-safe alternatives and high-contrast modes for accessibility compliance while maintaining professional appearance standards. Theme customization allows adaptation to organizational branding requirements while preserving statistical element placement and readability standards. Export configuration enables format and resolution optimization for specific use cases while maintaining metadata and audit trail requirements. The key is providing meaningful choices that enhance communication effectiveness without compromising statistical integrity or professional presentation standards required for regulatory and publication contexts.
Test Your Understanding
You’re preparing statistical visualizations for a peer-reviewed journal submission. Which elements are essential for publication-ready statistical plots? Select all that apply:
- High-resolution output (300+ DPI) for print reproduction
- Interactive tooltips with detailed statistical information
- Clear statistical annotations (p-values, effect sizes, confidence intervals)
- Colorblind-safe color palettes for accessibility
- Professional typography and consistent theme styling
- Comprehensive figure captions with methodology details
- Export metadata for reproducibility documentation
- Consider what journal editors and reviewers expect to see
- Think about accessibility and reproducibility requirements
- Consider the difference between interactive tools and publication outputs
A, C, D, E, F, and G are essential for publication-ready visualizations.
Essential elements explained:
- A) High-resolution output: Required for print journals - typically 300 DPI minimum for figures
- C) Statistical annotations: Essential for readers to interpret results without consulting text extensively
- D) Colorblind-safe palettes: Accessibility requirement for many journals and funding agencies
- E) Professional typography: Ensures readability and meets journal style guidelines
- F) Comprehensive captions: Required for figure interpretation and reproducibility
- G) Export metadata: Supports reproducibility and audit requirements
B) Interactive tooltips are valuable for analysis but not applicable to static publication figures. However, they’re essential for supplementary interactive materials or regulatory submissions where dynamic exploration adds value.
The key principle: Publication-ready visualizations must be self-contained, accessible, and professionally formatted while providing all information necessary for interpretation.
You’re implementing an interactive diagnostic suite for assumption testing. Your users need to explore normality violations and outlier patterns. Design the optimal interactive visualization approach:
What combination of features would be most effective for diagnostic exploration?
- Static Q-Q plots with hover tooltips showing theoretical vs. sample quantiles
- Linked plots where selecting outliers in one view highlights them across all diagnostic displays
- Separate tabs for each assumption with independent zoom/pan controls
- Combined dashboard with synchronized brushing across normality, outlier, and residual plots
- Downloadable diagnostic reports with static plots only
- Consider how analysts actually explore diagnostic information
- Think about the relationships between different types of assumption violations
- Consider workflow efficiency and insight discovery
D) Combined dashboard with synchronized brushing across normality, outlier, and residual plots
Rationale for optimal approach:
Why this works best: - Linked exploration: Outliers identified in one plot are immediately visible across all relevant diagnostics - Pattern recognition: Users can see how assumption violations relate to each other (e.g., outliers affecting normality) - Workflow efficiency: Single interface provides comprehensive assumption assessment without context switching - Insight discovery: Synchronized highlighting reveals relationships between different diagnostic indicators
Why other options are less optimal: - A) Static with tooltips: Provides information but lacks exploratory capability for pattern discovery - B) Linked plots only: Good but limited scope compared to comprehensive dashboard - C) Separate tabs: Creates workflow fragmentation and prevents relationship discovery - E) Static reports only: No interactive exploration capability for complex diagnostic assessment
Implementation considerations: Include brush selection, zoom synchronization, and contextual annotations that update across linked views to support comprehensive diagnostic workflow.
Your biostatistics team needs a comprehensive export strategy that serves multiple stakeholders: regulatory submissions (FDA), peer-reviewed publications, executive presentations, and clinical team discussions. Design the optimal export configuration:
- Different stakeholders have different format and quality requirements
- Consider audit trail and compliance needs
- Think about workflow efficiency and automation
Comprehensive Multi-Stakeholder Export Strategy:
Format Matrix by Use Case:
- Regulatory Submissions:
- Formats: PDF (vector), PNG (high-res backup)
- Resolution: 600 DPI (print quality)
- Metadata: Full audit trail with analysis IDs, timestamps, software versions
- Documentation: Automated compliance reports with assumption testing evidence
- Peer-Reviewed Publications:
- Formats: PDF, EPS, SVG (vector graphics for journals)
- Resolution: 300-600 DPI depending on journal requirements
- Styling: Journal-specific formatting, colorblind-safe palettes
- Captions: Comprehensive figure legends with statistical methodology details
- Executive Presentations:
- Formats: PNG (high-res), PowerPoint-compatible dimensions
- Resolution: 200-300 DPI (presentation quality)
- Styling: Corporate branding, simplified annotations
- Interactive versions: HTML widgets for board meeting tablets
- Clinical Team Discussions:
- Formats: PNG (standard), PDF (portable)
- Resolution: 150-200 DPI (web/screen optimized)
- Features: Clear interpretation guidance, clinical significance highlighting
- Interactive elements: Hover details for clinical context
Implementation Strategy:
# Export configuration for comprehensive stakeholder support
<- list(
export_configurations regulatory = list(
formats = c("pdf", "png"),
resolution = "print", # 600 DPI
size_preset = "full_page",
metadata = TRUE,
compliance_documentation = TRUE
),
publication = list(
formats = c("pdf", "eps", "svg"),
resolution = "publication", # 300 DPI
size_preset = "double_column",
colorblind_safe = TRUE,
journal_formatting = TRUE
),
executive = list(
formats = c("png", "html"),
resolution = "presentation", # 200 DPI
size_preset = "presentation",
corporate_branding = TRUE,
simplified_annotations = TRUE
),
clinical = list(
formats = c("png", "pdf"),
resolution = "web", # 150 DPI
size_preset = "single_column",
clinical_annotations = TRUE,
interpretation_guidance = TRUE
) )
Automation Benefits:
- Single analysis generates all required formats automatically
- Consistent quality standards across stakeholder groups
- Audit trail maintained for regulatory compliance
- Workflow efficiency through batch processing
Conclusion
Advanced statistical visualization transforms enterprise applications from basic analytical tools into sophisticated communication platforms that effectively bridge the gap between complex statistical analysis and stakeholder understanding. The comprehensive visualization framework you’ve implemented provides the foundation for creating publication-ready graphics, interactive exploration tools, and regulatory-compliant documentation that meets the demanding standards of biostatistics and clinical research applications.
Your enhanced t-test application now demonstrates how professional visualization systems can seamlessly integrate static and interactive elements while maintaining statistical rigor and regulatory compliance. These patterns extend across all statistical analyses, providing a scalable foundation for building comprehensive biostatistics platforms that serve diverse stakeholder needs from regulatory submissions to clinical decision-making.
The combination of ggplot2 mastery, plotly integration, and professional export capabilities positions your applications to compete with commercial statistical software while maintaining the flexibility and transparency that R-based solutions provide to regulated industries.
Next Steps
Based on your mastery of advanced visualization principles, here are the recommended paths for continuing your enterprise development journey:
Immediate Next Steps (Complete These First)
- Testing Framework and Validation - Implement comprehensive testing strategies that validate your visualization outputs and ensure reliability across different data scenarios and edge cases
- Enterprise Documentation Standards - Create professional documentation systems that support regulatory compliance and user training for your advanced visualization capabilities
- Practice Exercise: Extend your visualization framework to support ANOVA or regression analysis, implementing diagnostic plots and interactive exploration tools for more complex statistical methods
Building Your Visual Analytics Platform (Choose Your Focus)
For Clinical Integration:
For Production Excellence:
For Platform Development:
Long-term Goals (2-4 Weeks)
- Develop a complete biostatistics visualization library with standardized themes and export capabilities for your organization
- Create interactive dashboard systems that combine multiple statistical analyses with coordinated visualization displays
- Build custom visualization components that extend beyond standard statistical plots to support specialized clinical and regulatory requirements
- Contribute to the R community by developing and sharing enterprise-grade visualization extensions
Explore More Articles
Here are more articles from the same category to help you dive deeper into enterprise Shiny development.
Reuse
Citation
@online{kassambara2025,
author = {Kassambara, Alboukadel},
title = {Advanced {Visualizations:} {Professional} {Statistical}
{Plots} for {Enterprise} {Applications}},
date = {2025-05-23},
url = {https://www.datanovia.com/learn/tools/shiny-apps/enterprise-development/advanced-visualizations.html},
langid = {en}
}