TSQCA Tutorial (English)

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.


Data Requirements

TS-QCA assumes:

Example dataset structure:

library(TSQCA)
data("sample_data")
dat <- sample_data
str(dat)
#> 'data.frame':    80 obs. of  4 variables:
#>  $ Y : int  8 4 5 7 2 2 7 5 3 8 ...
#>  $ X1: int  7 2 6 8 8 9 8 8 1 5 ...
#>  $ X2: int  7 5 8 4 0 5 8 4 4 2 ...
#>  $ X3: int  1 6 6 5 3 4 5 3 6 8 ...

Define outcome and conditions:

Yvar  <- "Y"
Xvars <- 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:

  • If x = 0: 0 >= 1 → FALSE → 0 (preserved)
  • If x = 1: 1 >= 1 → TRUE → 1 (preserved)

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

Practical Example: Mixed Data

Suppose your dataset has:

  • X1: Gender (0 = Male, 1 = Female) — binary variable
  • X2: Product Quality Score (0-10) — continuous variable
  • X3: Store Atmosphere Score (0-10) — continuous variable

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
)

res_mixed <- ctSweepM(
  dat            = dat,
  Yvar           = "Y",
  Xvars          = c("X1", "X2", "X3"),
  sweep_list     = sweep_list,
  thrY           = 7,
  dir.exp        = 1
)

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), ")")
  }
})

CTS–QCA: Single-Condition Sweep (ctSweepS)

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

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)

res_cts <- ctSweepS(
  dat            = dat,
  Yvar           = "Y",
  Xvars          = c("X1", "X2", "X3"),
  sweep_var      = sweep_var,
  sweep_range    = sweep_range,
  thrY           = thrY,
  thrX_default   = thrX_default,
  dir.exp        = 1,
  return_details = FALSE
)

head(res_cts)
#>   threshold  expression inclS covS
#> 1         6 No solution    NA   NA
#> 2         7 No solution    NA   NA
#> 3         8 No solution    NA   NA
#> 4         9 No solution    NA   NA

MCTS–QCA: Multiple X Sweep (ctSweepM)

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

res_mcts <- ctSweepM(
  dat            = dat,
  Yvar           = "Y",
  Xvars          = c("X1", "X2", "X3"),
  sweep_vars     = c("X2", "X3"),
  sweep_range    = 6:9,
  thrY           = 7,
  thrX_default   = 7,
  dir.exp        = 1,
  return_details = FALSE
)
#> Error in ctSweepM(dat = dat, Yvar = "Y", Xvars = c("X1", "X2", "X3"), : unused arguments (sweep_vars = c("X2", "X3"), sweep_range = 6:9, thrX_default = 7)

head(res_mcts)
#> Error: object 'res_mcts' not found

OTS–QCA: Outcome Sweep (otSweep)

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

res_ots <- otSweep(
  dat            = dat,
  Yvar           = "Y",
  Xvars          = c("X1", "X2", "X3"),
  sweep_range    = 6:9,
  thrX           = c(X1 = 7, X2 = 7, X3 = 7),
  dir.exp        = 1,
  return_details = FALSE
)

head(res_ots)
#>   thrY  expression inclS covS
#> 1    6 No solution    NA   NA
#> 2    7 No solution    NA   NA
#> 3    8 No solution    NA   NA
#> 4    9 No solution    NA   NA

DTS–QCA: Two-Dimensional Sweep (dtSweep)

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

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

sweep_range_dts_Y <- 6:8

res_dts <- dtSweep(
  dat            = dat,
  Yvar           = "Y",
  Xvars          = c("X1", "X2", "X3"),
  sweep_list_X   = sweep_list_dts_X,
  sweep_range_Y  = sweep_range_dts_Y,
  dir.exp        = 1,
  return_details = FALSE
)

head(res_dts)
#>   combo_id thrY             thrX  expression inclS covS
#> 1        1    6 X1=6, X2=6, X3=6 No solution    NA   NA
#> 2        1    7 X1=6, X2=6, X3=6 No solution    NA   NA
#> 3        1    8 X1=6, X2=6, X3=6 No solution    NA   NA
#> 4        2    6 X1=7, X2=6, X3=6 No solution    NA   NA
#> 5        2    7 X1=7, X2=6, X3=6 No solution    NA   NA
#> 6        2    8 X1=7, X2=6, X3=6 No solution    NA   NA

Understanding the Output

Each sweep result contains:

General guidance:


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:

References

For more information on TS-QCA methodology, see:

Session Info

sessionInfo()
#> R version 4.4.1 (2024-06-14 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26200)
#> 
#> Matrix products: default
#> 
#> 
#> locale:
#> [1] LC_COLLATE=C                    LC_CTYPE=Japanese_Japan.utf8   
#> [3] LC_MONETARY=Japanese_Japan.utf8 LC_NUMERIC=C                   
#> [5] LC_TIME=Japanese_Japan.utf8    
#> 
#> time zone: Asia/Tokyo
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] QCA_3.23    admisc_0.39 TSQCA_0.1.2
#> 
#> loaded via a namespace (and not attached):
#>  [1] cli_3.6.5         knitr_1.50        rlang_1.1.6       xfun_0.52        
#>  [5] otel_0.2.0        promises_1.5.0    shiny_1.12.1      jsonlite_2.0.0   
#>  [9] xtable_1.8-4      htmltools_0.5.9   httpuv_1.6.16     sass_0.4.10      
#> [13] lpSolve_5.6.23    rmarkdown_2.29    evaluate_1.0.4    jquerylib_0.1.4  
#> [17] fastmap_1.2.0     yaml_2.3.10       lifecycle_1.0.4   compiler_4.4.1   
#> [21] Rcpp_1.1.0        rstudioapi_0.17.1 later_1.4.4       digest_0.6.39    
#> [25] R6_2.6.1          magrittr_2.0.4    bslib_0.9.0       declared_0.25    
#> [29] tools_4.4.1       mime_0.13         venn_1.12         cachem_1.1.0