diff --git a/NAMESPACE b/NAMESPACE index 56ad05a..4f3271d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,10 +1,12 @@ # Generated by roxygen2: do not edit by hand +S3method(plot,cchart.DSnp) export(T2.1) export(T2.2) export(add.data) export(alpha.risk) export(c4) +export(cchart.DSnp) export(cchart.R) export(cchart.S) export(cchart.T2.1) @@ -30,6 +32,7 @@ export(table.qtukey) importFrom(MASS,mvrnorm) importFrom(graphics,abline) importFrom(graphics,axis) +importFrom(graphics,legend) importFrom(graphics,lines) importFrom(graphics,mtext) importFrom(graphics,par) diff --git a/R/cchart.DSnp.R b/R/cchart.DSnp.R new file mode 100644 index 0000000..6d8efef --- /dev/null +++ b/R/cchart.DSnp.R @@ -0,0 +1,360 @@ +#' Double-Sampling np Control Chart +#' +#' Build and optionally plot a double-sampling np (DS-np) control chart for +#' monitoring the nonconforming proportion in high-quality processes. +#' +#' The DS-np chart uses two sampling stages. At the first stage, a sample of +#' size \code{n1} is inspected. If the count \code{x1} is at or below the +#' warning limit, the process is accepted. If \code{x1} exceeds the first +#' upper control limit, the process signals out-of-control immediately. +#' Otherwise, a second sample of size \code{n2} is inspected and the combined +#' count \code{x1 + x2} is compared to the second upper control limit. +#' +#' Limits can be supplied manually via \code{wl}, \code{ucl1}, and +#' \code{ucl2}, obtained from a pre-computed \code{dsnp_limits()} object via +#' the \code{limits} argument, or computed automatically inside the function +#' when neither is provided. +#' +#' The fractional limits are converted to integer thresholds using the +#' convention from the numerical core functions: +#' \itemize{ +#' \item \code{wl_accept = floor(wl)}: accept at first stage if +#' \code{x1 <= wl_accept}. +#' \item \code{ucl1_reject = floor(ucl1) + 1}: signal at first stage if +#' \code{x1 >= ucl1_reject}. +#' \item \code{ucl2_accept = floor(ucl2)}: accept at second stage if +#' \code{x1 + x2 <= ucl2_accept}. +#' } +#' +#' @param x1 Integer vector of nonconforming counts from the first sample. +#' @param n1 First-stage sample size (positive integer). +#' @param n2 Second-stage sample size (positive integer). +#' @param p0 In-control nonconforming proportion. +#' @param x2 Optional integer vector of nonconforming counts from the second +#' sample. Must have the same length as \code{x1} when provided. Use +#' \code{NA} for observations where no second sample was taken. Required +#' for observations where \code{x1} falls in the intermediate (warning) +#' zone. +#' @param wl Fractional warning limit. Must be less than \code{ucl1}. +#' @param ucl1 Fractional upper control limit for the first stage. Must be +#' greater than \code{wl}. +#' @param ucl2 Fractional upper control limit for the combined samples. +#' @param limits Optional object returned by \code{dsnp_limits()}. When +#' provided, \code{wl}, \code{ucl1}, and \code{ucl2} are taken from +#' \code{limits$best}. +#' @param alpha Maximum desired false alarm probability at \code{p0}. Used +#' only when limits need to be computed via \code{dsnp_limits()}. +#' @param p1 Optional out-of-control proportion. When provided, performance +#' metrics at \code{p1} are included in the returned object. +#' @param plot Logical. If \code{TRUE} (default), draws the control chart. +#' If \code{FALSE}, only returns the result object. +#' @param ... Additional arguments passed to \code{plot()} (currently +#' unused). +#' +#' @return An object of class \code{"cchart.DSnp"}, which is a list with the +#' following elements: +#' \describe{ +#' \item{call}{The matched call.} +#' \item{data}{A data.frame with columns \code{index}, \code{x1}, +#' \code{x2}, \code{total}, \code{stage}, and \code{signal}.} +#' \item{limits}{A list with the fractional and integer thresholds: +#' \code{wl}, \code{ucl1}, \code{ucl2}, \code{wl_accept}, +#' \code{ucl1_reject}, \code{ucl2_accept}.} +#' \item{parameters}{A list with \code{n1}, \code{n2}, \code{p0}, +#' \code{alpha}, and \code{p1}.} +#' \item{performance}{A list with in-control performance metrics +#' (\code{arl0}, \code{ass0}, \code{p_signal0}), and optionally +#' out-of-control metrics (\code{arl1}, \code{ass1}, +#' \code{p_signal1}) when \code{p1} is provided.} +#' } +#' +#' @export +#' @importFrom graphics legend abline plot points +#' @author Daniela R. Recchia, Emanuel P. Barbosa +#' @seealso \code{\link{dsnp_limits}}, \code{\link{dsnp_prob_accept}}, +#' \code{\link{dsnp_arl}}, \code{\link{dsnp_ass}} +#' @references Joekes, S., Smrekar, M. and Barbosa, E. (2015). Extending a +#' double sampling control chart for non-conforming proportion in high quality +#' processes to the case of small samples. \emph{Statistical Methodology}. +#' @examples +#' +#' # Small example with manual limits +#' x1 <- c(0, 1, 2, 3, 1, 0, 2, 4, 1, 0) +#' x2 <- c(NA, NA, 2, NA, NA, NA, 3, NA, NA, NA) +#' res <- cchart.DSnp(x1, n1 = 10, n2 = 20, p0 = 0.05, +#' x2 = x2, wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, +#' plot = FALSE) +#' res$limits +#' res$performance +#' +cchart.DSnp <- function(x1, n1, n2, p0, + x2 = NULL, + wl = NULL, + ucl1 = NULL, + ucl2 = NULL, + limits = NULL, + alpha = 0.0027, + p1 = NULL, + plot = TRUE, + ...) +{ + cl <- match.call() + + # --- Validate x1 --- + if(!is.numeric(x1) || length(x1) < 1) + stop("x1 must be a non-empty numeric vector") + if(any(x1 < 0)) + stop("x1 must not contain negative values") + if(any(x1 > n1)) + stop("x1 values must not exceed n1") + if(any(x1 != floor(x1))) + stop("x1 must contain integer values") + + m <- length(x1) + + # --- Resolve limits --- + have_limits_obj <- !is.null(limits) + have_manual <- !is.null(wl) || !is.null(ucl1) || !is.null(ucl2) + have_none <- !have_limits_obj && !have_manual + + if(have_limits_obj && have_manual) + stop("Cannot specify both 'limits' and manual 'wl'/'ucl1'/'ucl2'") + + if(have_manual && (is.null(wl) || is.null(ucl1) || is.null(ucl2))) + stop("All of 'wl', 'ucl1', and 'ucl2' must be provided together") + + if(have_limits_obj) + { + if(!is.null(limits$best)) + { + wl <- limits$best$wl + ucl1 <- limits$best$ucl1 + ucl2 <- limits$best$ucl2 + } + else + { + stop("'limits' object does not contain a 'best' element") + } + } + + if(have_none) + { + lim <- dsnp_limits(p0, n1, n2, alpha = alpha, p1 = p1, + max_results = 1) + wl <- lim$best$wl + ucl1 <- lim$best$ucl1 + ucl2 <- lim$best$ucl2 + } + + # --- Validate limits --- + if(!is.numeric(wl) || !is.numeric(ucl1) || !is.numeric(ucl2)) + stop("wl, ucl1, and ucl2 must be numeric") + if(length(wl) != 1 || length(ucl1) != 1 || length(ucl2) != 1) + stop("wl, ucl1, and ucl2 must be scalar") + if(wl >= ucl1) + stop("wl must be less than ucl1") + if(ucl2 <= wl) + stop("ucl2 must be greater than wl") + + # --- Integer thresholds --- + wl_accept <- floor(wl) + ucl1_reject <- floor(ucl1) + 1 + ucl2_accept <- floor(ucl2) + + # --- Validate x2 --- + if(!is.null(x2)) + { + if(length(x2) != m) + stop("x2 must have the same length as x1") + if(any(!is.na(x2) & x2 < 0)) + stop("x2 must not contain negative values") + if(any(!is.na(x2) & x2 > n2)) + stop("x2 values must not exceed n2") + if(any(!is.na(x2) & x2 != floor(x2))) + stop("x2 must contain integer values") + } + + # --- Classify each observation --- + index <- seq_len(m) + stage <- character(m) + signal <- logical(m) + total <- rep(NA_real_, m) + x2_used <- if(!is.null(x2)) x2 else rep(NA_real_, m) + + for(i in seq_len(m)) + { + d1 <- x1[i] + + if(d1 <= wl_accept) + { + stage[i] <- "accept_first" + signal[i] <- FALSE + } + else if(d1 >= ucl1_reject) + { + stage[i] <- "signal_first" + signal[i] <- TRUE + } + else + { + # Intermediate zone: second sample required + if(is.null(x2) || is.na(x2_used[i])) + stop(paste0("Second sample (x2) is required for observation ", + i, " where x1 = ", d1, + " falls in the intermediate zone")) + d2 <- x2_used[i] + total[i] <- d1 + d2 + if(total[i] > ucl2_accept) + { + stage[i] <- "signal_second" + signal[i] <- TRUE + } + else + { + stage[i] <- "accept_second" + signal[i] <- FALSE + } + } + } + + # --- Build data frame --- + data_df <- data.frame( + index = index, + x1 = x1, + x2 = x2_used, + total = total, + stage = stage, + signal = signal, + stringsAsFactors = FALSE + ) + + # --- Performance metrics --- + arl0_res <- dsnp_arl(p0, n1, n2, wl, ucl1, ucl2) + ass0_res <- dsnp_ass(p0, n1, n2, wl, ucl1) + pa0_res <- dsnp_prob_accept(p0, n1, n2, wl, ucl1, ucl2) + + performance <- list( + arl0 = arl0_res$arl, + ass0 = ass0_res$ass, + p_signal0 = pa0_res$p_signal + ) + + if(!is.null(p1)) + { + arl1_res <- dsnp_arl(p1, n1, n2, wl, ucl1, ucl2) + ass1_res <- dsnp_ass(p1, n1, n2, wl, ucl1) + pa1_res <- dsnp_prob_accept(p1, n1, n2, wl, ucl1, ucl2) + + performance$arl1 <- arl1_res$arl + performance$ass1 <- ass1_res$ass + performance$p_signal1 <- pa1_res$p_signal + } + + # --- Assemble result --- + result <- list( + call = cl, + data = data_df, + limits = list( + wl = wl, + ucl1 = ucl1, + ucl2 = ucl2, + wl_accept = wl_accept, + ucl1_reject = ucl1_reject, + ucl2_accept = ucl2_accept + ), + parameters = list( + n1 = n1, + n2 = n2, + p0 = p0, + alpha = alpha, + p1 = p1 + ), + performance = performance + ) + class(result) <- "cchart.DSnp" + + # --- Plot --- + if(plot) + plot.cchart.DSnp(result, ...) + + result +} + +#' Plot a DS-np Control Chart +#' +#' S3 method for plotting objects of class \code{"cchart.DSnp"}. +#' +#' The plot shows the first-stage counts \code{x1} against the sample index. +#' Points requiring a second sample are shown as open circles. Points that +#' signal (at either stage) are shown in red. Horizontal lines mark the +#' fractional limits. +#' +#' Note that \code{ucl2} is on the scale of the combined count +#' \code{x1 + x2}. The plot is a simple operational visualization; the +#' full decision logic is in the returned object. +#' +#' @param x An object of class \code{"cchart.DSnp"}. +#' @param ... Additional graphical parameters (currently unused). +#' @return Invisible \code{x}. +#' @export +#' @method plot cchart.DSnp +plot.cchart.DSnp <- function(x, ...) +{ + d <- x$data + lim <- x$limits + n1 <- x$parameters$n1 + + # Determine y-axis range + y_max <- max(d$x1, na.rm = TRUE) + y_max <- max(y_max, lim$ucl1 + 1, lim$ucl2, na.rm = TRUE) + y_max <- ceiling(y_max * 1.1) + if(y_max < 5) y_max <- 5 + + # Base plot: x1 vs index + plot(d$index, d$x1, type = "n", + xlab = "Sample index", ylab = "Count (x1)", + xlim = c(1, nrow(d)), + ylim = c(0, y_max), + main = "DS-np Control Chart", + las = 1) + + # Points that stayed at first stage (no second sample needed) + first_only <- d$stage %in% c("accept_first", "signal_first") + points(d$index[first_only], d$x1[first_only], + pch = 16, cex = 1.2) + + # Points that needed a second sample + second <- d$stage %in% c("accept_second", "signal_second") + if(any(second)) + points(d$index[second], d$x1[second], + pch = 1, cex = 1.2) + + # Highlight signals in red + if(any(d$signal)) + points(d$index[d$signal], d$x1[d$signal], + pch = 4, cex = 1.4, col = "red", lwd = 2) + + # Limit lines + abline(h = lim$wl, lty = 2, col = "blue") + abline(h = lim$ucl1, lty = 2, col = "red") + abline(h = lim$ucl2, lty = 3, col = "darkgreen") + + # Legend + legend("topleft", + legend = c( + "First stage only", + "Second sample needed", + "Signal", + paste0("wl = ", lim$wl), + paste0("ucl1 = ", lim$ucl1), + paste0("ucl2 = ", lim$ucl2) + ), + pch = c(16, 1, 4, NA, NA, NA), + lty = c(NA, NA, NA, 2, 2, 3), + col = c("black", "black", "red", "blue", "red", "darkgreen"), + cex = 0.8, + bg = "white", + bty = "n") + + invisible(x) +} diff --git a/R/d2.R b/R/d2.R index e84e2c1..fef5e28 100755 --- a/R/d2.R +++ b/R/d2.R @@ -9,7 +9,7 @@ #' @export #' @author Daniela R. Recchia, Emanuel P. Barbosa #' @seealso \link{d3},\link{c4} -#' @importFrom stats ptukey +#' @importFrom stats ptukey integrate #' @examples #' #' d2(8) diff --git a/README.md b/README.md index 864b1ee..8a69a4d 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The package is motivated by a recurring practical problem in classical Shewhart- | Range / process dispersion | `cchart.R()` | Shewhart R chart; exact Tukey-based R chart | The exact chart uses the relative range distribution through the Tukey distribution. | | Standard deviation | `cchart.S()` | Normalized S chart; exact chi-square-based S chart | Exact limits are based on the chi-square distribution of the sample variance. | | Nonconforming proportion | `cchart.p()` | Shewhart p chart; Cornish-Fisher p chart; standardized p chart | The Cornish-Fisher option is designed for low nonconforming proportions where normal approximation is poor. | -| Double-sampling nonconforming count | `dsnp_prob_accept()`, `dsnp_arl()`, `dsnp_ass()` | Numerical core for DS-np charts | Limit search and plotting are under development. | +| Double-sampling np chart | `dsnp_prob_accept()`, `dsnp_arl()`, `dsnp_ass()`, `dsnp_limits()`, `cchart.DSnp()` | DS-np numerical core, limit search, and control chart | Two-stage sampling for high-quality processes with small samples. | | Nonconformities per unit | `cchart.u()` | Shewhart u chart; standardized u chart | Attribute chart for counts per inspection unit. | | Multivariate mean vector | `T2.1()`, `T2.2()`, `cchart.T2.1()`, `cchart.T2.2()` | Hotelling T² charts for Phase I and Phase II | Supports individual and subgroup observations. | | Relative range constants | `d2()`, `d3()` | Numerical integration using Tukey distribution functions | Used by exact R-chart calculations and false alarm diagnostics. | @@ -107,7 +107,7 @@ The package currently implements several core ideas from the research program, b | Candidate extension | Statistical target | Possible function names | Status | |---|---|---|---| -| Double-sampling np chart | Nonconforming proportion in high-quality processes with small samples | `dsnp_limits()`, `cchart.DSnp()` | Numerical core implemented; limit search and plotting planned | +| Double-sampling np chart | Nonconforming proportion in high-quality processes with small samples | `dsnp_limits()`, `cchart.DSnp()` | Implemented | | Generalized variance chart | Multivariate process variability using `|S|` | `gv_limits()`, `cchart.GV()` | Planned | | Cornish-Fisher corrected generalized variance chart | Corrected limits for `|S|` under non-normal sampling distribution | `gv_cf_limits()` | Planned | | Auxiliary trace chart | Complementary monitoring using `tr(V)` | `trv_limits()`, `cchart.trV()` | Planned | diff --git a/man/IQCC-package.Rd b/man/IQCC-package.Rd new file mode 100644 index 0000000..27d69b6 --- /dev/null +++ b/man/IQCC-package.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/constants.R +\docType{package} +\name{IQCC-package} +\alias{IQCC} +\alias{IQCC-package} +\title{Package constants for control chart computations.} +\description{ +Builds statistical control charts with exact limits for univariate and multivariate cases. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/flaviobarros/IQCC} + \item \url{https://flaviobarros.github.io/IQCC} + \item Report bugs at \url{https://github.com/flaviobarros/IQCC/issues} +} + +} +\author{ +\strong{Maintainer}: Flavio Barros \email{flaviomargarito@gmail.com} + +Other contributors: +\itemize{ + \item Emanuel Barbosa [contributor] + \item Elias Goncalves [contributor] + \item Daniela Recchia [contributor] +} + +} +\keyword{internal} diff --git a/man/add.data.Rd b/man/add.data.Rd index cf8deb0..06a626f 100644 --- a/man/add.data.Rd +++ b/man/add.data.Rd @@ -7,7 +7,7 @@ add.data(datum2, estat, T2II, n, j, m = NULL) } \arguments{ -\item{datum2}{The data set for the phase II. Shoul be a vector.} +\item{datum2}{The data set for the phase II. Should be a matrix.} \item{estat}{The values of the auxiliary statistics. Should be a list with a vector with the mean of the mean vectors, a matrix with the average of the @@ -23,8 +23,8 @@ variance-covariance matrices and a matrix with the means.} set is show on the plot.} } \value{ -Add the new observation to the current Hoteliing control chart for -phase II. +Add the new observation to the current Hotelling control chart for +phase II. Returns the new T2 statistic invisibly. } \description{ This function is used to update the phase II control chart with new diff --git a/man/alpha.risk.Rd b/man/alpha.risk.Rd index ae50d04..f378b5c 100644 --- a/man/alpha.risk.Rd +++ b/man/alpha.risk.Rd @@ -7,10 +7,10 @@ alpha.risk(n) } \arguments{ -\item{n}{The sample size.} +\item{n}{The sample size. Can be a vector for multiple sample sizes.} } \value{ -Return the value of the alpha risk for a given sample size n. +Return a vector of alpha risk values for the given sample sizes. } \description{ Used to calculate the real probability of false alarm in the 3-sigma R diff --git a/man/binomdata.Rd b/man/binomdata.Rd index ce8268c..b52838c 100644 --- a/man/binomdata.Rd +++ b/man/binomdata.Rd @@ -4,12 +4,14 @@ \name{binomdata} \alias{binomdata} \title{Binomial Data.} -\format{A data frame with 25 observations on the following 4 variables. +\format{ +A data frame with 25 observations on the following 4 variables. \describe{ \item{i}{Index.} \item{ni}{The sample Size.} \item{Di}{Number of non-conforming units per sample.} -\item{pi}{Proportion of non-conforming units per sample.} }} +\item{pi}{Proportion of non-conforming units per sample.} } +} \source{ Montgomery, D.C.,2001."Introduction to Statistical Quality Control". } diff --git a/man/cchart.DSnp.Rd b/man/cchart.DSnp.Rd new file mode 100644 index 0000000..54e25d8 --- /dev/null +++ b/man/cchart.DSnp.Rd @@ -0,0 +1,130 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cchart.DSnp.R +\name{cchart.DSnp} +\alias{cchart.DSnp} +\title{Double-Sampling np Control Chart} +\usage{ +cchart.DSnp( + x1, + n1, + n2, + p0, + x2 = NULL, + wl = NULL, + ucl1 = NULL, + ucl2 = NULL, + limits = NULL, + alpha = 0.0027, + p1 = NULL, + plot = TRUE, + ... +) +} +\arguments{ +\item{x1}{Integer vector of nonconforming counts from the first sample.} + +\item{n1}{First-stage sample size (positive integer).} + +\item{n2}{Second-stage sample size (positive integer).} + +\item{p0}{In-control nonconforming proportion.} + +\item{x2}{Optional integer vector of nonconforming counts from the second +sample. Must have the same length as \code{x1} when provided. Use +\code{NA} for observations where no second sample was taken. Required +for observations where \code{x1} falls in the intermediate (warning) +zone.} + +\item{wl}{Fractional warning limit. Must be less than \code{ucl1}.} + +\item{ucl1}{Fractional upper control limit for the first stage. Must be +greater than \code{wl}.} + +\item{ucl2}{Fractional upper control limit for the combined samples.} + +\item{limits}{Optional object returned by \code{dsnp_limits()}. When +provided, \code{wl}, \code{ucl1}, and \code{ucl2} are taken from +\code{limits$best}.} + +\item{alpha}{Maximum desired false alarm probability at \code{p0}. Used +only when limits need to be computed via \code{dsnp_limits()}.} + +\item{p1}{Optional out-of-control proportion. When provided, performance +metrics at \code{p1} are included in the returned object.} + +\item{plot}{Logical. If \code{TRUE} (default), draws the control chart. +If \code{FALSE}, only returns the result object.} + +\item{...}{Additional arguments passed to \code{plot()} (currently +unused).} +} +\value{ +An object of class \code{"cchart.DSnp"}, which is a list with the +following elements: +\describe{ + \item{call}{The matched call.} + \item{data}{A data.frame with columns \code{index}, \code{x1}, + \code{x2}, \code{total}, \code{stage}, and \code{signal}.} + \item{limits}{A list with the fractional and integer thresholds: + \code{wl}, \code{ucl1}, \code{ucl2}, \code{wl_accept}, + \code{ucl1_reject}, \code{ucl2_accept}.} + \item{parameters}{A list with \code{n1}, \code{n2}, \code{p0}, + \code{alpha}, and \code{p1}.} + \item{performance}{A list with in-control performance metrics + (\code{arl0}, \code{ass0}, \code{p_signal0}), and optionally + out-of-control metrics (\code{arl1}, \code{ass1}, + \code{p_signal1}) when \code{p1} is provided.} +} +} +\description{ +Build and optionally plot a double-sampling np (DS-np) control chart for +monitoring the nonconforming proportion in high-quality processes. +} +\details{ +The DS-np chart uses two sampling stages. At the first stage, a sample of +size \code{n1} is inspected. If the count \code{x1} is at or below the +warning limit, the process is accepted. If \code{x1} exceeds the first +upper control limit, the process signals out-of-control immediately. +Otherwise, a second sample of size \code{n2} is inspected and the combined +count \code{x1 + x2} is compared to the second upper control limit. + +Limits can be supplied manually via \code{wl}, \code{ucl1}, and +\code{ucl2}, obtained from a pre-computed \code{dsnp_limits()} object via +the \code{limits} argument, or computed automatically inside the function +when neither is provided. + +The fractional limits are converted to integer thresholds using the +convention from the numerical core functions: +\itemize{ + \item \code{wl_accept = floor(wl)}: accept at first stage if + \code{x1 <= wl_accept}. + \item \code{ucl1_reject = floor(ucl1) + 1}: signal at first stage if + \code{x1 >= ucl1_reject}. + \item \code{ucl2_accept = floor(ucl2)}: accept at second stage if + \code{x1 + x2 <= ucl2_accept}. +} +} +\examples{ + +# Small example with manual limits +x1 <- c(0, 1, 2, 3, 1, 0, 2, 4, 1, 0) +x2 <- c(NA, NA, 2, NA, NA, NA, 3, NA, NA, NA) +res <- cchart.DSnp(x1, n1 = 10, n2 = 20, p0 = 0.05, + x2 = x2, wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) +res$limits +res$performance + +} +\references{ +Joekes, S., Smrekar, M. and Barbosa, E. (2015). Extending a +double sampling control chart for non-conforming proportion in high quality +processes to the case of small samples. \emph{Statistical Methodology}. +} +\seealso{ +\code{\link{dsnp_limits}}, \code{\link{dsnp_prob_accept}}, + \code{\link{dsnp_arl}}, \code{\link{dsnp_ass}} +} +\author{ +Daniela R. Recchia, Emanuel P. Barbosa +} diff --git a/man/cchart.R.Rd b/man/cchart.R.Rd index 6cb6bbf..365a685 100644 --- a/man/cchart.R.Rd +++ b/man/cchart.R.Rd @@ -15,7 +15,8 @@ cchart.R(x, n, type = "norm", y = NULL) (traditional Shewhart R chart) and "tukey" (exact R chart). If not specified, a Shewhart R chart will be plotted.} -\item{y}{The data used in phase I to estimate the standard deviation.} +\item{y}{The data used in phase I to estimate the standard deviation. +Required when type = "tukey".} } \value{ Return a R control chart. diff --git a/man/cchart.S.Rd b/man/cchart.S.Rd index 8bae96d..7e0076f 100644 --- a/man/cchart.S.Rd +++ b/man/cchart.S.Rd @@ -13,7 +13,7 @@ cchart.S(x, type = "n", m = NULL) plotted where "n" plots a S chart with normalized probability limits and "e" plots a S chart with exact limits.} -\item{m}{The sample sizes. Only necessary in the control chart with exact +\item{m}{The sample size. Only necessary in the control chart with exact (probability) limits.} } \value{ diff --git a/man/cchart.T2.1.Rd b/man/cchart.T2.1.Rd index d7c8777..18faca2 100644 --- a/man/cchart.T2.1.Rd +++ b/man/cchart.T2.1.Rd @@ -7,12 +7,12 @@ cchart.T2.1(T2, m, n, p) } \arguments{ -\item{T2}{The values of the T2 statistic. Shoul be a matrix.} +\item{T2}{The values of the T2 statistic. Should be a numeric vector.} \item{m}{The number of samples generated previously in data.1.} \item{n}{The size of each sample used previously in data.1. If they are -individual obsersations, then use n = 1.} +individual observations, then use n = 1.} \item{p}{The dimension used previously in function data.1.} } diff --git a/man/cchart.T2.2.Rd b/man/cchart.T2.2.Rd index 43fb6c1..57fc150 100644 --- a/man/cchart.T2.2.Rd +++ b/man/cchart.T2.2.Rd @@ -46,7 +46,7 @@ datum <- data.1(20, 10, mu, Sigma) estat <- stats(datum, 20, 10, 2) datum2 <- data.2(estat, 10, p = 2) T2II <- T2.2(datum2, estat, 10) -# For the first sample j = 1. T2II is a vector with the value of the firts T2 statistic. +# For the first sample j = 1. T2II is a vector with the value of the first T2 statistic. cchart.T2.2(T2II, 20, 10, 1, 25, 2) # Same of the above, but now showing the phase I data set. cchart.T2.2(T2II, 20, 10, 1, 25, 2, datum = datum) @@ -56,7 +56,7 @@ datum <- data.1(50, 1, mu, Sigma) estat <- stats(datum, 50, 1, 2) datum2 <- data.2(estat, 1, p = 2) T2II <- T2.2(datum2, estat, 1) -# For the first sample j = 1. T2II is a vector with the value of the firts T2 statistic. +# For the first sample j = 1. T2II is a vector with the value of the first T2 statistic. cchart.T2.2(T2II, 50, 1, 1, 25, 2) # Same of the above, but now showing the phase I data set. cchart.T2.2(T2II, 50, 1, 1, 25, 2, datum = datum) diff --git a/man/cchart.Xbar.Rd b/man/cchart.Xbar.Rd index f346c89..eae192a 100644 --- a/man/cchart.Xbar.Rd +++ b/man/cchart.Xbar.Rd @@ -4,16 +4,28 @@ \alias{cchart.Xbar} \title{X-bar Control Chart for phase I and II.} \usage{ -cchart.Xbar(x1 = NULL, n1 = NULL, x2 = NULL, n2 = NULL, x2bars = NULL, sigma = NULL) +cchart.Xbar( + x1 = NULL, + n1 = NULL, + x2 = NULL, + n2 = NULL, + x2bars = NULL, + sigma = NULL +) } \arguments{ \item{x1}{The phase I data to be plotted.} + \item{n1}{A value or a vector of values specifying the sample sizes associated with each group for the phase I data.} + \item{x2}{The phase II data to be plotted.} + \item{n2}{A value or a vector of values specifying the sample sizes associated with each group for the phase II data.} + \item{x2bars}{The mean of means from phase I.} + \item{sigma}{The standard deviation from phase I.} } \value{ diff --git a/man/cchart.Xbar1.Rd b/man/cchart.Xbar1.Rd index dcc6bb3..b59c8bd 100644 --- a/man/cchart.Xbar1.Rd +++ b/man/cchart.Xbar1.Rd @@ -13,7 +13,8 @@ cchart.Xbar1(x, sizes) associated with each group.} } \value{ -Return a x-bar control chart for phase I. +Returns a list with the mean of means (x2bar) and the standard +deviation (sigma), invisibly. Also plots the control chart as a side effect. } \description{ Builds the x-bar control chart for phase I. @@ -25,7 +26,7 @@ normal by the central limit theorem. \examples{ data(pistonrings) -cchart.Xbar1(pistonrings[1:25, ]) +cchart.Xbar1(pistonrings[1:25, ], 5) } \seealso{ diff --git a/man/cchart.Xbar2.Rd b/man/cchart.Xbar2.Rd index a22ae6d..0964517 100644 --- a/man/cchart.Xbar2.Rd +++ b/man/cchart.Xbar2.Rd @@ -11,7 +11,7 @@ cchart.Xbar2(x, x2bar, sigma, sizes) \item{x2bar}{The mean of means.} -\item{sigma}{The standar deviation of the data.} +\item{sigma}{The standard deviation of the data.} \item{sizes}{A value or a vector of values specifying the sample sizes associated with each group.} @@ -29,8 +29,8 @@ function XbarI. \examples{ data(pistonrings) -stat <- cchart.Xbar1(pistonrings[1:25, ]) -cchart.Xbar2(pistonrings[26:40, ], stat[[1]][1], stat[[1]][2]) +stat <- cchart.Xbar1(pistonrings[1:25, ], 5) +cchart.Xbar2(pistonrings[26:40, ], stat[[1]][1], stat[[1]][2], 5) } \seealso{ diff --git a/man/cchart.Xbar_R.Rd b/man/cchart.Xbar_R.Rd index c556921..09c0cf9 100644 --- a/man/cchart.Xbar_R.Rd +++ b/man/cchart.Xbar_R.Rd @@ -22,7 +22,7 @@ This function builds the X-bar and R control charts in the same window. data(pistonrings) attach(pistonrings) -cchart.Xbar_R(pistonrings[1:25, ]) +cchart.Xbar_R(pistonrings[1:25, ], 5) } \author{ diff --git a/man/cchart.p.Rd b/man/cchart.p.Rd index a8481e6..62e2ca7 100644 --- a/man/cchart.p.Rd +++ b/man/cchart.p.Rd @@ -4,8 +4,16 @@ \alias{cchart.p} \title{p-chart} \usage{ -cchart.p(x1 = NULL, n1 = NULL, type = "norm", p1 = NULL, x2 = NULL, - n2 = NULL, phat = NULL, p2 = NULL) +cchart.p( + x1 = NULL, + n1 = NULL, + type = "norm", + p1 = NULL, + x2 = NULL, + n2 = NULL, + phat = NULL, + p2 = NULL +) } \arguments{ \item{x1}{The phase I data that will be plotted (if it is a phase I chart).} @@ -38,7 +46,7 @@ This function builds p-charts. \details{ For a phase I p-chart, n1 must be specified and either x1 or p1. For a phase II p-chart, n2 must be specified, plus x2 or p2 and either phat, x1 -and n1, or p1 and n1. The Shewhart is based on normal-aprroximation and +and n1, or p1 and n1. The Shewhart is based on normal-approximation and should be used only for large values of np or n*p (n*p > 6). } \examples{ diff --git a/man/cchart.u.Rd b/man/cchart.u.Rd index d593bc2..84d44fd 100644 --- a/man/cchart.u.Rd +++ b/man/cchart.u.Rd @@ -4,8 +4,16 @@ \alias{cchart.u} \title{u-chart} \usage{ -cchart.u(x1 = NULL, n1 = NULL, type = "norm", u1 = NULL, x2 = NULL, - n2 = NULL, lambda = NULL, u2 = NULL) +cchart.u( + x1 = NULL, + n1 = NULL, + type = "norm", + u1 = NULL, + x2 = NULL, + n2 = NULL, + lambda = NULL, + u2 = NULL +) } \arguments{ \item{x1}{The phase I data that will be plotted (if it is a phase I chart).} diff --git a/man/data.1.Rd b/man/data.1.Rd index 9033fa2..9f89c15 100644 --- a/man/data.1.Rd +++ b/man/data.1.Rd @@ -14,14 +14,14 @@ use n = 1.} \item{mu}{The vector with the means of the data to be generated.} -\item{Sigma}{The vector with the variance-covariance matrix of the data to +\item{Sigma}{The variance-covariance matrix of the data to be generated.} } \value{ Return an array with the simulated data. } \description{ -This function simulate a normal data set to be used in the phase I Hoteliing +This function simulates a normal data set to be used in the phase I Hotelling control charts. } \examples{ diff --git a/man/data.2.Rd b/man/data.2.Rd index 27b10fd..4b733ef 100644 --- a/man/data.2.Rd +++ b/man/data.2.Rd @@ -7,8 +7,9 @@ data.2(estat, n, delta = 0, p) } \arguments{ -\item{estat}{The values of the auxiliary statistics. Should be a list with a -matrix with the means, mean of the means and mean of the standard deviation.} +\item{estat}{The values of the auxiliary statistics. Should be a list with +the mean of means, the average variance-covariance matrix, and a matrix +with the means of each sample.} \item{n}{The size of each sample. If they are individual observations, use n = 1.} diff --git a/man/dsnp_limits.Rd b/man/dsnp_limits.Rd index be4cbab..af124ce 100644 --- a/man/dsnp_limits.Rd +++ b/man/dsnp_limits.Rd @@ -64,6 +64,10 @@ The function enumerates all valid combinations of integer thresholds limits compatible with \code{dsnp_prob_accept}, and evaluates each candidate's performance at the in-control proportion p0. When p1 is supplied, out-of-control metrics are also computed and used for ranking. + +When the warning zone is empty (ucl1_reject = wl_accept + 1), the +chart degenerates to a single-sample scheme and probabilities are +computed directly via \code{pbinom} without calling the core functions. } \examples{ diff --git a/man/moonroof.Rd b/man/moonroof.Rd index a2507b3..88c3838 100644 --- a/man/moonroof.Rd +++ b/man/moonroof.Rd @@ -4,12 +4,14 @@ \name{moonroof} \alias{moonroof} \title{Moonroof} -\format{A data frame with 34 observations on the following 4 variables. +\format{ +A data frame with 34 observations on the following 4 variables. \describe{ \item{i}{Index.} \item{yi}{The number of defects.} \item{ni}{The sample size.} -\item{ui}{The proportion of defects.} }} +\item{ui}{The proportion of defects.} } +} \source{ DeVor, R.E.; Chang, T.; Sutherland, J.W., 2007. "Statistical Quality Design and Control". diff --git a/man/pistonrings.Rd b/man/pistonrings.Rd index 6256240..4e05303 100644 --- a/man/pistonrings.Rd +++ b/man/pistonrings.Rd @@ -4,13 +4,15 @@ \name{pistonrings} \alias{pistonrings} \title{Piston Rings Data Set.} -\format{A data frame with 40 observations on the following 5 variables. +\format{ +A data frame with 40 observations on the following 5 variables. \describe{ -\item{V1}{The fisrt measure.} +\item{V1}{The first measure.} \item{V2}{The second measure.} \item{V3}{The third measure.} -\item{V4}{The fouth measure.} -\item{V5}{The fifth measure.} }} +\item{V4}{The fourth measure.} +\item{V5}{The fifth measure.} } +} \source{ Montgomery, D.C.,(2008)."Introduction to Statistical Quality Control".4th Ed. Wiley diff --git a/man/plot.cchart.DSnp.Rd b/man/plot.cchart.DSnp.Rd new file mode 100644 index 0000000..791a91e --- /dev/null +++ b/man/plot.cchart.DSnp.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cchart.DSnp.R +\name{plot.cchart.DSnp} +\alias{plot.cchart.DSnp} +\title{Plot a DS-np Control Chart} +\usage{ +\method{plot}{cchart.DSnp}(x, ...) +} +\arguments{ +\item{x}{An object of class \code{"cchart.DSnp"}.} + +\item{...}{Additional graphical parameters (currently unused).} +} +\value{ +Invisible \code{x}. +} +\description{ +S3 method for plotting objects of class \code{"cchart.DSnp"}. +} +\details{ +The plot shows the first-stage counts \code{x1} against the sample index. +Points requiring a second sample are shown as open circles. Points that +signal (at either stage) are shown in red. Horizontal lines mark the +fractional limits. + +Note that \code{ucl2} is on the scale of the combined count +\code{x1 + x2}. The plot is a simple operational visualization; the +full decision logic is in the returned object. +} diff --git a/man/softdrink.Rd b/man/softdrink.Rd index c5adb5c..aca1960 100644 --- a/man/softdrink.Rd +++ b/man/softdrink.Rd @@ -4,7 +4,8 @@ \name{softdrink} \alias{softdrink} \title{Soft Drink Data Set.} -\format{A data frame with 15 lines and 10 columns. +\format{ +A data frame with 15 lines and 10 columns. \describe{ \item{x1}{The first measure.} \item{x2}{The second measure.} @@ -13,10 +14,11 @@ \item{x5}{The fifth measure.} \item{x6}{The sixth measure.} \item{x7}{The seventh measure.} -\item{x8}{The eigth measure.} +\item{x8}{The eighth measure.} \item{x9}{The ninth measure.} \item{x10}{The tenth -measure.} }} +measure.} } +} \source{ Montgomery, D.C.,(2001)."Introduction to Statistical Quality Control".4th ed. Wiley. diff --git a/man/table.qtukey.Rd b/man/table.qtukey.Rd index f992649..db31c2e 100644 --- a/man/table.qtukey.Rd +++ b/man/table.qtukey.Rd @@ -13,10 +13,8 @@ to 1 minus the confidence level.} \item{n}{The maximum sample size.} } \value{ -It is used the fact that the sample relative range distribution is -the same as the sample studentized range distribution (tukey distribution) -with infinity d.f. in the denominator . It is considered 4 quantiles: -alpha/2 , alpha , 1-alpha and 1-alpha/2, for different sample size values . +Returns a matrix with 4 columns containing the quantiles, printed to +the console and returned invisibly. } \description{ Builds a table with quantiles of the sample relative range distribution. diff --git a/tests/testthat/test-cchart-DSnp.R b/tests/testthat/test-cchart-DSnp.R new file mode 100644 index 0000000..c74e386 --- /dev/null +++ b/tests/testthat/test-cchart-DSnp.R @@ -0,0 +1,315 @@ +# --- cchart.DSnp tests --- + +# --- 1. Structure tests --- + +test_that("cchart.DSnp returns structured list with plot=FALSE", { + x1 <- c(0, 1, 2, 3, 1) + x2 <- c(NA, NA, 1, NA, NA) + res <- cchart.DSnp(x1, n1 = 10, n2 = 20, p0 = 0.05, + x2 = x2, wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_type(res, "list") + expect_s3_class(res, "cchart.DSnp") + expect_named(res, c("call", "data", "limits", "parameters", "performance")) +}) + +test_that("cchart.DSnp data has correct columns", { + x1 <- c(0, 1, 2) + x2 <- c(NA, NA, 1) + res <- cchart.DSnp(x1, n1 = 10, n2 = 20, p0 = 0.05, + x2 = x2, wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_s3_class(res$data, "data.frame") + expect_named(res$data, c("index", "x1", "x2", "total", "stage", "signal")) + expect_equal(nrow(res$data), 3) +}) + +test_that("cchart.DSnp limits contains all threshold elements", { + res <- cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_named(res$limits, c("wl", "ucl1", "ucl2", + "wl_accept", "ucl1_reject", "ucl2_accept")) +}) + +test_that("cchart.DSnp parameters stores inputs", { + res <- cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + alpha = 0.01, plot = FALSE) + expect_equal(res$parameters$n1, 10) + expect_equal(res$parameters$n2, 20) + expect_equal(res$parameters$p0, 0.05) + expect_equal(res$parameters$alpha, 0.01) + expect_null(res$parameters$p1) +}) + +test_that("cchart.DSnp performance contains arl0, ass0, p_signal0", { + res <- cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_true("arl0" %in% names(res$performance)) + expect_true("ass0" %in% names(res$performance)) + expect_true("p_signal0" %in% names(res$performance)) +}) + +# --- 2. Manual limits integer thresholds --- + +test_that("cchart.DSnp integer thresholds are correct", { + res <- cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_equal(res$limits$wl_accept, 1) + expect_equal(res$limits$ucl1_reject, 3) + expect_equal(res$limits$ucl2_accept, 4) +}) + +# --- 3. Classification logic --- + +test_that("cchart.DSnp accepts at first stage when x1 <= wl_accept", { + # wl_accept = 1, so x1 = 0 and x1 = 1 should accept + res <- cchart.DSnp(c(0, 1, 1), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_equal(res$data$stage[1], "accept_first") + expect_false(res$data$signal[1]) + expect_equal(res$data$stage[2], "accept_first") + expect_false(res$data$signal[2]) +}) + +test_that("cchart.DSnp signals at first stage when x1 >= ucl1_reject", { + # ucl1_reject = 3, so x1 = 3 should signal + res <- cchart.DSnp(c(3, 4), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_equal(res$data$stage[1], "signal_first") + expect_true(res$data$signal[1]) + expect_equal(res$data$stage[2], "signal_first") + expect_true(res$data$signal[2]) +}) + +test_that("cchart.DSnp requires second sample when x1 in intermediate zone", { + # x1 = 2 is between wl_accept=1 and ucl1_reject=3 + res <- cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(1), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_equal(res$data$stage[1], "accept_second") + expect_false(res$data$signal[1]) + expect_equal(res$data$total[1], 3) +}) + +test_that("cchart.DSnp total <= ucl2_accept accepts at second stage", { + # x1=2, x2=2, total=4, ucl2_accept=4 -> accept + res <- cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(2), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_equal(res$data$stage[1], "accept_second") + expect_false(res$data$signal[1]) + expect_equal(res$data$total[1], 4) +}) + +test_that("cchart.DSnp total > ucl2_accept signals at second stage", { + # x1=2, x2=3, total=5, ucl2_accept=4 -> signal + res <- cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(3), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + expect_equal(res$data$stage[1], "signal_second") + expect_true(res$data$signal[1]) + expect_equal(res$data$total[1], 5) +}) + +# --- 4. Validation error tests --- + +test_that("cchart.DSnp errors on negative x1", { + expect_error( + cchart.DSnp(c(-1), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "x1 must not contain negative values" + ) +}) + +test_that("cchart.DSnp errors when x1 > n1", { + expect_error( + cchart.DSnp(c(11), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "x1 values must not exceed n1" + ) +}) + +test_that("cchart.DSnp errors on negative x2", { + expect_error( + cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(-1), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "x2 must not contain negative values" + ) +}) + +test_that("cchart.DSnp errors when x2 > n2", { + expect_error( + cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(21), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "x2 values must not exceed n2" + ) +}) + +test_that("cchart.DSnp errors when x2 needed but missing", { + expect_error( + cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "Second sample.*required" + ) +}) + +test_that("cchart.DSnp errors when x2 needed but NA", { + expect_error( + cchart.DSnp(c(2), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(NA), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "Second sample.*required" + ) +}) + +test_that("cchart.DSnp errors when only partial manual limits provided", { + expect_error( + cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, plot = FALSE), + "All of.*wl.*ucl1.*ucl2.*must be provided together" + ) + expect_error( + cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, plot = FALSE), + "All of.*wl.*ucl1.*ucl2.*must be provided together" + ) +}) + +test_that("cchart.DSnp errors when limits and manual limits both given", { + lim <- dsnp_limits(p0 = 0.05, n1 = 5, n2 = 10, alpha = 0.10, + max_results = 1) + expect_error( + cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + limits = lim, plot = FALSE), + "Cannot specify both.*limits.*and manual" + ) +}) + +test_that("cchart.DSnp errors on non-integer x1", { + expect_error( + cchart.DSnp(c(1.5), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, plot = FALSE), + "x1 must contain integer values" + ) +}) + +# --- 5. Use with dsnp_limits() --- + +test_that("cchart.DSnp auto-computes limits via dsnp_limits when none given", { + res <- cchart.DSnp(c(0, 1, 2, 0, 1), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(NA, NA, 1, NA, NA), + plot = FALSE) + expect_true(!is.null(res$limits$wl)) + expect_true(!is.null(res$limits$ucl1)) + expect_true(!is.null(res$limits$ucl2)) + expect_true(is.numeric(res$limits$wl)) +}) + +test_that("cchart.DSnp works with limits object from dsnp_limits", { + lim <- dsnp_limits(p0 = 0.05, n1 = 5, n2 = 10, alpha = 0.10, + max_results = 1) + res <- cchart.DSnp(c(0, 1, 0), n1 = 5, n2 = 10, p0 = 0.05, + x2 = c(NA, 0, NA), + limits = lim, plot = FALSE) + expect_equal(res$limits$wl, lim$best$wl) + expect_equal(res$limits$ucl1, lim$best$ucl1) + expect_equal(res$limits$ucl2, lim$best$ucl2) +}) + +# --- 6. Performance consistency --- + +test_that("cchart.DSnp performance matches dsnp_* functions", { + wl <- 1.5; ucl1 <- 2.5; ucl2 <- 4.5 + n1 <- 10; n2 <- 20; p0 <- 0.05 + + res <- cchart.DSnp(c(0, 1, 2, 3), n1 = n1, n2 = n2, p0 = p0, + x2 = c(NA, NA, 1, NA), + wl = wl, ucl1 = ucl1, ucl2 = ucl2, + plot = FALSE) + + arl0 <- dsnp_arl(p0, n1, n2, wl, ucl1, ucl2) + ass0 <- dsnp_ass(p0, n1, n2, wl, ucl1) + pa0 <- dsnp_prob_accept(p0, n1, n2, wl, ucl1, ucl2) + + expect_equal(res$performance$arl0, arl0$arl) + expect_equal(res$performance$ass0, ass0$ass) + expect_equal(res$performance$p_signal0, pa0$p_signal) +}) + +test_that("cchart.DSnp performance includes p1 metrics when p1 provided", { + res <- cchart.DSnp(c(0), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + p1 = 0.10, plot = FALSE) + expect_true("arl1" %in% names(res$performance)) + expect_true("ass1" %in% names(res$performance)) + expect_true("p_signal1" %in% names(res$performance)) + + arl1 <- dsnp_arl(0.10, 10, 20, 1.5, 2.5, 4.5) + expect_equal(res$performance$arl1, arl1$arl) +}) + +# --- 7. Plot tests --- + +test_that("cchart.DSnp plot=FALSE does not open graphics", { + expect_no_error( + cchart.DSnp(c(0, 1), n1 = 10, n2 = 20, p0 = 0.05, + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + ) +}) + +test_that("cchart.DSnp plot=TRUE produces a plot without error", { + pdf(NULL) # null device + on.exit(dev.off()) + res <- cchart.DSnp(c(0, 1, 2, 3, 0), n1 = 10, n2 = 20, p0 = 0.05, + x2 = c(NA, NA, 1, NA, NA), + wl = 1.5, ucl1 = 2.5, ucl2 = 4.5) + expect_s3_class(res, "cchart.DSnp") +}) + +# --- 8. Mixed observation types --- + +test_that("cchart.DSnp handles mixed stages correctly", { + x1 <- c(0, 1, 2, 3, 1, 0, 2, 4, 1, 0) + x2 <- c(NA, NA, 2, NA, NA, NA, 3, NA, NA, NA) + res <- cchart.DSnp(x1, n1 = 10, n2 = 20, p0 = 0.05, + x2 = x2, wl = 1.5, ucl1 = 2.5, ucl2 = 4.5, + plot = FALSE) + + # x1=0 -> accept_first + expect_equal(res$data$stage[1], "accept_first") + expect_false(res$data$signal[1]) + + # x1=1 -> accept_first + expect_equal(res$data$stage[2], "accept_first") + + # x1=2, x2=2 -> total=4, accept_second + expect_equal(res$data$stage[3], "accept_second") + expect_equal(res$data$total[3], 4) + expect_false(res$data$signal[3]) + + # x1=3 -> signal_first + expect_equal(res$data$stage[4], "signal_first") + expect_true(res$data$signal[4]) + + # x1=2, x2=3 -> total=5, signal_second + expect_equal(res$data$stage[7], "signal_second") + expect_equal(res$data$total[7], 5) + expect_true(res$data$signal[7]) + + # x1=4 -> signal_first + expect_equal(res$data$stage[8], "signal_first") + expect_true(res$data$signal[8]) +}) diff --git a/vignettes/iqcc-positioning.Rmd b/vignettes/iqcc-positioning.Rmd index aa6cf8b..94ab703 100644 --- a/vignettes/iqcc-positioning.Rmd +++ b/vignettes/iqcc-positioning.Rmd @@ -46,7 +46,7 @@ well calibrated for the distribution and sample size at hand. |---|---|---| | Range-based dispersion monitoring | Normal approximations can inflate the actual false alarm risk, especially for small subgroups. | Use exact range-chart limits based on the relative range distribution. | | Standard deviation monitoring | The distribution of the sample standard deviation is asymmetric for small samples. | Use exact limits derived from the chi-square distribution of the sample variance. | -| Nonconforming proportion in high-quality processes | The binomial distribution is discrete, bounded, and strongly skewed when the nonconforming proportion is very small. | Use Cornish-Fisher corrected limits or DS-np methods under development. | +| Nonconforming proportion in high-quality processes | The binomial distribution is discrete, bounded, and strongly skewed when the nonconforming proportion is very small. | Use Cornish-Fisher corrected limits or the double-sampling np chart. | | Multivariate process mean monitoring | Hotelling T² requires phase-specific calibration. | Use the implemented Phase I and Phase II Hotelling T² functions. | | Multivariate process variability | A single normal approximation for generalized variance may be poorly calibrated. | Candidate future extensions include generalized variance and auxiliary trace charts. | @@ -63,8 +63,8 @@ The current package includes several families of functions: monitoring. - `alpha.risk()` for studying the actual false alarm risk of classical range charts. -- `dsnp_prob_accept()`, `dsnp_arl()`, and `dsnp_ass()` for the numerical core of - double-sampling np charts. +- `dsnp_prob_accept()`, `dsnp_arl()`, `dsnp_ass()`, `dsnp_limits()`, and + `cchart.DSnp()` for double-sampling np charts. ## Example: using a corrected p chart @@ -86,20 +86,28 @@ cchart.p( ) ``` -## Example: inspecting the DS-np numerical core +## Example: DS-np chart with automatic limit search -The DS-np plotting interface is still under development, but the numerical core -can already be used to inspect the acceptance probability, average run length, -and average sample size for a proposed plan. +The double-sampling np chart uses two sampling stages to monitor the +nonconforming proportion in high-quality processes. `dsnp_limits()` searches +for feasible fractional control limits, and `cchart.DSnp()` builds and plots +the chart. -```{r dsnp-core-example, eval = FALSE} +```{r dsnp-example, eval = FALSE} library(IQCC) -plan <- list(n1 = 34, n2 = 162, wl = 1.5, ucl1 = 2.5, ucl2 = 4.5) +# Find limits for a given in-control proportion +lim <- dsnp_limits(p0 = 0.005, n1 = 34, n2 = 162, alpha = 0.0027, + p1 = 0.0075) +lim$best[, c("wl", "ucl1", "ucl2", "arl0", "arl1", "ass0")] -dsnp_prob_accept(0.005, plan$n1, plan$n2, plan$wl, plan$ucl1, plan$ucl2) -dsnp_arl(0.005, plan$n1, plan$n2, plan$wl, plan$ucl1, plan$ucl2) -dsnp_ass(0.005, plan$n1, plan$n2, plan$wl, plan$ucl1) +# Build the chart from observed data +x1 <- c(0, 0, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0) +x2 <- c(NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, NA, NA, NA, NA) + +chart <- cchart.DSnp(x1, n1 = 34, n2 = 162, p0 = 0.005, + x2 = x2, limits = lim) +chart$performance ``` ## Development principles @@ -122,7 +130,6 @@ where classical approximations are simple but inaccurate. The main planned extensions are: -- automatic limit search and plotting for double-sampling np charts; - generalized variance charts for multivariate process variability; - Cornish-Fisher corrected generalized variance limits; - auxiliary trace charts for complementary multivariate variability monitoring;