% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/calib.test.R
\name{calib.test}
\alias{calib.test}
\alias{print.calib_test}
\alias{as.data.frame.calib_test}
\title{Calibration-threshold robustness test for QCA solutions}
\usage{
calib.test(
  raw.data,
  calib.data,
  outcome,
  conditions,
  calib_spec,
  test.conditions = conditions,
  test.outcome = FALSE,
  anchors_to_test = NULL,
  solution = "all",
  include = NULL,
  which_M = 1,
  unit_step = NULL,
  unit_step_divisor = 10,
  max_steps = 20,
  incl.cut = 1,
  n.cut = 1,
  dir.exp = NULL,
  i_mode = c("all", "C1P1"),
  exclude_mode = c("recompute", "static", "none"),
  exclude_recompute = list(type = 2),
  exclude_static = NULL,
  result_shape = c("wide", "long"),
  progress = TRUE,
  ...
)

\method{print}{calib_test}(x, row.names = FALSE, ...)

\method{as.data.frame}{calib_test}(x, ...)
}
\arguments{
\item{raw.data}{A data frame object containing the raw, uncalibrated
condition columns.}

\item{calib.data}{A data frame object containing the calibrated outcome
and calibrated condition columns used for the analysis.}

\item{outcome}{Name of the outcome column in \code{calib.data}. This can be
supplied as a character string or as an unquoted name.}

\item{conditions}{Character vector of calibrated condition names in
\code{calib.data}. This can be supplied as a character vector or as unquoted
names.}

\item{calib_spec}{Named list of calibration specifications. When
\code{test.outcome = FALSE}, it must contain one entry per condition. When
\code{test.outcome = TRUE}, it must contain one entry per condition plus one
entry named by \code{outcome}. Each entry must contain \code{raw}, \code{type}, and
\code{thresholds}, may contain \code{method}, and may contain \code{calibrate}, a named
list of additional \code{\link[QCA:calibrate]{QCA::calibrate()}} arguments.}

\item{test.conditions}{Subset of \code{conditions} to perturb. Use \code{NULL} only
when \code{test.outcome = TRUE} and no condition calibration should be tested.}

\item{test.outcome}{Logical; if \code{TRUE}, also perturb the outcome
calibration while keeping the outcome out of \code{conditions}.}

\item{anchors_to_test}{Character vector of anchors to test, or \code{NULL} to
test all anchors implied by each set's calibration method. Crisp
sets use \code{"T"}. Fuzzy direct three-threshold sets use \code{"E"},
\code{"C"}, and \code{"I"}. Fuzzy direct six-threshold sets use \code{"E1"},
\code{"C1"}, \code{"I1"}, \code{"I2"}, \code{"C2"}, and \code{"E2"}. Fuzzy indirect sets
use \code{"T1"}, \code{"T2"}, and so on.}

\item{solution}{Solution type to monitor. Accepted values are \code{"all"},
\code{"con"} or \code{"conservative"}, \code{"par"} or \code{"parsimonious"}, and \code{"int"} or
\code{"intermediate"}.}

\item{include}{Optional minimization include setting. Currently, this
argument accepts only \code{NULL}, \code{""}, or \code{"?"}.}

\item{which_M}{Positive integer giving which solution alternative to use
when minimization returns multiple models.}

\item{unit_step}{Numeric step size used to move thresholds. Supply either
one value applied to all tested sets, one value per condition when
\code{test.outcome = FALSE}, or one value per \code{c(conditions, outcome)} when
\code{test.outcome = TRUE}. If \code{NULL}, qcaERT computes scale-aware steps from
each set's threshold spacing.}

\item{unit_step_divisor}{Positive number used to compute \code{unit_step}
automatically when \code{unit_step = NULL}. The default is \code{10}.}

\item{max_steps}{Maximum number of upward or downward threshold moves to
attempt for each tested anchor.}

\item{incl.cut}{Inclusion cutoff passed to \code{\link[QCA:truthTable]{QCA::truthTable()}}.}

\item{n.cut}{Frequency cutoff passed to \code{\link[QCA:truthTable]{QCA::truthTable()}}.}

\item{dir.exp}{Directional expectations used when the monitored solution is
intermediate.}

\item{i_mode}{Character string controlling intermediate-solution selection.
Accepted values are \code{"all"} and \code{"C1P1"}.}

\item{exclude_mode}{Character string controlling how excluded rows are
handled for parsimonious and intermediate minimization. \code{"recompute"}
recalculates exclusions from each perturbed truth table using
\code{exclude_recompute}, \code{"static"} reuses \code{exclude_static}, and \code{"none"}
does not use exclusions.}

\item{exclude_recompute}{Named list of arguments passed to \code{\link[QCA:findRows]{QCA::findRows()}}
when \code{exclude_mode = "recompute"}.}

\item{exclude_static}{Already computed exclusion object reused when
\code{exclude_mode = "static"}.}

\item{result_shape}{Layout of the clean \code{results} table when
\code{solution = "all"}. \code{"wide"} keeps one row per tested
set-anchor-direction path with solution-type-specific columns such as
\code{con_last_safe} and \code{par_reason}. \code{"long"} returns one row per tested
set-anchor-direction path and solution type, with a \code{solution_type} column.
Single-solution-type calls keep the compact single-solution-type layout.}

\item{progress}{Logical; if \code{TRUE} and the session is interactive, show a
text progress bar.}

\item{...}{Additional arguments routed through the QCA workflow. Arguments
matching \code{\link[QCA:truthTable]{QCA::truthTable()}} are forwarded to truth table construction;
remaining minimization arguments are filtered and forwarded to
\code{\link[QCA:minimize]{QCA::minimize()}}. The function also looks in \code{...} for \code{include},
\code{dir.exp}, or \code{direxp} if those arguments were not supplied explicitly.
In \code{\link[=print.calib_test]{print.calib_test()}}, \code{...} is passed to \code{\link[=print.data.frame]{print.data.frame()}}. In
\code{\link[=as.data.frame.calib_test]{as.data.frame.calib_test()}}, \code{...} is ignored.}

\item{x}{A \code{calib_test} object returned by \code{\link[=calib.test]{calib.test()}}.}

\item{row.names}{Logical; passed to \code{\link[=print.data.frame]{print.data.frame()}} by
\code{\link[=print.calib_test]{print.calib_test()}}.}
}
\value{
An object of class \code{calib_test} with the following components:
\describe{
\item{\code{diagnostics}}{A detailed data frame with one row per tested
anchor-direction path. When \code{solution = "all"}, each monitored solution
type is searched independently, so diagnostics has one row per tested
anchor, direction, and solution type. It includes the starting threshold, the
last safe value, the first failing value, the number of successful
steps, stop reason, and change classification.}
\item{\code{results}}{A compact data frame with the columns \code{set}, \code{role},
\code{raw}, \code{type}, \code{method}, \code{anchor}, \code{direction}, \code{start},
\code{last_safe}, \code{first_failing}, \code{step_unit}, \code{steps}, \code{total_delta},
\code{pct_raw_range}, and \code{reason}. When \code{solution = "all"} and
\code{result_shape = "wide"}, the row unit remains set-anchor-direction,
but the boundary and reason columns are solution-type-specific, using prefixes
\code{con_}, \code{par_}, and, when available, \code{int_}. When
\code{result_shape = "long"}, \code{results} has one row per tested path and
solution type.}
\item{\code{bounds}}{A named list of compact lower/upper bound matrices, one
per tested set. When \code{solution = "all"}, each tested set contains a
named list of solution-type-specific lower/upper matrices.}
\item{\code{baseline}}{A list containing the baseline analysis status,
selected solution terms used for comparison, metadata, and
solution-type-specific baseline objects.}
\item{\code{by_set}}{A named list containing per-set step results, including
path-level traces.}
\item{\code{settings}}{A list containing the analysis settings used to build
the result object.}
}

\code{print.calib_test()} prints a concise summary and a compact display table.
In that printed table, \code{set} and \code{role} are omitted and the raw source
column is labelled \code{condition}. \code{as.data.frame.calib_test()} returns the
stored \code{results} table.
If \code{ggplot2} is installed, \code{plot.calib_test()} provides interval, heatmap,
and trace views; see \code{?qcaERT_plots}.
}
\description{
Perturbs calibration thresholds for selected conditions, and optionally the
outcome, one anchor and one direction at a time, and checks when the
monitored QCA solution changes.
For each tested path, the function records the starting threshold, the last
value that preserved the baseline solution, the first value that changed the
solution or triggered an error, and the reason the path stopped.
}
\details{
The function rebuilds calibrated condition columns, and the outcome when
\code{test.outcome = TRUE}, from \code{raw.data} using \code{calib_spec}. It then compares
perturbed analyses against the baseline solution under the selected solution
type.

When \code{solution = "all"}, conservative, parsimonious, and, when requested,
intermediate paths are searched independently for each tested set, anchor,
and direction.

Each tested path starts from the supplied threshold for one method-specific
anchor of one set and moves in one direction at a time (\code{"lower"} or
\code{"upper"}), using \code{unit_step} until one of four things happens:
\itemize{
\item the monitored solution changes,
\item minimization fails,
\item the raw-data range or anchor ordering blocks the next move, or
\item \code{max_steps} is reached.
}

The shared solution-control, exclusion, calibration-specification, and
returned-object conventions are described in \code{?qcaERT_conventions}.
}
\examples{
\donttest{
library(QCA)
library(ggplot2)
data(LR)

conditions <- c("DEV", "URB", "LIT", "IND", "STB")
outcome <- "SURV"
dir_exp <- rep("1", length(conditions))

thresholds <- list(
  DEV = findTh(LR$DEV, groups = 7),
  URB = findTh(LR$URB, groups = 4),
  LIT = findTh(LR$LIT, groups = 4),
  IND = findTh(LR$IND, groups = 4),
  STB = findTh(LR$STB, groups = 4),
  SURV = findTh(LR$SURV, groups = 4)
)

dat <- LR
dat$DEV <- calibrate(LR$DEV, type = "fuzzy", thresholds = thresholds$DEV)
dat$URB <- calibrate(LR$URB, type = "fuzzy", thresholds = thresholds$URB)
dat$LIT <- calibrate(LR$LIT, type = "fuzzy", thresholds = thresholds$LIT)
dat$IND <- calibrate(LR$IND, type = "fuzzy", thresholds = thresholds$IND)
dat$STB <- calibrate(LR$STB, type = "fuzzy", thresholds = thresholds$STB)
dat$SURV <- calibrate(LR$SURV, type = "fuzzy", thresholds = thresholds$SURV)

calib_spec <- list(
  DEV = list(raw = "DEV", type = "fuzzy", method = "direct", thresholds = thresholds$DEV),
  URB = list(raw = "URB", type = "fuzzy", method = "direct", thresholds = thresholds$URB),
  LIT = list(raw = "LIT", type = "fuzzy", method = "direct", thresholds = thresholds$LIT),
  IND = list(raw = "IND", type = "fuzzy", method = "direct", thresholds = thresholds$IND),
  STB = list(raw = "STB", type = "fuzzy", method = "direct", thresholds = thresholds$STB)
)

calib_spec_outcome <- calib_spec
calib_spec_outcome$SURV <- list(
  raw = "SURV",
  type = "fuzzy",
  method = "direct",
  thresholds = thresholds$SURV
)

# Common use: test selected calibrated conditions with scale-aware steps.
calib_out <- calib.test(
  raw.data = LR,
  calib.data = dat,
  outcome = outcome,
  conditions = conditions,
  calib_spec = calib_spec,
  test.conditions = c("DEV", "URB"),
  unit_step = NULL,
  unit_step_divisor = 10,
  max_steps = 5,
  incl.cut = 0.8,
  n.cut = 1,
  solution = "all",
  dir.exp = dir_exp,
  progress = TRUE
)

calib_out
as.data.frame(calib_out)
calib_out$bounds
calib_out$diagnostics

plot(calib_out, solution_type = "conservative")
plot(calib_out, solution_type = "conservative", type = "heatmap")

# Special case: test the calibrated outcome without putting it in
# conditions.
outcome_out <- calib.test(
  raw.data = LR,
  calib.data = dat,
  outcome = outcome,
  conditions = conditions,
  calib_spec = calib_spec_outcome,
  test.conditions = NULL,
  test.outcome = TRUE,
  unit_step = NULL,
  unit_step_divisor = 10,
  max_steps = 5,
  incl.cut = 0.8,
  n.cut = 1,
  solution = "all",
  dir.exp = dir_exp,
  progress = TRUE
)

outcome_out
as.data.frame(outcome_out)
plot(outcome_out, solution_type = "conservative")

# Special case: focus on selected anchors in a six-threshold direct
# calibration. For DEV, anchors are E1, C1, I1, I2, C2, and E2.
dev_anchor_out <- calib.test(
  raw.data = LR,
  calib.data = dat,
  outcome = outcome,
  conditions = conditions,
  calib_spec = calib_spec,
  test.conditions = "DEV",
  anchors_to_test = c("E1", "C1", "I1"),
  unit_step = NULL,
  unit_step_divisor = 10,
  max_steps = 5,
  incl.cut = 0.8,
  n.cut = 1,
  solution = "all",
  dir.exp = dir_exp,
  progress = TRUE
)

as.data.frame(dev_anchor_out)

plot(
  dev_anchor_out,
  solution_type = "conservative",
  type = "trace",
  set = "DEV",
  anchor = "E1",
  direction = "lower"
)

# Special case: indirect calibration. Indirect anchors are positional:
# T1, T2, and so on.
thresholds_indirect <- thresholds
thresholds_indirect$DEV <- findTh(LR$DEV, groups = 4)

dat_indirect <- dat
dat_indirect$DEV <- calibrate(
  LR$DEV,
  type = "fuzzy",
  method = "indirect",
  thresholds = thresholds_indirect$DEV
)

calib_spec_indirect <- calib_spec
calib_spec_indirect$DEV <- list(
  raw = "DEV",
  type = "fuzzy",
  method = "indirect",
  thresholds = thresholds_indirect$DEV
)

indirect_out <- calib.test(
  raw.data = LR,
  calib.data = dat_indirect,
  outcome = outcome,
  conditions = conditions,
  calib_spec = calib_spec_indirect,
  test.conditions = "DEV",
  anchors_to_test = c("T1", "T2"),
  unit_step = NULL,
  unit_step_divisor = 10,
  max_steps = 5,
  incl.cut = 0.8,
  n.cut = 1,
  solution = "all",
  dir.exp = dir_exp,
  progress = TRUE
)

as.data.frame(indirect_out)
}

}
\seealso{
\link{qcaERT_plots}, \code{\link[=incl.test]{incl.test()}}, \code{\link[=ncut.test]{ncut.test()}}, \code{\link[=loo.test]{loo.test()}},
\code{\link[=subsample.test]{subsample.test()}}, \code{\link[=altset.test]{altset.test()}}, \code{\link[=theory.test]{theory.test()}}, \code{\link[=cluster.test]{cluster.test()}},
\code{\link[=sol.df]{sol.df()}}
}
