knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) options(width = 100)
ThSQCA runs a crisp-set QCA analysis many times, once for each threshold setting you specify, and collects the solutions in one table. This section gets you from zero to a first result. Later sections explain each step.
The examples use small simulated data sets, so that every number in this tutorial can be reproduced exactly. The first one mimics a marketing survey: three evaluation scores (0 to 10) and a loyalty score. The data-generating code is shown so that you can see how the structure was built.
make_demo_data <- function(n = 400, seed = 2026) { set.seed(seed) clip <- function(x) pmin(10, pmax(0, round(x))) QUA <- clip(rnorm(n, 6, 2)) # quality evaluation SER <- clip(rnorm(n, 6, 2)) # service evaluation ENV <- clip(rnorm(n, 6, 2)) # store environment evaluation # Loyalty is high when quality and service are both high, or (a little # less strongly) when quality and environment are both high. core <- pmax(pmin(QUA, SER), pmin(QUA, ENV) - 1) LOY <- clip(core + rnorm(n, 0, 0.7)) data.frame(LOY, QUA, SER, ENV) } demo <- make_demo_data() head(demo)
Now run a sweep of the outcome threshold. We treat a case as a member of
the outcome set when LOY >= t, and we let t take the values 5, 6, 7 and 8.
The condition thresholds are held fixed at 7.
library(ThSQCA) res <- otSweep( dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_range = 5:8, # outcome thresholds to try thrX = c(QUA = 7, SER = 7, ENV = 7) # fixed condition thresholds )
summary(res)
Read the table from top to bottom. Each row is one complete QCA analysis:
LOY >= 5 and LOY >= 6, two configurations are sufficient for the
outcome: high quality together with high service (QUA*SER), and high
quality together with a high store environment score (QUA*ENV).LOY >= 7, only QUA*SER remains.LOY >= 8, no configuration reaches the consistency cutoff (0.8), so
the analysis reports "No solution".That is the whole idea: the sufficiency structure you report can depend on the target level of the outcome. A single-threshold analysis would show only one of these rows. Reporting all four rows shows where the structure is stable and where it changes.
The remaining sections show how to prepare your data, how to sweep the other thresholds, how to choose a solution type, how to read the output, and how to report the results.
In crisp-set QCA, the outcome and each condition must be turned into 0/1 membership using a threshold. Threshold-Sweep QCA (ThS-QCA) treats these thresholds as an explicit analytical dimension instead of a fixed preprocessing input. It records the sufficiency solution obtained at each threshold setting.
ThSQCA implements four sweeps:
| Sweep | What varies | What stays fixed | Function |
|-------|-------------|------------------|----------|
| CTS (single) | One condition threshold | Outcome threshold and the other conditions | ctSweepS() |
| CTS (multiple) | Several condition thresholds (a grid) | Outcome threshold | ctSweepM() |
| OTS | Outcome threshold | All condition thresholds | otSweep() |
| DTS | Outcome threshold and condition thresholds | Nothing | dtSweep() |
What ThSQCA does not do. It does not reimplement QCA. For every threshold setting, it binarizes the data and then calls
QCA::truthTable()andQCA::minimize(). ThSQCA only loops over the settings, collects the results, and reports them. Section "Reporting your results" shows how to check any single cell directly against the QCA package.Scope. The sweeps address sufficiency: which combinations of conditions are sufficient for the outcome at each threshold setting.
Tip: The Sweep Builder web tool generates ready-to-run ThSQCA code from your variable names and thresholds.
ThS-QCA works on raw scores and binarizes them itself, so no calibration step is needed before a sweep.
x >= threshold). Thresholds can be any real number.If a condition is already binary (for example an indicator for a customer segment), do not sweep it. Give it the threshold 1, which leaves 0 as 0 and 1 as 1. Any larger threshold would turn every value into 0 and destroy the variable.
# X1 is binary (0/1); X2 and X3 are 0-10 scores. res_mixed <- ctSweepM( dat = dat, outcome = "Y", conditions = c("X1", "X2", "X3"), sweep_list = list(X1 = 1, # binary: fixed threshold 1, not swept X2 = 6:8, # scores: swept X3 = 6:8), thrY = 7 )
This explores 1 x 3 x 3 = 9 threshold combinations. A quick way to spot binary variables before you set up a sweep:
sapply(dat[, c("X1", "X2", "X3")], function(x) all(x %in% c(0, 1)))
pre_calibrated)Sometimes a condition has a theoretically grounded fuzzy calibration that you
do not want to sweep. List it in pre_calibrated. Such a variable is passed to
QCA::truthTable() as it is, without binarization, and needs no entry in
thrX. The other conditions are still binarized at their thresholds.
d <- demo d$ENV_fz <- pmin(1, pmax(0, (d$ENV - 2.5) / 6)) # example calibration (0 to 1) res_mix <- otSweep( dat = d, outcome = "LOY", conditions = c("QUA", "SER", "ENV_fz"), sweep_range = 5:8, thrX = c(QUA = 7, SER = 7), # no entry for ENV_fz pre_calibrated = "ENV_fz" )
Points to remember:
pre_calibrated is never swept. To sweep a variable
that is already on a 0 to 1 membership scale, leave it out of
pre_calibrated; see the next subsection.Membership scores can be swept like any other numeric variable. Leave them out
of pre_calibrated and give thresholds on the 0 to 1 scale. A case is then a
member when its membership score is at least the threshold, so the sweep asks
how the sufficiency structure changes as the membership criterion becomes
stricter (for example 0.4, 0.6, 0.8). This applies to the conditions and to the
outcome alike.
fz <- function(x) pmin(1, pmax(0, (x - 2.5) / 6)) # example calibration (0 to 1) d_fz <- data.frame(LOY_fz = fz(demo$LOY), QUA_fz = fz(demo$QUA), SER_fz = fz(demo$SER), ENV_fz = fz(demo$ENV)) res_fz <- otSweep( dat = d_fz, outcome = "LOY_fz", conditions = c("QUA_fz", "SER_fz", "ENV_fz"), sweep_range = c(0.4, 0.6, 0.8), # outcome membership criteria thrX = c(QUA_fz = 0.6, SER_fz = 0.6, ENV_fz = 0.6) )
Which approach to use is a design decision, and both are defensible:
pre_calibrated, the fuzzy scores stay as they are and QCA works with
them directly. The calibration is fixed and is not part of the sensitivity
analysis.>=). At a threshold of 0.5 this includes cases at the crossover point, where
membership is most ambiguous. Check how many cases sit exactly at the
thresholds you use.Each subsection follows the same pattern: when to use the sweep, a minimal call, and how to read the result. All calls use the demo data from the quick start. The compute chunks hide the console output so that only the summary table is shown.
otSweep)Use it when the outcome is measured on a graded scale and you want to know
how the solution changes as the target level of the outcome rises. The
quick start already showed this sweep. The call is repeated here with the two
optional arguments you will use most often, incl.cut and n.cut.
res_ots <- otSweep( dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_range = 5:8, thrX = c(QUA = 7, SER = 7, ENV = 7), incl.cut = 0.8, # consistency cutoff for the truth table n.cut = 1 # minimum number of cases per configuration )
summary(res_ots)
How to read it. The solution changes between LOY >= 6 and LOY >= 7
(two configurations become one), and it disappears at LOY >= 8. In the
vocabulary of the method, the sweep has a threshold transition between 6 and
7, and a further one between 7 and 8.
By default otSweep() returns the complex solution (include = ""). In this
data set all eight combinations of the three conditions are observed, so the
complex, parsimonious and intermediate solutions coincide. The choice of
solution type matters when some combinations are unobserved; the section
"Choosing a solution type" below uses a second data set for that.
ctSweepS)Use it when you want to know how sensitive the solution is to a single calibration decision, such as "what counts as a high service evaluation".
res_cts <- ctSweepS( dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_var = "SER", # the condition whose threshold is swept sweep_range = 5:9, # candidate thresholds for SER thrY = 7, # fixed outcome threshold (LOY >= 7) thrX_default = 7 # fixed threshold for the other conditions )
summary(res_cts)
How to read it.
SER >= 5 or SER >= 6), no
configuration reaches the consistency cutoff.SER >= 7, the solution is QUA*SER, and its consistency stays close
to 0.9.covS) falls as the criterion tightens (0.656, 0.333, 0.122),
because fewer cases satisfy the condition.SER >= 9 the solution adds ~ENV. Only a small number of cases meet
QUA >= 7 and SER >= 9, as the next chunk shows, so this last row rests on
very few cases and should be read with caution.sum(demo$QUA >= 7 & demo$SER >= 9)
ctSweepM)Use it when you want to explore the joint space of several calibration decisions at once. The function evaluates every combination of the candidate thresholds you supply, so the number of cells grows quickly (here 3 x 3 x 3 = 27).
res_mcts <- ctSweepM( dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_list = list(QUA = 6:8, SER = 6:8, ENV = 6:8), # candidates per condition thrY = 7 # fixed outcome threshold )
summary(res_mcts)
How to read it. With 27 rows, look for regions rather than single rows.
QUA = 6, every cell reports "No solution".QUA = 7 and SER is 7 or 8, the solution is QUA*SER, whatever
the threshold for ENV. This is a stable region of the threshold space.~ENV or QUA*ENV appear only in cells with
QUA = 8, the strictest quality criterion in this grid.dtSweep)Use it when you want the fullest picture: the target level of the outcome and the criteria for the conditions vary at the same time. The result is a two-dimensional map, with one row per combination.
res_dts <- dtSweep( dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_list_X = list(QUA = 6:7, SER = 6:7, ENV = 6:7), # condition candidates sweep_range_Y = 6:8 # outcome candidates )
summary(res_dts)
How to read it. There are 24 cells (8 condition combinations by 3 outcome thresholds).
LOY >= 6, every one of the 8 condition combinations gives a solution.LOY >= 7, a solution exists only when QUA = 7 and SER = 7.LOY >= 8, there is no solution anywhere in this grid.In this data set, the higher the target level of the outcome, the stricter the
criteria for the conditions had to be before a configuration was consistent
enough. To look for solutions at LOY >= 8, you could extend the condition
thresholds upward (for example QUA and SER in 8:9).
QCA can minimize the truth table in three ways, which differ in how they treat
logical remainders: combinations of conditions that do not occur in the
data. All sweep functions accept the same two arguments as QCA::minimize().
| Solution type | include | dir.exp | Logical remainders |
|---------------|-----------|-----------|--------------------|
| Complex (default) | "" | NULL | Not used |
| Parsimonious | "?" | NULL | Any remainder may be used if it shortens the formula |
| Intermediate | "?" | c(1, 1, ...) | Only remainders consistent with your directional expectations |
dir.exp states, for each condition, whether its presence (1) or absence
(0) is expected to contribute to the outcome.
The first data set has no remainders, so the three types give the same answer.
To see the difference we need data in which some combinations are missing. The
second data set is again simulated. It describes 60 business-to-business
software accounts: RNW is renewal intention, and TRU (trust in the vendor),
PRC (price fairness) and SUP (support quality) are the conditions, all on
0 to 10 scales. The three conditions share a common factor, so that accounts
tend to be high on all of them or low on all of them, and some combinations do
not occur.
make_demo_data2 <- function(n = 60, seed = 89, rho = 0.85) { set.seed(seed) clip <- function(x) pmin(10, pmax(0, round(x))) L <- rnorm(n) # common factor mk <- function() clip(6 + 2 * (rho * L + sqrt(1 - rho^2) * rnorm(n))) TRU <- mk(); PRC <- mk(); SUP <- mk() # Renewal is high when trust is high and either price or support is high. RNW <- clip(pmin(TRU, pmax(PRC, SUP)) + rnorm(n, 0, 0.7)) data.frame(RNW, TRU, PRC, SUP) } demo2 <- make_demo_data2() head(demo2)
Take the target RNW >= 5 and the criterion 7 or more for every condition.
The truth table, built directly with the QCA package, shows which of the eight
combinations occur.
library(QCA) bin2 <- data.frame( RNW = as.integer(demo2$RNW >= 5), TRU = as.integer(demo2$TRU >= 7), PRC = as.integer(demo2$PRC >= 7), SUP = as.integer(demo2$SUP >= 7) ) tt2 <- truthTable(bin2, outcome = "RNW", conditions = c("TRU", "PRC", "SUP"), incl.cut = 0.8, n.cut = 1, show.cases = FALSE) tt2
Six rows are listed. Rows 3 (TRU PRC SUP = 0 1 0) and 5 (1 0 0) are missing
because no account has that combination: they are the logical remainders.
Five of the observed rows meet the consistency cutoff (OUT = 1), and only the
combination low on all three conditions (row 1) does not.
Run the same OTS sweep three times, changing only include and dir.exp.
thr2 <- c(TRU = 7, PRC = 7, SUP = 7) cond2 <- c("TRU", "PRC", "SUP") run2 <- function(...) { otSweep(dat = demo2, outcome = "RNW", conditions = cond2, sweep_range = 5:8, thrX = thr2, incl.cut = 0.8, n.cut = 1, ...) } res_cx <- run2() # complex res_ps <- run2(include = "?") # parsimonious res_im <- run2(include = "?", dir.exp = c(1, 1, 1)) # intermediate data.frame( thrY = 5:8, complex = res_cx$summary$expression, parsimonious = res_ps$summary$expression, intermediate = res_im$summary$expression )
(The chunk hides one warning, which the next section explains.)
How to read the comparison:
RNW >= 5 the complex solution is
SUP + TRU*PRC, while the parsimonious solution is TRU + SUP. The term
TRU*PRC was shortened to TRU. That step is justified only by the
assumption that the unobserved combination TRU present, PRC and SUP
absent (row 5) would also be sufficient for renewal. The parsimonious formula
is shorter because it makes this assumption, not because the data say more.dir.exp = c(1, 1, 1) a remainder
is used only if it extends an observed sufficient configuration by adding
conditions whose presence is expected to help. Neither remainder qualifies:
each contains a single condition, and no observed sufficient configuration is
contained in it. The intermediate solution therefore falls back on the
complex one. In other data sets the intermediate solution lies between the
other two.inclS 0.967 and covS 0.690 at
RNW >= 5), because the two remainders contain no cases. Only the formulas
differ, and with them the assumptions about cases that were not observed. You
can confirm this by printing summary() for each of the three results.RNW >= 7 upward the three types agree (TRU*SUP, then no
solution at 8).| Solution | When it fits | Strength | Caution |
|----------|--------------|----------|---------|
| Complex | Exploration; you want no assumptions about unobserved cases | Every claim rests on observed cases | Formulas can be long |
| Parsimonious | Checking which conditions survive maximal simplification | Shortest formulas | Relies on remainders that may be implausible |
| Intermediate | Theory-driven reporting | Uses only plausible remainders | Needs a justified dir.exp |
Ragin (2008) recommends the intermediate solution for reporting, together with
the parsimonious solution to show which conditions are core. Whichever you
choose, state the solution type, incl.cut, n.cut and dir.exp in your
methods section. Because the sweep repeats the analysis at every threshold,
the chosen type applies to all rows.
Look again at the parsimonious result at RNW >= 5: the table showed
TRU + SUP, but the n_solutions column of the sweep summary reads 2. The
shortening of TRU*PRC can go two ways: to TRU (assuming row 5 is
sufficient) or to PRC (assuming the other remainder, PRC alone, is
sufficient). Both choices fit the observed data equally well, so QCA returns
two equivalent models. Sweeps signal this with a warning and with the
n_solutions column.
extract_modeThe argument extract_mode controls what the sweep table shows.
| extract_mode | What the expression column contains |
|----------------|----------------------------------------|
| "first" (default) | Model M1 only. Check n_solutions to see whether others exist. |
| "all" | All models, for example M1: ...; M2: ... |
| "essential" | Terms common to all models, plus extra columns for the rest |
res_all <- run2(include = "?", extract_mode = "all") summary(res_all)
At RNW >= 5 the two models are TRU + SUP and PRC + SUP. The warning printed
above says that fit measures are those of M1. Here M2 has the same values
(inclS 0.967, covS 0.690), but in general you should check each model.
res_ess <- run2(include = "?", extract_mode = "essential") res_ess$summary[, c("thrY", "expression", "selective_terms", "unique_terms", "n_solutions")]
| Type | Definition | In this example (RNW >= 5) |
|------|------------|------------------------------|
| Essential prime implicants | Present in every model | SUP |
| Selective prime implicants | Present in some but not all models | TRU, PRC |
| Unique terms | Present in only one model | M1: TRU; M2: PRC |
A careful report says that SUP appears in every equivalent model, and that
the second term is TRU or PRC, depending on which unobserved combination is
assumed sufficient. Two cautions:
"essential" row the inclS and covS values are those of the full
model M1 (SUP + TRU), not of SUP alone. Alone, SUP has consistency
0.962 and coverage 0.595.For the details of every model, write the full report:
generate_report(res_all, "ots_multiple_report.md", dat = demo2, format = "full")
In the full report, the section for RNW >= 5 lists the number of solutions,
both models, the essential and selective terms, the unique terms and the raw
QCA output. Its per-term table and configuration chart follow M1, and a note
says so.
Each sweep returns an object with three parts:
names(res_ots)
summary: the table you saw above (one row per threshold setting).details: the full QCA results for every setting (truth table, solution,
fit measures). generate_report() reads this part.params: the settings that were used, for reproducibility.The columns of the summary table are:
| Column | Meaning |
|--------|---------|
| thrY, threshold, thrX | The threshold setting for that row |
| expression | The minimized solution (* is AND, + is OR, ~ is negation) |
| inclS | Solution consistency: how consistently the cases covered by the solution are also members of the outcome set |
| covS | Solution coverage: the share of the outcome set that the solution accounts for |
| n_solutions | Number of equivalent minimal solutions found |
Four points help avoid common misreadings.
incl.cut. Coverage says how much of the outcome set is
accounted for. It does not rank solutions; use it to describe how much of the
outcome the solution explains.expression across rows. Rows with the
same expression form a stable region. A change of expression between
adjacent rows marks a threshold transition. A solution that changes with
small threshold shifts is less robust.covS at LOY >= 5 and covS at LOY >= 7 are
proportions of different sets. A larger covS in a later row does not mean
that the solution has become "better". Likewise, covS is not the share of
customers or cases that a solution "captures" in the population.Changing a threshold changes which cases belong to the sets, so the sufficiency structure and its fit change with it. These tables describe that dependence. They do not show that manipulating a condition would change the outcome.
QCA usually analyses what is sufficient for the presence of the outcome. The absence of the outcome can be analysed too, for example the combinations sufficient for low loyalty or for non-renewal. Prefix the outcome with a tilde:
# Presence: cases with LOY >= threshold res_pos <- otSweep(dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_range = 4:6, thrX = c(QUA = 7, SER = 7, ENV = 7)) # Absence: cases with LOY < threshold res_neg <- otSweep(dat = demo, outcome = "~LOY", conditions = c("QUA", "SER", "ENV"), sweep_range = 4:6, thrX = c(QUA = 7, SER = 7, ENV = 7))
In the solution of a ~ analysis, ~QUA means that quality is below its
threshold. The formulas for LOY and ~LOY are not mirror images of each
other, so both need to be analysed and interpreted on their own.
All four sweep functions accept a ~ outcome. The stored settings record it:
res_neg$params$negate_outcome # TRUE res_neg$params$outcome # "~LOY"
A sweep yields many rows, so decide in advance which parts go into the main text and which into a supplement.
Report enough for a reader to reproduce every row.
incl.cut, n.cut, pri.cut, the solution type
(include) and any directional expectations (dir.exp).c(R = R.version.string, QCA = as.character(packageVersion("QCA")), ThSQCA = as.character(packageVersion("ThSQCA")))
generate_report() writes a Markdown file. The "simple" format suits a main
text or an appendix. The "full" format includes truth tables and fit measures
for every setting, essential and selective terms, and configuration charts, and
suits supplementary material.
generate_report(res_ots, "ots_report_simple.md", dat = demo, format = "simple") generate_report(res_ots, "ots_report_full.md", dat = demo, format = "full")
Solution formulas in the report use your own outcome name (for example
-> LOY). Optional arguments control what is included:
generate_report(res_ots, "r.md", dat = demo, include_chart = FALSE) # no charts generate_report(res_ots, "r.md", dat = demo, chart_symbol_set = "latex") # LaTeX symbols generate_report(res_ots, "r.md", dat = demo, include_raw_output = FALSE) # omit QCA output
The report also ends with a short snippet of QCA code that reproduces a cell directly, which is the subject of the next subsection.
Reports contain Fiss-style configuration charts (conditions in rows, solution terms in columns). The chart functions can also be used on their own, starting from path strings:
paths <- c("A*B*~C", "A*D", "B*E") cat(config_chart_from_paths(paths))
Symbol sets are "unicode" (default), "ascii" for maximum compatibility, and
"latex" for PDF output ($\bullet$ for presence, $\otimes$ for absence).
cat(config_chart_from_paths(paths, symbol_set = "ascii"))
When a threshold has several equivalent solutions, a chart with one block per solution is available:
solutions <- list(c("A*B", "C*D"), c("A*B", "C*E")) cat(config_chart_multi_solutions(solutions))
Because ThSQCA calls the QCA package for every cell, any row can be
reproduced with QCA alone. The example reproduces the row LOY >= 7 of the
OTS sweep, in which all thresholds are 7.
bin <- function(x, t) as.integer(x >= t) d7 <- data.frame( LOY = bin(demo$LOY, 7), QUA = bin(demo$QUA, 7), SER = bin(demo$SER, 7), ENV = bin(demo$ENV, 7) ) tt <- truthTable(d7, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), incl.cut = 0.8, n.cut = 1, show.cases = FALSE) sol <- minimize(tt)
sol
The solution matches the LOY >= 7 row of the sweep (QUA*SER). Before you
publish, do this for at least the rows you discuss in the text, and compare the
formulas, the inclS and covS values and the number of models (M1, M2, ...).
For solution types other than complex, pass the same include and dir.exp
to minimize().
The following paragraph illustrates a careful description of the OTS result. It is written for the simulated data and should be adapted to your study.
We examined how the sufficient configurations for high loyalty change with the outcome threshold (LOY >= 5 to 8), holding the condition thresholds at 7 (consistency cutoff 0.8, frequency cutoff 1, complex solution). At LOY >= 5 and LOY >= 6, two configurations met the criterion: quality together with service, and quality together with store environment. At LOY >= 7, only the first remained. At LOY >= 8, no configuration met the consistency cutoff under these settings. These patterns describe how the sufficiency structure depends on the definition of the outcome; they are not evidence that improving any condition would raise loyalty.
Some habits make the wording safer:
Fiss (2011) refined QCA configuration tables by distinguishing two kinds of condition within a configuration (a term of the intermediate solution):
| Type | Definition | Symbol | |------|------------|--------| | Core condition | Also part of the parsimonious solution | large symbol | | Peripheral condition | Eliminated in the parsimonious solution, so it appears in the intermediate solution only | small symbol |
A condition that survives even the most aggressive simplification (the parsimonious solution) rests on stronger evidence than one that appears only once theoretical expectations are applied. The four-symbol set is:
● = core condition present ⊗ = core condition absent ⊙ = peripheral condition present ⊘ = peripheral condition absent (blank) = the condition does not matter
How ThSQCA applies the definition. In Fiss's own solution tables the
classification is made configuration by configuration: solutions are grouped
by their core conditions, and the same condition can be core in one
configuration and peripheral in another. compute_fiss_core() follows this.
For each term of the intermediate solution it looks for the parsimonious
terms contained in it (every condition of the parsimonious term has the same
status in the intermediate term). The conditions of those parsimonious terms
are core; the other conditions of the term are peripheral. With the
parsimonious solution ~A*E + A*B, for example, the intermediate term
~A*~B*C*E contains ~A*E, so ~A and E are core, while ~B and C
are peripheral even though B appears in the other parsimonious term.
Two situations are not covered by Fiss (2011). ThSQCA treats both conservatively and says so in a warning:
QCA::minimize() records which
parsimonious solution each intermediate solution was derived from. If the
reported intermediate solution comes from one of several tied parsimonious
solutions, only that one is used. If it comes from several of them, a
condition is core only when it is core relative to each. In the second
data set at RNW >= 5, the intermediate solution SUP + TRU*PRC is
derived both from TRU + SUP and from PRC + SUP; TRU would be core
relative to the first and PRC relative to the second, so both are
reported as peripheral and only SUP is core.If you want to report core and peripheral conditions relative to one particular parsimonious solution, state that choice in the text.
For LaTeX output the symbols are $\bullet$, $\otimes$, $\odot$ and
$\oslash$.
compute_fiss_core() works on results of otSweep() and ctSweepS()
(results of ctSweepM() and dtSweep() are not supported yet). It needs an
intermediate-solution sweep with the logical remainders enabled and the
details stored:
include = "?",dir.exp specified,return_details = TRUE (the default).res_i <- otSweep( dat = demo, outcome = "LOY", conditions = c("QUA", "SER", "ENV"), sweep_range = 6:8, thrX = c(QUA = 7, SER = 7, ENV = 7), include = "?", dir.exp = c(1, 1, 1) ) # For every threshold, compare each term of the stored intermediate solution # with the parsimonious term(s) contained in it. res_fiss <- compute_fiss_core(res_i, conditions = c("QUA", "SER", "ENV")) print_fiss_summary(res_fiss, thr_key = "7") # one threshold cat(generate_fiss_chart(res_fiss, symbol_set = "unicode")) cat(generate_fiss_chart(res_fiss, symbol_set = "latex")) generate_report(res_fiss, "fiss_report.md", dat = demo, format = "full", include_fiss_core = TRUE)
print_fiss_summary() lists, term by term, which conditions are core and which
are peripheral. When the parsimonious and intermediate solutions are identical,
every condition is core and none is peripheral. For the second data set at
RNW >= 6, the parsimonious solution is TRU + ~PRC*SUP and the intermediate
solution TRU*PRC + ~PRC*SUP: in TRU*PRC, TRU is core and PRC
peripheral; in ~PRC*SUP, both conditions are core.
Start small, then expand. Test a call with one threshold, then a short range, and only then the full grid. The number of QCA analyses grows quickly:
| Function | Number of analyses | Example |
|----------|--------------------|---------|
| otSweep() | one per outcome threshold | 5 thresholds: 5 |
| ctSweepS() | one per threshold of the swept condition | 5 thresholds: 5 |
| ctSweepM() | product of the candidate counts | 3 x 3 x 3: 27 |
| dtSweep() | outcome thresholds x condition grid | 3 x (3 x 3 x 3): 81 |
A grid with five conditions and five candidates each has 3,125 cells; reduce the number of swept conditions first.
Why do I see a solution at one threshold but "No solution" at the next? Consistency depends on how the cases are classified. Near a threshold where several cases change membership at once, a configuration can drop below the consistency cutoff. Check the truth table of that row in the full report.
A solution appears only at the edge of my range. Look at the number of cases
behind it (see the SER >= 9 example above). Solutions built on very few
cases are fragile.
The solution changes a lot across thresholds. What do I report? Report the whole sweep, and say where the structure is stable and where it changes. If you also give one headline result, choose its thresholds on substantive grounds before looking at the sweep, and state that the results are threshold-sensitive.
The sweep prints a warning about multiple solutions. Equivalent models exist
at one or more thresholds; see "Multiple equivalent solutions". Use
extract_mode = "all" or "essential" and generate_report().
Where to find help. Questions and bug reports are welcome at https://github.com/im-research-yt/ThSQCA/issues.
ThSQCA makes the threshold choices of a crisp-set QCA explicit. With the CTS, OTS and DTS sweeps you can see where a sufficiency structure is stable, where it changes, and where it disappears, and you can report this in a reproducible way. Because every cell is an ordinary QCA analysis, each result can be checked against the QCA package.
sessionInfo()
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.