如何用ggplot基于3D数组绘制带观测值折线的多面板箱线图
Absolutely! This is totally doable with ggplot2, but first we need to reshape your matrix and 3D array into a tidy (long-form) data structure—since ggplot works best with this format. Let's walk through the step-by-step solution:
Step 1: Load Required Packages
We'll use the tidyverse suite (includes ggplot2, dplyr, and tidyr for data manipulation):
library(tidyverse)
Step 2: Reshape Observed Data
Convert your observed matrix into a data frame with explicit columns for month, region, and observed value:
# Convert observed matrix to tidy data frame observed_df <- as.data.frame(observed) %>% # Add month column (1 to 6, since you have 6 rows) mutate(month = 1:n()) %>% # Reshape to long format pivot_longer( cols = -month, # Keep month as identifier names_to = "region", # Rename columns to "region" values_to = "observed" # Values become the observed metric ) %>% # Clean up region names (e.g., "V1" → "Region 1") mutate(region = str_replace(region, "V", "Region "))
Step 3: Reshape Simulated Data
Your simul 3D array needs to be converted to a data frame with columns for month, region, simulation run, and simulated value:
# Convert 3D simul array to tidy data frame simul_df <- as.data.frame.table(simul) %>% # Rename columns for clarity rename( month = Var1, region = Var2, sim_run = Var3, sim_value = Freq ) %>% # Convert month to integer (it comes as a factor from as.data.frame.table) mutate( month = as.integer(as.character(month)), region = str_replace(region, "V", "Region ") # Match region names to observed data )
Step 4: Create the Multi-panel Plot
Now we'll build the ggplot: each panel is a region, with boxplots for simulated values per month, plus a red line/points for observed values.
ggplot(simul_df, aes(x = factor(month), y = sim_value)) + # Add boxplots for simulated values (one per month per region) geom_boxplot(fill = "lightblue", alpha = 0.7, outlier.size = 0.8) + # Add line for observed values (use observed_df explicitly) geom_line( data = observed_df, aes(x = factor(month), y = observed, group = 1), color = "darkred", linewidth = 1.2 ) + # Add points for observed values to make them stand out geom_point( data = observed_df, aes(x = factor(month), y = observed), color = "darkred", size = 2 ) + # Create a panel for each region facet_wrap(~region, ncol = 3) + # Use ncol=1 for vertical panels, ncol=3 for horizontal # Customize labels and theme labs( x = "Month", y = "Value", title = "Simulated Value Distributions vs Observed Values" ) + theme_bw() + theme( plot.title = element_text(hjust = 0.5, size = 14, face = "bold"), axis.title = element_text(size = 12), strip.text = element_text(size = 11, face = "bold") )
Key Details:
factor(month): Converts month to a discrete variable, ensuring one boxplot per month (instead of treating month as continuous).data = observed_df: We explicitly specify the observed data for the line/points since it comes from a different data frame than the boxplots.group = 1: Ensures ggplot draws a single line connecting all observed points for a region (without this, it won't know how to group the points).- Facet Layout: Adjust
ncolinfacet_wrap()to change how panels are arranged (3 columns for side-by-side, 1 column for stacked).
内容的提问来源于stack exchange,提问作者thomas leon

