% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/loo.test.R
\name{loo.test}
\alias{loo.test}
\alias{print.loo_test}
\alias{as.data.frame.loo_test}
\title{Leave-one-out robustness test for QCA solutions}
\usage{
loo.test(
  data,
  outcome,
  conditions = NULL,
  cases = NULL,
  case_labels = NULL,
  calib = c("fixed", "recompute"),
  raw.data = NULL,
  calib_spec = NULL,
  incl.cut = 1,
  n.cut = 1,
  solution = "all",
  include = NULL,
  dir.exp = NULL,
  which_M = 1,
  i_mode = c("all", "C1P1"),
  exclude_mode = c("recompute", "static", "none"),
  exclude_recompute = list(type = 2),
  exclude_static = NULL,
  fit_measures = c("inclS", "PRI", "covS"),
  fit_tol = 0,
  progress = TRUE,
  ...
)

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

\method{as.data.frame}{loo_test}(x, ...)
}
\arguments{
\item{data}{A data frame object containing the outcome and condition
columns used in the QCA analysis. \code{data} must have at least
two rows.}

\item{outcome}{Name of the outcome. This must be a single
non-empty character string.}

\item{conditions}{Optional character vector of condition names. If \code{NULL},
the condition set is left to \code{\link[QCA:truthTable]{QCA::truthTable()}}. When
\code{calib = "recompute"}, \code{conditions} must be supplied explicitly.}

\item{cases}{Cases to test. Use \code{NULL} to test all rows, a numeric vector of
row indices, or a character vector of case labels.}

\item{case_labels}{Optional character vector of case labels with length
\code{nrow(data)}. If \code{NULL}, the function uses \code{rownames(data)} when
available; otherwise it uses row numbers converted to character.}

\item{calib}{Calibration handling for reduced-data runs. \code{"fixed"} uses the
calibrated values already present in \code{data}. \code{"recompute"} recalibrates
the outcome and conditions after deleting each case, using \code{raw.data} and
\code{calib_spec}.}

\item{raw.data}{Raw-data frame object used when \code{calib = "recompute"}.
It must have the same number of rows as \code{data}.}

\item{calib_spec}{Calibration specification used when
\code{calib = "recompute"}. This must be a named list keyed by the analysis
sets \code{c(outcome, conditions)}. Each entry must describe the raw source
column, the set type, and the calibration inputs used to rebuild the
calibrated set. Use the same \code{calib_spec} structure described for
\code{\link[=calib.test]{calib.test()}}. An entry may additionally contain \code{findTh}, a named list
of \code{\link[QCA:findTh]{QCA::findTh()}} arguments; when supplied, thresholds are re-estimated
after each case deletion instead of reusing the baseline \code{thresholds}.}

\item{incl.cut}{Inclusion cutoff passed to \code{\link[QCA:truthTable]{QCA::truthTable()}}. This must
be a single finite number in \verb{[0, 1]}.}

\item{n.cut}{Truth table frequency cutoff passed to \code{\link[QCA:truthTable]{QCA::truthTable()}}.
This must be an integer-like value of at least \code{1}.}

\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{dir.exp}{Directional expectations used when the monitored solution is
intermediate.}

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

\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 reduced truth table, \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{fit_measures}{Character vector of fit-measure names to compare between
the baseline run and each reduced-data run. Use \code{NULL} to disable fit
comparison.}

\item{fit_tol}{Non-negative tolerance used when deciding whether fit values
changed.}

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

\item{...}{Additional arguments. In \code{\link[=loo.test]{loo.test()}}, named arguments matching
\code{\link[QCA:truthTable]{QCA::truthTable()}} formals are passed to \code{\link[QCA:truthTable]{QCA::truthTable()}}, and the
remaining named arguments are forwarded to \code{\link[QCA:minimize]{QCA::minimize()}} after
removing names reserved by \code{loo.test()}. 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.loo_test]{print.loo_test()}}, \code{...} is passed to
\code{\link[=print.data.frame]{print.data.frame()}}. In \code{\link[=as.data.frame.loo_test]{as.data.frame.loo_test()}}, \code{...} is ignored.}

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

\item{row.names}{Logical; passed to \code{\link[=print.data.frame]{print.data.frame()}} by
\code{\link[=print.loo_test]{print.loo_test()}}.}
}
\value{
An object of class \code{loo_test} with the following components:
\describe{
\item{\code{diagnostics}}{A detailed data frame with one row per tested case.
It records the tested row index, case label, run status, solution-change
classification, fit-change classification, the number of changed fit
measures, the largest absolute fit change, and error information when a
reduced-data run fails.}
\item{\code{results}}{A compact data frame with the columns
\code{row_index}, \code{case_label}, \code{status}, \code{solution_change},
\code{fit_changed_types}, \code{n_fit_deltas}, and \code{max_abs_fit_delta}.}
\item{\code{baseline}}{A list containing the baseline analysis built from the
full dataset, including the truth table, minimization output, selected
solution terms used for comparison, fit values, exclusion information,
and calibration information.}
\item{\code{by_case}}{A named list with one entry per tested case. Each entry
stores the baseline run, the reduced-data run when available, change
summaries, fit deltas, exclusion information, calibration information,
and any error information for that case.}
\item{\code{settings}}{A list containing the analysis settings used to build
the result object.}
}

\code{print.loo_test()} prints a concise summary and the \code{results} table.
\code{as.data.frame.loo_test()} returns the \code{results} table.
}
\description{
Deletes one case at a time, reruns the QCA analysis, and records whether the
monitored solution changes, if selected fit measures change, and
whether the reduced-data run ends with an error. The tested cases can be
identified by row index or by case label.
}
\details{
The function first runs the baseline analysis on the full dataset, then
removes one selected case at a time and reruns the analysis on the reduced
data.

When \code{calib = "fixed"}, the reduced-data runs use the calibrated values
already present in \code{data}. When \code{calib = "recompute"}, the function rebuilds
the calibrated outcome and condition columns after each case deletion
using \code{\link[QCA:findTh]{QCA::findTh()}} and \code{\link[QCA:calibrate]{QCA::calibrate()}} as specified in \code{calib_spec}.

For each tested case, the function records whether the monitored solution
changed, whether monitored fit measures changed beyond \code{fit_tol}, and
whether the reduced-data run stopped with a calibration, truth table,
exclusion, or minimization error.

The shared solution-control, exclusion, and returned-object conventions are
described in \code{?qcaERT_conventions}.
}
\examples{
\donttest{
library(QCA)
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)

out <- loo.test(
  data = dat,
  outcome = outcome,
  conditions = conditions,
  cases = seq_len(nrow(dat)),
  case_labels = rownames(dat),
  calib = "fixed",
  incl.cut = 0.8,
  n.cut = 1,
  solution = "all",
  dir.exp = dir_exp,
  fit_measures = c("inclS", "PRI", "covS"),
  progress = TRUE
)

out
as.data.frame(out)
}

}
\seealso{
\code{\link[=calib.test]{calib.test()}}, \code{\link[=incl.test]{incl.test()}}, \code{\link[=ncut.test]{ncut.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()}}
}
