TSQCA Tutorial (English)

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

Introduction

Threshold-Sweep QCA (TS-QCA) is a framework for systematically exploring how different threshold settings affect the results of crisp-set Qualitative Comparative Analysis (QCA).
In crisp-set QCA, the researcher must choose thresholds to binarize:

Small changes in these thresholds may lead to substantial differences in truth tables and minimized solutions.

TS-QCA provides:

The TSQCA package implements four sweep methods:

| Method | What varies | What stays fixed | Purpose | |--------|-------------|------------------|----------| | CTS–QCA | One X threshold | Y + other Xs | Evaluate influence of a single condition | | MCTS–QCA | Multiple X thresholds | Y | Explore combinations of X thresholds | | OTS–QCA | Y threshold | All Xs | Assess robustness to Y calibration | | DTS–QCA | X and Y thresholds | None | Full 2D sensitivity analysis |

Scope: This package focuses on sufficiency analysis—identifying condition combinations that are sufficient for an outcome. Necessity analysis (whether a condition is required for an outcome) involves different logical structures and evaluation metrics, and is planned for future versions.

Important (v1.2.0): Version 1.2.0 fixed a critical bug where intermediate solutions (using dir.exp) were incorrectly extracted. If you used intermediate solutions in previous versions, please re-run your analyses. Reports now also display the Solution Type and include optional QCA package output for verification.


Three Types of QCA Solutions (New in v1.1.0)

Overview

QCA minimization can produce three types of solutions depending on how logical remainders (unobserved configurations) are handled:

| Solution Type | include | dir.exp | Logical Remainders | Description | |--------------|-----------|-----------|-------------------|-------------| | Complex (default) | "" | NULL | Not used | Most conservative; only observed configurations | | Parsimonious | "?" | NULL | All used | Most simplified; uses all remainders | | Intermediate | "?" | c(1,1,...) | Theory-guided | Uses remainders consistent with theory |

QCA-Compatible Defaults (v1.1.0)

As of v1.1.0, TSQCA uses the same defaults as QCA::minimize():

This produces the complex (conservative) solution by default.

Example: Computing All Three Solutions

library(TSQCA)
data(sample_data)
thrX <- c(X1 = 7, X2 = 7, X3 = 7)

# 1. Complex Solution (default, most conservative)
result_comp <- otSweep(
  dat = sample_data,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 7,
  thrX = thrX
  # include = "" (default), dir.exp = NULL (default)
)
cat("Complex Solution:", result_comp$summary$expression, "\n")

# 2. Parsimonious Solution (uses all logical remainders)
result_pars <- otSweep(
  dat = sample_data,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 7,
  thrX = thrX,
  include = "?"  # Include logical remainders
)
cat("Parsimonious Solution:", result_pars$summary$expression, "\n")

# 3. Intermediate Solution (theory-guided remainders)
result_int <- otSweep(
  dat = sample_data,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 7,
  thrX = thrX,
  include = "?",
  dir.exp = c(1, 1, 1)  # All conditions expected to contribute positively
)
cat("Intermediate Solution:", result_int$summary$expression, "\n")

When to Use Each Solution

| Solution | When to Use | Pros | Cons | |----------|-------------|------|------| | Complex | Exploratory analysis, maximum caution | No assumptions about unobserved cases | Often too complex for interpretation | | Parsimonious | Robustness checks, simplicity | Most simplified form | May rely on implausible assumptions | | Intermediate | Theory-driven analysis, publications | Balances parsimony and theory | Requires theoretical justification |

Note: The intermediate solution is most commonly reported in QCA publications (Ragin, 2008; Fiss, 2011), as it incorporates theoretical knowledge while avoiding implausible simplifying assumptions.

Migration from v1.0.0

If you were using TSQCA v1.0.0, note that the default behavior has changed:

# v1.0.0 (intermediate solution by default due to bug)
result <- otSweep(dat, "Y", c("X1", "X2", "X3"), sweep_range = 7, thrX = thrX)

# v1.1.0: To get the same result as v1.0.0, explicitly specify:
result <- otSweep(
  dat = sample_data,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 7,
  thrX = thrX,
  include = "?",
  dir.exp = c(1, 1, 1)  # Explicit intermediate solution
)

Data Requirements

TS-QCA assumes:

Example dataset structure:

library(TSQCA)
data("sample_data")
dat <- sample_data
str(dat)

Define outcome and conditions:

outcome  <- "Y"
conditions <- c("X1", "X2", "X3")

Working with Mixed Data Types

Handling Binary and Continuous Variables

In real-world social science research, datasets often contain both binary variables (e.g., gender, yes/no responses) and continuous variables (e.g., sales, satisfaction scores). When using TSQCA with such mixed data, special attention is required.

Key Principles

  1. Do NOT sweep binary variables — they are already binarized (0/1)
  2. Use threshold = 1 for binary variables to preserve their original values
  3. Explicitly specify thresholds for each variable in sweep_list

Why Threshold = 1 for Binary Variables?

The internal qca_bin() function uses the rule x >= thr for binarization:

This ensures that binary variables remain unchanged during the binarization process.

Practical Example: Mixed Data

Suppose your dataset has:

When using ctSweepM():

# CORRECT: Specify threshold explicitly for each variable
sweep_list <- list(
  X1 = 1,      # Binary variable: use threshold 1
  X2 = 6:8,    # Continuous: sweep thresholds
  X3 = 6:8     # Continuous: sweep thresholds
)

# Using intermediate solution (most common in publications)
res_mixed <- ctSweepM(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_list     = sweep_list,
  thrY           = 7,
  include        = "?",           # Include logical remainders
  dir.exp        = c(1, 1, 1)     # Directional expectations (intermediate)
)

This explores 1 × 3 × 3 = 9 threshold combinations, treating X1 as a fixed binary condition while sweeping X2 and X3.

Common Mistake

# WRONG: Using sweep range for binary variables
sweep_list <- list(
  X1 = 6:8,    # All values become 0 (since 0 < 6 and 1 < 6)
  X2 = 6:8,
  X3 = 6:8
)

If you accidentally specify X1 = 6:8, both 0 and 1 will fail the >= 6 condition, making all X1 values become 0. This destroys the information in your binary variable.

Best Practice

Always examine your data structure before setting up threshold sweeps:

# Check variable ranges
summary(dat[, c("X1", "X2", "X3")])

# Identify binary variables (only 0 and 1)
sapply(dat[, c("X1", "X2", "X3")], function(x) {
  unique_vals <- sort(unique(x))
  if (length(unique_vals) == 2 && all(unique_vals == c(0, 1))) {
    "Binary (use threshold = 1)"
  } else {
    paste("Continuous (range:", min(x), "-", max(x), ")")
  }
})

Using Pre-Calibrated Fuzzy Conditions (pre_calibrated)

Version 1.3.0 introduces the pre_calibrated argument to all four sweep functions (otSweep, ctSweepS, ctSweepM, dtSweep). This argument enables a mixed crisp/fuzzy workflow: some conditions can be passed as fuzzy-set membership scores (values in [0, 1] produced by QCA::calibrate()), while other conditions are swept on their raw scale as usual.

How it works

Validation

The function raises an error if:

A warning is issued if the variable contains NA values.

Sweep variables vs. pre-calibrated variables

Sweep variables should be kept on their raw (original) scale. When you sweep a variable on its raw scale (e.g., Likert scores 6, 7, 8), each threshold carries direct substantive meaning. Pre-calibrating a sweep variable into fuzzy values and then sweeping over those values (e.g., 0.3, 0.5, 0.7) makes interpretation indirect and harder to justify.

If a variable appears in both pre_calibrated and a sweep list, TSQCA issues a warning and uses the pre-calibrated values, ignoring the sweep thresholds.

Recommended practice

| Variable type | Recommended approach | |---|---| | Conditions you want to sweep | Keep on raw scale; use qca_bin() via thrX | | Conditions with theoretically grounded fuzzy calibration | Pre-calibrate and list in pre_calibrated | | Binary conditions (0/1) | Use thrX = 1; optionally list in pre_calibrated |

A worked example with sample data will be added in a future release.


CTS–QCA: Single-Condition Sweep (ctSweepS)

CTS–QCA varies the threshold for one X condition, keeping the others fixed.

Default: Complex Solution

sweep_var   <- "X3"   # Condition (X) whose threshold is swept
sweep_range <- 6:9    # Candidate threshold values to evaluate

thrY         <- 7     # Outcome (Y) threshold (fixed)
thrX_default <- 7     # Threshold for other X conditions (fixed)

# Default: Complex solution (include = "", dir.exp = NULL)
res_cts <- ctSweepS(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_var      = sweep_var,
  sweep_range    = sweep_range,
  thrY           = thrY,
  thrX_default   = thrX_default,
  return_details = TRUE
)

summary(res_cts)

Intermediate Solution (Theory-Driven)

# Intermediate solution: specify include = "?" and dir.exp
res_cts_int <- ctSweepS(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_var      = sweep_var,
  sweep_range    = sweep_range,
  thrY           = thrY,
  thrX_default   = thrX_default,
  include        = "?",           # Include logical remainders
  dir.exp        = c(1, 1, 1),    # Directional expectations
  return_details = TRUE
)

summary(res_cts_int)

MCTS–QCA: Multiple X Sweep (ctSweepM)

MCTS–QCA evaluates all combinations of thresholds for multiple X conditions.

Default: Complex Solution

# Create a sweep list specifying thresholds for each condition
sweep_list <- list(
  X1 = 6:7,
  X2 = 6:7,
  X3 = 6:7
)

# Default: Complex solution
res_mcts <- ctSweepM(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_list     = sweep_list,
  thrY           = 7,
  return_details = TRUE
)

summary(res_mcts)

Intermediate Solution (Theory-Driven)

# Intermediate solution: specify include = "?" and dir.exp
res_mcts_int <- ctSweepM(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_list     = sweep_list,
  thrY           = 7,
  include        = "?",
  dir.exp        = c(1, 1, 1),
  return_details = TRUE
)

summary(res_mcts_int)

OTS–QCA: Outcome Sweep (otSweep)

OTS–QCA varies only the threshold of Y, keeping X thresholds fixed.

Default: Complex Solution

# Default: Complex solution
res_ots <- otSweep(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_range    = 6:8,
  thrX           = c(X1 = 7, X2 = 7, X3 = 7),
  return_details = TRUE
)

summary(res_ots)

Intermediate Solution (Theory-Driven)

# Intermediate solution: specify include = "?" and dir.exp
res_ots_int <- otSweep(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_range    = 6:8,
  thrX           = c(X1 = 7, X2 = 7, X3 = 7),
  include        = "?",
  dir.exp        = c(1, 1, 1),
  return_details = TRUE
)

summary(res_ots_int)

DTS–QCA: Two-Dimensional Sweep (dtSweep)

DTS–QCA varies both X thresholds and Y thresholds, creating a full 2D grid.

Default: Complex Solution

sweep_list_dts_X <- list(
  X1 = 6:7,
  X2 = 6:7,
  X3 = 6:7
)

sweep_range_dts_Y <- 6:7

# Default: Complex solution
res_dts <- dtSweep(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_list_X   = sweep_list_dts_X,
  sweep_range_Y  = sweep_range_dts_Y,
  return_details = TRUE
)

summary(res_dts)

Intermediate Solution (Theory-Driven)

# Intermediate solution: specify include = "?" and dir.exp
res_dts_int <- dtSweep(
  dat            = dat,
  outcome        = "Y",
  conditions     = c("X1", "X2", "X3"),
  sweep_list_X   = sweep_list_dts_X,
  sweep_range_Y  = sweep_range_dts_Y,
  include        = "?",
  dir.exp        = c(1, 1, 1),
  return_details = TRUE
)

summary(res_dts_int)

Understanding the Output

Each sweep result contains:

General guidance:


Handling Multiple Solutions (New in v0.2.0)

Why Multiple Solutions Matter

When QCA minimization produces multiple equivalent intermediate solutions, researchers face a methodological challenge: which solution should be reported? Traditional approaches often report only the first solution (M1), but this may miss important causal heterogeneity.

TSQCA v0.2.0 addresses this by:

  1. Detecting when multiple solutions exist
  2. Extracting essential prime implicants (terms common to all solutions)
  3. Identifying selective prime implicants and unique terms

Essential vs. Selective Prime Implicants

Understanding the distinction between essential and selective prime implicants is essential for robust QCA interpretation:

| Type | Definition | Interpretation | |------|------------|----------------| | Essential prime implicants | Present in ALL solutions | Robust findings; essential causal factors | | Selective prime implicants | Present in SOME but not all solutions | Context-dependent; may vary across cases | | Unique terms | Present in only ONE specific solution | Solution-specific; least robust |

Using extract_mode

The extract_mode parameter controls how solutions are extracted:

Mode: "first" (Default)

# Returns only the first solution (M1)
# Backward compatible with v0.1.x
result <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = c(X1 = 7, X2 = 7, X3 = 7),
  extract_mode = "first"  # Default
)

Mode: "all"

# Returns all solutions concatenated
# Useful for seeing all equivalent solutions
result_all <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = c(X1 = 7, X2 = 7, X3 = 7),
  extract_mode = "all"
)

# Output includes n_solutions column
head(result_all$summary)
# expression column shows: "M1: A*B + C; M2: A*B + D; M3: ..."

Mode: "essential"

# Returns essential prime implicants (terms common to all solutions)
# Best for identifying robust findings
result_essential <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = c(X1 = 7, X2 = 7, X3 = 7),
  extract_mode = "essential"
)

# Output includes:
# - expression: essential prime implicants
# - selective_terms: terms in some but not all solutions
# - unique_terms: solution-specific terms
# - n_solutions: number of equivalent solutions

Practical Example

Consider a scenario where QCA produces three equivalent solutions:

The analysis reveals:

| Component | Terms | Interpretation | |-----------|-------|----------------| | Essential (EPI) | A*B | Present in all three solutions; robust finding | | Selective (SPI) | C, D, E | Each appears in one solution; context-dependent | | Unique (M1) | C | Only in solution 1 | | Unique (M2) | D | Only in solution 2 | | Unique (M3) | E | Only in solution 3 |

Recommendation: Report the essential prime implicants (A*B → Y) as your main finding, and discuss the selective prime implicants as alternative pathways in your discussion section.


Generating Reports (New in v0.2.0)

Overview

The generate_report() function creates comprehensive markdown reports from your analysis results. This automates the documentation process and ensures reproducibility.

Report Formats

Full Report

Contains comprehensive analysis details:

# Generate full report
generate_report(res_ots, "my_analysis_full.md", dat = dat, format = "full")

Simple Report

Designed for journal manuscripts:

# Generate simple report
generate_report(res_ots, "my_analysis_simple.md", dat = dat, format = "simple")

Using Reports Effectively

For Research Papers

  1. Run analysis with return_details = TRUE (default in v0.2.0)
  2. Generate a simple report for the main manuscript
  3. Generate a full report for supplementary materials
# Complete workflow
result <- otSweep(
  dat = mydata,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = c(X1 = 7, X2 = 7, X3 = 7)
)

# For main text
generate_report(result, "manuscript_results.md", dat = mydata, format = "simple")

# For supplementary materials
generate_report(result, "supplementary_full.md", dat = mydata, format = "full")

Accessing Analysis Parameters

All analysis parameters are stored in result$params for reproducibility:

# View stored parameters
result$params

# Includes:
# - outcome, conditions: variable names
# - thrX, thrY: threshold values
# - incl.cut, n.cut, pri.cut: QCA parameters
# - dir.exp, include: minimization settings

Best Practices

Start Small, Then Expand

When using sweep functions, the number of QCA analyses grows quickly. A systematic approach prevents wasted computation:

Step 1: Single Value Test

# Test with a single threshold first
result <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 7,  # Single value
  thrX = c(X1 = 7, X2 = 7, X3 = 7)
)

Step 2: Small Range

# Expand to a small range
result <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:7,  # Small range
  thrX = c(X1 = 7, X2 = 7, X3 = 7)
)

Step 3: Full Analysis

# Run full analysis only after confirming it works
result <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 5:9,  # Full range
  thrX = c(X1 = 7, X2 = 7, X3 = 7)
)

Computational Complexity

Understanding computational complexity helps plan your analysis:

| Function | Complexity | Example | Analyses | |----------|------------|---------|----------| | otSweep() | O(n) | 5 Y thresholds | 5 | | ctSweepS() | O(n) | 5 X thresholds | 5 | | ctSweepM() | O(m^k) | 3 thresholds × 3 conditions | 27 | | dtSweep() | O(n × m^k) | 3 Y × 3^3 X | 81 |

Managing Large Sweeps

For dtSweep() and ctSweepM(), reduce conditions first:

# Manageable: 2 × 2 × 2 = 8 combinations
sweep_list <- list(X1 = 6:7, X2 = 6:7, X3 = 6:7)

# Caution: 5 × 5 × 5 = 125 combinations
sweep_list <- list(X1 = 5:9, X2 = 5:9, X3 = 5:9)

# Avoid: 5 × 5 × 5 × 5 × 5 = 3125 combinations!
sweep_list <- list(X1 = 5:9, X2 = 5:9, X3 = 5:9, X4 = 5:9, X5 = 5:9)

Interpreting Results

When Solutions Are Stable

If the same solution appears across multiple thresholds, your findings are robust:

thrY | expression    | inclS | covS
-----|---------------|-------|------
6    | A*B + C → Y   | 0.85  | 0.72
7    | A*B + C → Y   | 0.88  | 0.68
8    | A*B + C → Y   | 0.91  | 0.65

Interpretation: The solution A*B + C is robust across threshold variations.

When Solutions Change

If solutions vary significantly, investigate the threshold sensitivity:

thrY | expression    | inclS | covS
-----|---------------|-------|------
6    | A*B + C → Y   | 0.82  | 0.75
7    | A*B → Y       | 0.89  | 0.62
8    | B*D → Y       | 0.93  | 0.45

Interpretation: Results are threshold-sensitive. Consider reporting the most theoretically justified threshold with appropriate caveats.


Negated Outcome Analysis (New in v0.3.0)

Why Analyze Negated Outcomes?

In QCA, researchers typically analyze conditions sufficient for the presence of an outcome (Y = 1). However, understanding conditions for the absence of an outcome (~Y) can provide equally valuable insights:

Using the ~ Notation

TSQCA v0.3.0 supports the QCA package's tilde (~) notation for negated outcomes:

# Analyze conditions for Y >= threshold (standard)
result_Y <- otSweep(
  dat = dat,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = c(X1 = 7, X2 = 7, X3 = 7)
)

# Analyze conditions for Y < threshold (negated)
result_negY <- otSweep(
  dat = dat,
  outcome = "~Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = c(X1 = 7, X2 = 7, X3 = 7)
)

Interpreting Negated Results

When using ~Y, the solution shows conditions sufficient for the absence of the outcome:

| Analysis | Solution Example | Interpretation | |----------|------------------|----------------| | outcome = "Y" | X1*X2 + X3 | High X1 AND X2, OR high X3 → High Y | | outcome = "~Y" | ~X1*~X3 + ~X2*~X3 | Low X1 AND X3, OR low X2 AND X3 → Low Y |

Note: Negated conditions (~X1) in the solution mean the absence of that condition (below threshold).

All Sweep Functions Support Negation

# ctSweepS with negated outcome
result <- ctSweepS(
  dat = dat,
  outcome = "~Y",
  conditions = c("X1", "X2", "X3"),
  sweep_var = "X3",
  sweep_range = 6:8,
  thrY = 7,
  thrX_default = 7
)

# ctSweepM with negated outcome
result <- ctSweepM(
  dat = dat,
  outcome = "~Y",
  conditions = c("X1", "X2"),
  sweep_list = list(X1 = 6:7, X2 = 6:7),
  thrY = 7
)

# dtSweep with negated outcome
result <- dtSweep(
  dat = dat,
  outcome = "~Y",
  conditions = c("X1", "X2"),
  sweep_list_X = list(X1 = 6:7, X2 = 7),
  sweep_range_Y = 6:8
)

Checking Negation in Results

The params object stores whether negation was used:

result <- otSweep(dat = dat, outcome = "~Y", ...)

# Check if negated
result$params$negate_outcome
# [1] TRUE

result$params$outcome
# [1] "~Y"

Configuration Charts (New in v0.5.0)

TSQCA can generate Fiss-style configuration charts (Table 5 format) commonly used in QCA publications.

Automatic Inclusion in Reports

Configuration charts are now automatically included in reports generated by generate_report():

# Generate report with configuration charts (default)
generate_report(result, "my_report.md", dat = dat, format = "full")

# Disable charts if needed
generate_report(result, "my_report.md", dat = dat, include_chart = FALSE)

# Use LaTeX symbols for academic papers
generate_report(result, "my_report.md", dat = dat, chart_symbol_set = "latex")

# Include QCA package raw output for verification (default is TRUE)
generate_report(result, "my_report.md", dat = dat, include_raw_output = TRUE)

# Disable raw output if not needed
generate_report(result, "my_report.md", dat = dat, include_raw_output = FALSE)

Note (v1.2.0): Reports now display the Solution Type (Complex/Parsimonious/Intermediate) in the Analysis Overview section, and optionally include raw QCA package output for verification purposes.

Standalone Chart Functions

You can also generate configuration charts directly:

# From path strings
paths <- c("A*B*~C", "A*D", "B*E")
chart <- config_chart_from_paths(paths)
cat(chart)

Symbol Sets

Three symbol sets are available:

# ASCII (maximum compatibility)
cat(config_chart_from_paths(paths, symbol_set = "ascii"))
# LaTeX (for PDF/academic papers)
cat(config_chart_from_paths(paths, symbol_set = "latex"))
# Output: $\bullet$ for presence, $\otimes$ for absence

Multiple Solutions

When you have multiple equivalent solutions:

solutions <- list(
  c("A*B", "C*D"),
  c("A*B", "C*E")
)
chart <- config_chart_multi_solutions(solutions)
cat(chart)

Conclusion

TSQCA provides a structured and reproducible way to evaluate
how threshold choices influence QCA results.

Using CTS, MCTS, OTS, and DTS sweeps, researchers can:

New in v1.1.0:

New in v0.5.0:

New in v0.3.0:

New in v0.2.0:


Verification for Academic Publications

For academic publications, we strongly recommend verifying TSQCA results directly with the QCA package before submission. This ensures accuracy and reproducibility.

Recommended Verification Workflow

library(QCA)
library(TSQCA)

# Step 1: Run TSQCA analysis
data(sample_data)
thrX <- c(X1 = 7, X2 = 7, X3 = 7)

result <- otSweep(
  dat = sample_data,
  outcome = "Y",
  conditions = c("X1", "X2", "X3"),
  sweep_range = 6:8,
  thrX = thrX,
  include = "?",
  dir.exp = c(1, 1, 1),
  return_details = TRUE
)

# Step 2: Generate report with QCA output for verification
generate_report(result, "my_analysis.md", dat = sample_data, 
                include_raw_output = TRUE)  # Default is TRUE

# Step 3: Verify key results directly with QCA package
# For each important threshold, run QCA::minimize() directly
dat_bin <- sample_data
dat_bin$Y_bin <- ifelse(sample_data$Y >= 7, 1, 0)
dat_bin$X1_bin <- ifelse(sample_data$X1 >= 7, 1, 0)
dat_bin$X2_bin <- ifelse(sample_data$X2 >= 7, 1, 0)
dat_bin$X3_bin <- ifelse(sample_data$X3 >= 7, 1, 0)

tt <- truthTable(dat_bin, outcome = "Y_bin", 
                 conditions = c("X1_bin", "X2_bin", "X3_bin"),
                 incl.cut = 0.8)
sol <- minimize(tt, include = "?", dir.exp = c(1, 1, 1))
print(sol)  # Compare with TSQCA report

Why Verification Matters

  1. Accuracy: Confirms TSQCA extraction matches QCA package output
  2. Reproducibility: Provides independent verification for reviewers
  3. Transparency: Documents the analysis pipeline clearly

What to Check

Tip: The "QCA Package Output" section in TSQCA reports (enabled by include_raw_output = TRUE) shows the direct print(sol) output for easy comparison.

References

For more information on TS-QCA methodology, see:

Session Info

sessionInfo()


Try the TSQCA package in your browser

Any scripts or data that you put into this service are public.

TSQCA documentation built on Feb. 18, 2026, 5:06 p.m.