qcaERT provides enhanced robustness tests for Qualitative Comparative Analysis (QCA). It is designed for the moment after calibration, truth table construction, and minimization, when the question becomes:
How much does this QCA solution depend on my thresholds, cutoffs, cases, sample, or grouping structure?
The package is built as a family of sibling tools. They share common argument conventions, returned-object structure, print behavior, and data-frame coercion.
The strongest coverage is the QCA package workflow built
around QCA::calibrate(), QCA::truthTable(),
QCA::minimize(), and QCA::findRows(),
especially crisp-set and fuzzy-set analyses. qcaERT is not intended to
cover every possible QCA variant.
| Concern | Use |
|---|---|
| Calibration thresholds may be fragile | calib.test() |
| Inclusion cutoff may be fragile | incl.test() |
| Frequency cutoff may be fragile | ncut.test() |
| Individual cases may drive the solution | loo.test() |
| Sample composition may matter | subsample.test() |
| Several analytic choices may vary together | altset.test() |
| Theoretical condition-set specifications should be compared | theory.test() |
| Results may differ across groups or clusters | cluster.test() |
| QCA minimization output needs a clean table or chart | sol.df(), sol.chart() |
Inside R, the same map is available with:
?qcaERT_testsqcaERT is currently in development. From a local source checkout, install it with:
install.packages("devtools")
devtools::install()Then load it normally:
library(qcaERT)
library(QCA)Plotting is optional and uses ggplot2:
install.packages("ggplot2")This example uses the LR data from the QCA
package.
library(QCA)
library(qcaERT)
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)Run a regular QCA analysis:
tt <- truthTable(
dat,
outcome = outcome,
conditions = conditions,
incl.cut = 0.8,
n.cut = 1,
complete = TRUE,
show.cases = TRUE
)
sol <- minimize(tt, include = "", details = TRUE, show.cases = FALSE)
solution_table <- sol.df(conservative = sol, solution = "conservative")
solution_table
sol.chart(solution_table)Then check robustness.
incl_out <- incl.test(
data = dat,
outcome = outcome,
conditions = conditions,
incl.cut = 0.8,
step = 0.05,
max_steps = 4,
n.cut = 1,
solution = "all",
dir.exp = dir_exp,
progress = TRUE
)
incl_out
as.data.frame(incl_out)
incl_out$diagnosticsCompare theoretically motivated condition sets under the same analytic settings:
theories <- list(
development = c("DEV", "URB", "LIT"),
industrial = c("DEV", "URB", "IND"),
broad = c("DEV", "URB", "LIT", "IND", "STB")
)
dir_exp_theories <- list(
development = c("1", "1", "1"),
industrial = c("1", "1", "1"),
broad = c("1", "1", "1", "1", "1")
)
theory_out <- theory.test(
data = dat,
outcome = outcome,
theories = theories,
incl.cut = 0.8,
n.cut = 1,
solution = "all",
dir.exp = dir_exp_theories,
progress = TRUE
)
theory_out
as.data.frame(theory_out)
theory_out$results$solutions
theory_out$results$pairwiseFor calibration robustness, define calib_spec and let
qcaERT compute scale-aware perturbation steps. calib_spec
records the raw column, calibration type, method, thresholds, and any
extra QCA::calibrate() arguments in one place.
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
)
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$boundsHere, conditions defines the full QCA model, while
test.conditions selects which calibrated conditions are
perturbed. If test.conditions is omitted, all model
conditions are tested.
To test the outcome calibration, keep the outcome out of
conditions and ask for it explicitly:
calib_outcome <- 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
)Most qcaERT robustness functions return an S3 object with:
diagnostics: detailed/internal resultsresults: clean resultssettings: the settings used to run the analysisbaseline,
bounds, by_direction, by_case,
by_run, by_draw, or summaryUse:
print(incl_out)
as.data.frame(incl_out)
incl_out$diagnosticsFor incl.test(), ncut.test(), and
calib.test(), result_shape controls the layout
of the clean table when solution = "all". The default
"wide" layout keeps one row per tested path with
solution-type-specific columns; "long" returns one row per
tested path and solution type, with a solution_type
column.
cluster.test() and theory.test() are the
deliberate structured-result exceptions. cluster.test()
contains three tables:
overviewclustersunitsFor cluster_test objects, as.data.frame()
returns results$overview.
theory.test() contains:
modelssolutionspairwiseFor theory_test objects, as.data.frame()
returns results$models.
If ggplot2 is installed, calib.test(),
incl.test(), and theory.test() results can be
plotted directly.
plot(incl_out, solution_type = "conservative")
plot(incl_out, solution_type = "conservative", type = "trace", direction = "lower")
plot(calib_out, solution_type = "conservative")
plot(calib_out, solution_type = "conservative", type = "heatmap")
plot(calib_out, solution_type = "conservative", type = "trace", set = "DEV", anchor = "E1", direction = "lower")
plot(theory_out, solution_type = "conservative")For direct six-threshold fuzzy calibration, anchors are
E1, C1, I1, I2,
C2, and E2. For indirect calibration, anchors
are T1, T2, and so on.
See:
?qcaERT_plots?qcaERT
?qcaERT_tests
?qcaERT_conventions
?qcaERT_plots
vignette("qcaERT-overview", package = "qcaERT")
vignette("qcaERT-result-objects", package = "qcaERT")
vignette("qcaERT-calibration", package = "qcaERT")
news(package = "qcaERT")
citation(package = "qcaERT")qcaERT is release-ready.