Windowing Strategy for the Additive p-Control Chart


Developed by:

Jean Paul Morán-Zabala, Juan Miguel Cogollo-Flórez & Alexander Alberto Correa-Espinal

2026


Analytical route of the document. The workflow is: (1) configure paths and packages, (2) read the general sheet a1, (3) plot the general additive p-chart, (4) read windows Vent1 to Vent12, (5) plot each window with control and specification limits, (6) count out-of-control signals, (7) consolidate tables, (8) generate comparative plots, and (9) export high-resolution images for reporting.
Before running. Place the GW_Comparative.xlsx file in the same folder as this RHTML file or modify the XLSX_PATH variable. If the Excel file or any expected column is missing, the document stops with an explicit diagnostic message.

1. Methodological foundation

1.1. Monitored variable

The p-chart monitors an observed proportion. In this file, the Pi column already contains the estimated proportion for each observation or subgroup. Conceptually, this proportion is computed as:
\[p_i=\frac{X_i}{n_i}\]

where \(X_i\) represents the number of nonconforming units, critical events, discarded observations, or deviations observed in subgroup \(i\), and \(n_i\) represents the subgroup size.

1.2. General additive p-chart

The a1 sheet contains a general chart in which the observed proportions \(p_i\) are compared against a central line and control limits. This chart provides a global view of the process before the window-based analysis.
\[LCL_i \leq p_i \leq UCL_i\]

The out-of-control signal rule is defined as:

\[OOC_i^{control}=\begin{cases}1, & p_i > UCL_i\;\lor\;p_i< LCL_i \\ 0, & LCL_i\leq p_i\leq UCL_i\end{cases}\]

1.3. Windowing with control and specification limits

Sheets Vent1 to Vent12 contain window-level results. In each window, control limits and specification limits are displayed simultaneously. This separates statistical process stability from compliance with operational specification bands.

Control limits:

\[LCL_i \leq p_i \leq UCL_i\]

Specification limits:

\[LSL_i \leq p_i \leq USL_i\]

Therefore, two signal rules are computed:

\[OOC_i^{control}=\begin{cases}1, & p_i>UCL_i\;\lor\;p_i< LCL_i \\ 0, & LCL_i\leq p_i\leq UCL_i\end{cases}\]
\[OOC_i^{spec}=\begin{cases}1, & p_i>USL_i\;\lor\;p_i< LSL_i \\ 0, & LSL_i\leq p_i\leq USL_i\end{cases}\]
Methodological reading. A control-limit signal indicates statistically unusual behavior relative to the estimated process pattern. A specification-limit signal indicates noncompliance with a defined operational band. Both readings are complementary: the first evaluates stability; the second evaluates conformity.

2. General RHTML configuration

This section loads packages, defines paths, creates the output folder, validates the Excel file, and sets full-page, high-resolution graphical parameters.
knitr::opts_chunk$set(fig.width=16, fig.height=8, fig.align="center", out.width="100%", dpi=350)
required_pkgs <- c("readxl", "dplyr", "tidyr", "ggplot2", "knitr")
optional_pkgs <- c("magick", "writexl")
install_if_missing <- function(pkgs){for(pkg in pkgs){if(!requireNamespace(pkg, quietly=TRUE)){install.packages(pkg, dependencies=TRUE)}; suppressPackageStartupMessages(library(pkg, character.only=TRUE))}}
install_if_missing(required_pkgs)
for(pkg in optional_pkgs){if(requireNamespace(pkg, quietly=TRUE)){suppressPackageStartupMessages(library(pkg, character.only=TRUE))}}
XLSX_PATH <- "GW_Comparative.xlsx"
OUT_DIR <- "outputs_GW_RHTML"
dir.create(OUT_DIR, showWarnings=FALSE, recursive=TRUE)
IMG_WIDTH_PX <- 5200; IMG_HEIGHT_PX <- 1900; IMG_DPI <- 360
IMG_FACET_W <- 18; IMG_FACET_H <- 12; IMG_BAR_W <- 16; IMG_BAR_H <- 7
N_VENT <- 12
COL_PI <- "black"; COL_CONTROL <- "darkblue"; COL_CENTER <- "gray40"; COL_SPEC <- "darkgreen"; COL_OOC_CTRL <- "red"; COL_OOC_SPEC <- "purple4"
if(!file.exists(XLSX_PATH)){stop(paste0("The file '", XLSX_PATH, "' was not found. Place GW_Comparative.xlsx in the same directory as this RHTML file or modify XLSX_PATH."))}
sheets_available <- readxl::excel_sheets(XLSX_PATH)
knitr::kable(data.frame(Available_sheets=sheets_available), caption="Sheets found in the Excel file")
Sheets found in the Excel file
Available_sheets
a1
Vent1
Vent2
Vent3
Vent4
Vent5
Vent6
Vent7
Vent8
Vent9
Vent10
Vent11
Vent12

3. Auxiliary validation and plotting functions

What is computed here. Reusable functions are defined to validate columns, remove residual rows with missing values, identify OOC signals, and plot the charts. This improves traceability and avoids duplicated code.

3.1. Column validation

\[\{\text{required columns}\}\subseteq\{\text{available columns}\}\]
validate_columns <- function(df, required_cols, sheet_name){
  missing_cols <- setdiff(required_cols, names(df))
  if(length(missing_cols)>0){stop(paste0("In sheet '", sheet_name, "' the following columns are missing: ", paste(missing_cols, collapse=", "), "\nAvailable columns: ", paste(names(df), collapse=", ")))}
  invisible(TRUE)
}
safe_numeric <- function(x){suppressWarnings(as.numeric(x))}

3.2. Function for the general additive p-chart

Generated plot. This function produces a general chart with the \(p_i\) series, the central line, upper/lower control limits, and red markers for out-of-control observations.
plot_control_from_cols <- function(x, pi, ucl, cl, lcl, out_png=NULL, width_px=IMG_WIDTH_PX, height_px=IMG_HEIGHT_PX, res_dpi=IMG_DPI){
  ok <- !(is.na(pi) & is.na(ucl) & is.na(cl) & is.na(lcl)); n_residuals <- sum(!ok)
  x <- x[ok]; pi <- pi[ok]; ucl <- ucl[ok]; cl <- cl[ok]; lcl <- lcl[ok]
  n <- length(x); if(n<2) stop("There are not enough data points to plot the general chart.")
  y_all <- c(pi,ucl,cl,lcl); y_min <- min(y_all,na.rm=TRUE); y_max <- max(y_all,na.rm=TRUE); pad <- .02*(y_max-y_min); if(!is.finite(pad)||pad==0) pad <- 1e-6; ylim <- c(y_min-pad,y_max+pad)
  ooc_up <- which(pi>ucl); ooc_down <- which(pi<lcl); ooc_all <- sort(unique(c(ooc_up,ooc_down)))
  if(!is.null(out_png)){png(out_png,width=width_px,height=height_px,res=res_dpi); on.exit(dev.off(), add=TRUE)}
  op <- par(no.readonly=TRUE); on.exit(par(op), add=TRUE)
  par(mar=c(6,6.2,3.5,2.5),xaxs="i",yaxs="i",cex=1.15,cex.axis=1.05,cex.lab=1.15)
  plot(x, pi, type="l", col=COL_PI, lwd=1.2, ylim=ylim, xaxt="n", xlab="Weeks", ylab=expression(p[i]), main="Additive - General adjusted p-chart")
  axis(1, at=c(1,n), labels=c("1",n)); lines(x,cl,col=COL_CENTER,lwd=1.4,lty=1); lines(x,ucl,col=COL_CONTROL,lwd=1.2,lty=3); lines(x,lcl,col=COL_CONTROL,lwd=1.2,lty=3)
  if(length(ooc_all)>0){points(x[ooc_all], pi[ooc_all], pch=4, col=COL_OOC_CTRL, cex=.65, lwd=1.3)}
  box(); legend("bottom", inset=-.30, xpd=TRUE, horiz=TRUE, bty="n", legend=c("pi","UCL/LCS","CL/LC","LCL/LCI","OOC-Control"), col=c(COL_PI,COL_CONTROL,COL_CENTER,COL_CONTROL,COL_OOC_CTRL), lty=c(1,3,1,3,NA), lwd=c(1.2,1.2,1.4,1.2,NA), pch=c(NA,NA,NA,NA,4), pt.cex=.75)
  invisible(list(total=length(ooc_all), above_ucl=length(ooc_up), below_lcl=length(ooc_down), idx=ooc_all, residuals=n_residuals, n=n))
}

3.3. Function for windows with control and specification limits

Generated plot. For each window, the function plots \(p_i\), control limits \(UCL, CCL, LCL\), specification limits \(USL, CEL, LEL\), OOC-Control signals, and OOC-Specification signals.
plot_control_and_spec_from_cols <- function(x, pi, ucl, ccl, lcl, usl, csl, lsl, out_png=NULL, title=NULL, width_px=IMG_WIDTH_PX, height_px=IMG_HEIGHT_PX, res_dpi=IMG_DPI){
  ok <- !(is.na(pi)&is.na(ucl)&is.na(ccl)&is.na(lcl)&is.na(usl)&is.na(csl)&is.na(lsl)); n_residuals <- sum(!ok)
  x <- x[ok]; pi <- pi[ok]; ucl <- ucl[ok]; ccl <- ccl[ok]; lcl <- lcl[ok]; usl <- usl[ok]; csl <- csl[ok]; lsl <- lsl[ok]
  n <- length(x); if(n<2) stop("There are not enough data points to plot the window.")
  y_all <- c(pi,ucl,ccl,lcl,usl,csl,lsl); y_min <- min(y_all,na.rm=TRUE); y_max <- max(y_all,na.rm=TRUE); pad <- .02*(y_max-y_min); if(!is.finite(pad)||pad==0) pad <- 1e-6; ylim <- c(y_min-pad,y_max+pad)
  ooc_ctrl_up <- which(pi>ucl); ooc_ctrl_down <- which(pi<lcl); ooc_ctrl_all <- sort(unique(c(ooc_ctrl_up,ooc_ctrl_down)))
  ooc_spec_up <- which(pi>usl); ooc_spec_down <- which(pi<lsl); ooc_spec_all <- sort(unique(c(ooc_spec_up,ooc_spec_down)))
  if(!is.null(out_png)){png(out_png,width=width_px,height=height_px,res=res_dpi); on.exit(dev.off(), add=TRUE)}
  op <- par(no.readonly=TRUE); on.exit(par(op), add=TRUE); layout(matrix(c(1,2), nrow=1), widths=c(4.8,1.8))
  par(mar=c(6,6.2,3.5,1.5), xaxs="i", yaxs="i", cex=1.15, cex.axis=1.05, cex.lab=1.15)
  plot(x,pi,type="l",col=COL_PI,lwd=1.2,ylim=ylim,xaxt="n",xlab="Weeks",ylab=expression(p[i]),main=ifelse(is.null(title),"Window - Control and specification",title)); axis(1,at=c(1,n),labels=c("1",n))
  lines(x,ccl,col=COL_CENTER,lwd=1.4,lty=1); lines(x,ucl,col=COL_CONTROL,lwd=1.2,lty=3); lines(x,lcl,col=COL_CONTROL,lwd=1.2,lty=3)
  lines(x,csl,col=COL_SPEC,lwd=1.3,lty=1); lines(x,usl,col=COL_SPEC,lwd=1.2,lty=2); lines(x,lsl,col=COL_SPEC,lwd=1.2,lty=2)
  if(length(ooc_ctrl_all)>0){points(x[ooc_ctrl_all],pi[ooc_ctrl_all],pch=4,col=COL_OOC_CTRL,cex=.70,lwd=1.4)}
  if(length(ooc_spec_all)>0){points(x[ooc_spec_all],pi[ooc_spec_all],pch=17,col=COL_OOC_SPEC,cex=.45)}
  box(); par(mar=c(2,.5,2,.5)); plot.new(); legend("topleft",bty="n",cex=.98,y.intersp=1.15,legend=c("pi","UCL","CCL","LCL","USL","CEL/CSL","LEL/LSL","OOC-Control","OOC-Specification"), col=c(COL_PI,COL_CONTROL,COL_CENTER,COL_CONTROL,COL_SPEC,COL_SPEC,COL_SPEC,COL_OOC_CTRL,COL_OOC_SPEC), lty=c(1,3,1,3,2,1,2,NA,NA), lwd=c(1.2,1.2,1.4,1.2,1.2,1.3,1.2,NA,NA), pch=c(NA,NA,NA,NA,NA,NA,NA,4,17), pt.cex=c(NA,NA,NA,NA,NA,NA,NA,.90,.55))
  invisible(list(ooc_control_total=length(ooc_ctrl_all), ooc_control_above=length(ooc_ctrl_up), ooc_control_below=length(ooc_ctrl_down), ooc_control_idx=ooc_ctrl_all, ooc_spec_total=length(ooc_spec_all), ooc_spec_above=length(ooc_spec_up), ooc_spec_below=length(ooc_spec_down), ooc_spec_idx=ooc_spec_all, residuals=n_residuals, n=n))
}

4. Reading and analysis of the general additive p-chart

What is done in this section. The a1 sheet is read, the required columns are validated, and the general chart is generated. This plot provides a global view of the process.
\[\text{Total OOC}=\sum_{i=1}^{N}OOC_i^{control}\]
if(!"a1" %in% sheets_available){stop("The Excel file does not contain the required sheet 'a1'.")}
df_gw <- readxl::read_excel(XLSX_PATH, sheet="a1"); validate_columns(df_gw, c("Pi","LCS","LC","LCI"), "a1")
GW_PNG <- file.path(OUT_DIR,"Goedhart_Woodall_chart.png")
res_gw <- plot_control_from_cols(x=seq_len(nrow(df_gw)), pi=safe_numeric(df_gw$Pi), ucl=safe_numeric(df_gw$LCS), cl=safe_numeric(df_gw$LC), lcl=safe_numeric(df_gw$LCI), out_png=GW_PNG)
knitr::include_graphics(GW_PNG)
plot of chunk gw-read-and-plot
How to interpret the general plot. The black line represents the observed proportion \(p_i\). The dotted blue lines represent the upper and lower control limits. The gray line represents the central line. Red crosses identify out-of-control observations.
gw_summary <- data.frame(Chart="General additive chart", Observations=res_gw$n, OOC_Control_Total=res_gw$total, Above_UCL_LCS=res_gw$above_ucl, Below_LCL_LCI=res_gw$below_lcl, Residual_Rows_Removed=res_gw$residuals)
knitr::kable(gw_summary, caption="Summary of OOC signals for the general additive chart")
Summary of OOC signals for the general additive chart
Chart Observations OOC_Control_Total Above_UCL_LCS Below_LCL_LCI Residual_Rows_Removed
General additive chart 200 5 5 0 0

5. Sequential windowing strategy

What is computed. Sheets Vent1 to Vent12 are read, columns are validated, and individual charts are generated. For each window, control-limit and specification-limit signals are counted.
\[\text{Signals}_{w}^{control}=\sum_{i\in w}OOC_i^{control}\]
\[\text{Signals}_{w}^{spec}=\sum_{i\in w}OOC_i^{spec}\]
vent_results <- vector("list", N_VENT); names(vent_results) <- paste0("Vent",1:N_VENT)
vent_data_all <- list(); vent_png_files <- character(N_VENT)
for(k in 1:N_VENT){
  sheet_name <- paste0("Vent",k); if(!sheet_name %in% sheets_available){stop(paste0("Missing sheet: ", sheet_name))}
  df_vk <- readxl::read_excel(XLSX_PATH, sheet=sheet_name); validate_columns(df_vk, c("Pi","UCL","CCL","LCL","USL","CEL","LEL"), sheet_name)
  vent_png_files[k] <- file.path(OUT_DIR, sprintf("Vent%02d_Control_Spec_chart.png", k))
  vent_results[[k]] <- plot_control_and_spec_from_cols(x=seq_len(nrow(df_vk)), pi=safe_numeric(df_vk$Pi), ucl=safe_numeric(df_vk$UCL), ccl=safe_numeric(df_vk$CCL), lcl=safe_numeric(df_vk$LCL), usl=safe_numeric(df_vk$USL), csl=safe_numeric(df_vk$CEL), lsl=safe_numeric(df_vk$LEL), out_png=vent_png_files[k], title=paste0("Window ", k, " - Control and specification"))
  tmp <- data.frame(Window=sheet_name, Index=seq_len(nrow(df_vk)), Pi=safe_numeric(df_vk$Pi), UCL=safe_numeric(df_vk$UCL), CCL=safe_numeric(df_vk$CCL), LCL=safe_numeric(df_vk$LCL), USL=safe_numeric(df_vk$USL), CEL=safe_numeric(df_vk$CEL), LEL=safe_numeric(df_vk$LEL))
  tmp <- tmp |> dplyr::mutate(OOC_Control=(Pi>UCL)|(Pi<LCL), OOC_Specification=(Pi>USL)|(Pi<LEL))
  vent_data_all[[k]] <- tmp
}
vent_summary <- dplyr::bind_rows(lapply(seq_len(N_VENT), function(k){r <- vent_results[[k]]; data.frame(Window=paste0("Vent",k), Observations=r$n, OOC_Control_Total=r$ooc_control_total, Above_UCL=r$ooc_control_above, Below_LCL=r$ooc_control_below, OOC_Spec_Total=r$ooc_spec_total, Above_USL=r$ooc_spec_above, Below_LSL=r$ooc_spec_below, Residual_Rows_Removed=r$residuals)}))
vent_data_all_df <- dplyr::bind_rows(vent_data_all)
knitr::kable(vent_summary, caption="Summary of signals by window: control and specification")
Summary of signals by window: control and specification
Window Observations OOC_Control_Total Above_UCL Below_LCL OOC_Spec_Total Above_USL Below_LSL Residual_Rows_Removed
Vent1 1000 43 43 0 171 171 0 0
Vent2 1000 19 19 0 171 171 0 0
Vent3 1000 4 4 0 236 143 93 0
Vent4 1000 5 5 0 178 177 1 0
Vent5 1000 2 2 0 43 43 0 0
Vent6 1000 1 1 0 153 141 12 0
Vent7 1000 3 3 0 212 178 34 0
Vent8 1000 4 4 0 238 211 27 0
Vent9 1000 2 2 0 77 77 0 0
Vent10 1000 4 4 0 57 45 12 0
Vent11 1000 1 1 0 10 10 0 0
Vent12 399 13 13 0 109 109 0 0

5.1. Individual plots by window

Reading individual plots. Each window should be interpreted as a local stage of the process. Red crosses correspond to control-limit signals; purple triangles correspond to specification-limit signals.

Window 1

[1] "outputs_GW_RHTML/Vent01_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 2

[1] "outputs_GW_RHTML/Vent02_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 3

[1] "outputs_GW_RHTML/Vent03_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 4

[1] "outputs_GW_RHTML/Vent04_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 5

[1] "outputs_GW_RHTML/Vent05_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 6

[1] "outputs_GW_RHTML/Vent06_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 7

[1] "outputs_GW_RHTML/Vent07_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 8

[1] "outputs_GW_RHTML/Vent08_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 9

[1] "outputs_GW_RHTML/Vent09_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 10

[1] "outputs_GW_RHTML/Vent10_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 11

[1] "outputs_GW_RHTML/Vent11_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

Window 12

[1] "outputs_GW_RHTML/Vent12_Control_Spec_chart.png" attr(,"class") [1] "knit_image_paths" "knit_asis"

6. General plot of the sequential windowing strategy

What is shown. The following faceted plot summarizes all windows in one panel, supporting cross-window comparison of stability and specification compliance.
vent_long_limits <- vent_data_all_df |> dplyr::select(Window,Index,UCL,CCL,LCL,USL,CEL,LEL) |> tidyr::pivot_longer(cols=c(UCL,CCL,LCL,USL,CEL,LEL), names_to="Limit", values_to="Value")
p_faceted <- ggplot() + geom_line(data=vent_data_all_df, aes(x=Index,y=Pi), color="black", linewidth=.35) + geom_line(data=vent_long_limits, aes(x=Index,y=Value,linetype=Limit,color=Limit), linewidth=.32) + geom_point(data=vent_data_all_df |> dplyr::filter(OOC_Control), aes(x=Index,y=Pi), color=COL_OOC_CTRL, shape=4, size=.9, stroke=.7) + geom_point(data=vent_data_all_df |> dplyr::filter(OOC_Specification), aes(x=Index,y=Pi), color=COL_OOC_SPEC, shape=17, size=.8, alpha=.8) + facet_wrap(~Window, scales="free_x", ncol=3) + scale_color_manual(values=c("UCL"=COL_CONTROL,"CCL"=COL_CENTER,"LCL"=COL_CONTROL,"USL"=COL_SPEC,"CEL"=COL_SPEC,"LEL"=COL_SPEC)) + scale_linetype_manual(values=c("UCL"="dotted","CCL"="solid","LCL"="dotted","USL"="dashed","CEL"="solid","LEL"="dashed")) + labs(title="Sequential windowing: p-charts with control and specification limits", subtitle="Red crosses: OOC-Control; purple triangles: OOC-Specification", x="Local index within the window", y=expression(p[i]), color="Limits", linetype="Limits") + theme_classic(base_size=12) + theme(plot.title=element_text(face="bold",size=16), strip.background=element_rect(fill="#eef3f8",color="#cbd5e1"), strip.text=element_text(face="bold"), legend.position="bottom")
print(p_faceted); FACETED_PNG <- file.path(OUT_DIR,"ALL_WINDOWS_faceted_control_spec.png"); ggsave(FACETED_PNG,p_faceted,width=IMG_FACET_W,height=IMG_FACET_H,dpi=IMG_DPI)
plot of chunk faceted-window-plot
Expected interpretation. The faceted plot identifies windows with higher signal pressure. Many control signals may indicate local instability; many specification signals may indicate noncompliance with an operational threshold.

7. Quantitative comparison of signals by window

What is computed. OOC-Control and OOC-Specification signals are compared to distinguish whether the dominant issue is statistical, operational, or both.
signal_long <- vent_summary |> dplyr::select(Window,OOC_Control_Total,OOC_Spec_Total) |> tidyr::pivot_longer(cols=c(OOC_Control_Total,OOC_Spec_Total), names_to="Signal_Type", values_to="Signals") |> dplyr::mutate(Signal_Type=dplyr::recode(Signal_Type, OOC_Control_Total="OOC-Control", OOC_Spec_Total="OOC-Specification"), Window=factor(Window, levels=paste0("Vent",1:N_VENT)))
p_signal <- ggplot(signal_long, aes(x=Window,y=Signals,fill=Signal_Type)) + geom_col(position="dodge", width=.72) + labs(title="Comparison of signals by window", subtitle="Differentiation between control-limit and specification-limit signals", x="Window", y="Number of signals", fill="Signal type") + theme_classic(base_size=13) + theme(plot.title=element_text(face="bold"), axis.text.x=element_text(angle=45,hjust=1), legend.position="bottom")
print(p_signal); SIGNALS_PNG <- file.path(OUT_DIR,"Signals_by_Window_Control_vs_Spec.png"); ggsave(SIGNALS_PNG,p_signal,width=IMG_BAR_W,height=IMG_BAR_H,dpi=IMG_DPI)
plot of chunk signal-comparison-plot

8. Consolidated image of the 13 charts

What is generated. If magick is installed, the document combines the general chart and the 12 windows into a single two-column, high-resolution image.
all_png_files <- c(GW_PNG, vent_png_files); out_all <- file.path(OUT_DIR,"ALL_13_concatenated.png")
if(requireNamespace("magick", quietly=TRUE)){missing_png <- all_png_files[!file.exists(all_png_files)]; if(length(missing_png)>0){warning("The images could not be concatenated because the following files are missing: ", paste(missing_png,collapse=", "))} else {imgs <- magick::image_read(all_png_files); imgs <- magick::image_resize(imgs, paste0(IMG_WIDTH_PX,"x",IMG_HEIGHT_PX,"!")); info1 <- magick::image_info(imgs[1]); blank <- magick::image_blank(width=info1$width,height=info1$height,color="white"); if(length(imgs) %% 2 == 1) imgs <- c(imgs, blank); rows <- lapply(seq(1,length(imgs),by=2), function(i){magick::image_append(imgs[c(i,i+1)])}); final <- magick::image_append(do.call(c,rows), stack=TRUE); magick::image_write(final,path=out_all,format="png"); cat("<p><strong>Consolidated image saved at:</strong> ", out_all, "</p>", sep=""); print(knitr::include_graphics(out_all))}} else {cat("<div class='warn'><strong>Note:</strong> The <code>magick</code> package is not installed. Image concatenation is skipped, but all individual images and the faceted plot were generated.</div>")}
Note: The magick package is not installed. Image concatenation is skipped, but all individual images and the faceted plot were generated.

9. Exporting results

What is exported. Summary tables are saved as CSV files and, if writexl is available, as an Excel workbook. Images are automatically saved in outputs_GW_RHTML.
write.csv(gw_summary, file.path(OUT_DIR,"GW_general_summary.csv"), row.names=FALSE); write.csv(vent_summary, file.path(OUT_DIR,"Window_summary_control_spec.csv"), row.names=FALSE); write.csv(vent_data_all_df, file.path(OUT_DIR,"Window_all_data_control_spec.csv"), row.names=FALSE)
if(requireNamespace("writexl", quietly=TRUE)){writexl::write_xlsx(list(GW_General_Summary=gw_summary, Window_Summary=vent_summary, Window_All_Data=vent_data_all_df), path=file.path(OUT_DIR,"Goedhart_Woodall_RHTML_results.xlsx")); cat("<p><strong>Results Excel exported at:</strong> outputs_GW_RHTML/Goedhart_Woodall_RHTML_results.xlsx</p>")} else {cat("<div class='warn'><strong>Note:</strong> The <code>writexl</code> package is not installed. CSV files were exported, but no Excel file was generated.</div>")}

Results Excel exported at: outputs_GW_RHTML/Goedhart_Woodall_RHTML_results.xlsx

cat("<p><strong>Output folder:</strong> ", OUT_DIR, "</p>", sep="")

Output folder: outputs_GW_RHTML

10. Reading the results

1. General chart. The general chart makes it possible to observe the global stability of the process under the additive approach. A high number of OOC signals suggests that the process cannot be interpreted solely from a homogeneous structure.

2. Windowing. Windows support local analysis. If signals increase in specific windows, this may indicate periods or operational segments with higher variability or noncompliance.

3. Control vs. specification. Control-limit signals are related to statistical stability; specification-limit signals are related to compliance with an operational band.

4. Consolidated output. The 13-chart image and the faceted plot are useful for visually reporting process evolution, while the summary tables provide quantitative support.

11. Generated files