The following references are useful while using the QCA package:
The dataset for this exercise can be downloaded below:
The following packages are required to be installed:
library(tidyverse)
library(NCA)
library(QCA)
library(SetMethods)
library(stringr)
library(psych)
library(dplyr)
library(readr)
Note: When you can these packages you will get some output, warnings() if any, which has been hidden in this document.
Transform and Load
Initially, save the data set in a designated folder, establish the directory, and proceed to read the data, ensuring to conduct basic transformations to match the specifications outlined in Pappas and Woodside’s (2021) IJIM paper. Please be mindful to adjust the file path to correspond with your folder structure. The dataset should be stored under the name “DataIJIM.”
setwd("C:/Users/lela006/Dropbox/PHD_QCA/Code and dataset")
DataIJIM<- read.csv("C:/Users/lela006/Dropbox/PHD_QCA/Code and dataset/DATA_IJIMtutorial_shared.CSV",sep = ";", header= TRUE)
To view the data, execute the command “DataIJIM” to access the complete dataset. I recommend inspecting a sample visually using functions like “head()”, “summary()”, and “nrow()”. For instance, “head(DataIJIM, 5)” displays the first 5 rows, while “summary(DataIJIM)” provides an overview of the data types. Occasionally, you may need to convert character data into numeric format, among other transformations, to aid in exploration. We’ve verified the total number of rows, which is consistent at 582 as anticipated.
nrow(DataIJIM)
## [1] 582
Note: When you can these packages you will get some output, warnings() if any, which has been hidden in this document.
Following, we generate a new data frame named “DataIJIMAnalysis,” importing only the required columns. Explore the data frame employing “head()” and “colnames()”. Subsequently, adjust the data types utilizing “type_convert()” and finally rename column names to align with Pappas and Woodside’s (2021) specifications. We’ve provided some codes below; feel free to remove the “#” and execute the code to observe the corresponding outcomes (e.g., “head(DataIJIMAnalysis, 5)”).
#Select relevant columns and save as new DF
DataIJIMAnalysis<- DataIJIM[,(1:9)]
DataIJIMAnalysis<- subset (DataIJIMAnalysis, select = -persuasio)
#show first 5 rows or records to see sample and column names
#head(DataIJIM, 5)
#head(DataIJIMAnalysis, 5)
#summary(DataIJIMAnalysis)
colnames(DataIJIMAnalysis)
## [1] "inf_qual" "benef" "personalizatio" "intentio"
## [5] "strong_negative" "weak_negative" "strong_positive" "weak_positive"
Convert datatype to a character first
This is done, so as to proceed with the “type_convert()” function to adjust the data types as needed for further analysis..
str(DataIJIMAnalysis)
## 'data.frame': 582 obs. of 8 variables:
## $ inf_qual : chr "6,428571429" "4,857142857" "5" "3,428571429" ...
## $ benef : chr "6,6" "5,8" "4,6" "2,6" ...
## $ personalizatio : chr "6" "4,666666667" "4" "7" ...
## $ intentio : chr "4,333333333" "4,666666667" "5" "4" ...
## $ strong_negative: chr "0,5" "0,05" "0,01" "0,5" ...
## $ weak_negative : chr "3,166666667" "2,166666667" "1" "5" ...
## $ strong_positive: chr "0,5" "0,71" "0,04" "0,5" ...
## $ weak_positive : chr "2" "4,75" "1,5" "4" ...
DataIJIMAnalysis <- type_convert(DataIJIMAnalysis,trim_ws=TRUE,col_types = cols(Pesos=col_integer(),Alturas=col_double()),locale = locale(decimal_mark = ","))
#summary(DataIJIMAnalysis)
We have provided summary(DataIJIMAnalysis) above; you could remove # and run the code to see the respective results. Next,we rename the columns to suit Pappas and Woodside(2021) i.e. msg_qual,ben_prsnl and so on.
#Rename columns to suit Pappas and Woodside(2021)
colnames(DataIJIMAnalysis) <- c( "msg_qual", "ben_prsnl", "prsnl_qual", "int_purachse","strong_neg","weak_neg","strong_pos","weak_pos")
Initial exploration as per IJIM paper and Calibration of data
To explore the data further, you can utilize functions like “describe()”, “summary()”, and for writing the output, “write.csv()”. In the IJIM paper, quantiles are computed using the percentiles within the descriptive statistics function of SPSS. This can be achieved using “quantile()”. For instance, the result for “DataIJIMAnalysis$weak_pos” is displayed. We’ve provided the respective codes below; feel free to remove the “#” and execute the code to observe the results.
#quantile(DataIJIMAnalysis$msg_qual, c(.05, .5, .95))
#quantile(DataIJIMAnalysis$ben_prsnl, c(.05, .5, .95))
#quantile(DataIJIMAnalysis$prsnl_qual, c(.05, .5, .95))
#quantile(DataIJIMAnalysis$int_purachse, c(.05, .5, .95))
#quantile(DataIJIMAnalysis$strong_neg, c(.05, .5, .95))
#quantile(DataIJIMAnalysis$strong_pos, c(.05, .5, .95))
#quantile(DataIJIMAnalysis$weak_neg, c(.05, .5, .95))
quantile(DataIJIMAnalysis$weak_pos, c(.05, .5, .95))
## 5% 50% 95%
## 1.0 3.0 5.5
Creating Dataframe
Before proceeding with calibration, it’s recommended to set the working directory using “setwd()” and assign a folder where you intend to write the QCA output.Next, we’ve created “QCAMEM” as demonstrated below. It consists of 582 rows and 8 columns, following the same data type specifications as outlined by Pappas and Woodside (2021) in section 6.3.4. Additionally, we’ve set the same thresholds. Furthermore, we’ve written the original data into “QCAMEM” for easier comparison of results (e.g., “QCAMEM $ MSG_QUAL <- DataIJIMAnalysis $ msg_qual”). You can use “head(QCAMEM, 5)” to compare the first five rows.
#setwd("C:/Users/lela006/Dropbox/PHD_QCA/Code and dataset/QCA Output")
QCAMEM <- data.frame(matrix(rep(numeric(582), 8), nrow = 582,dimnames = list(row.names(DataIJIMAnalysis),toupper(names(DataIJIMAnalysis)))))
head(QCAMEM,n = c(5, 6))
## MSG_QUAL BEN_PRSNL PRSNL_QUAL INT_PURACHSE STRONG_NEG WEAK_NEG
## 1 0 0 0 0 0 0
## 2 0 0 0 0 0 0
## 3 0 0 0 0 0 0
## 4 0 0 0 0 0 0
## 5 0 0 0 0 0 0
Note: The use of head(QCAMEM, n = c(5, 6)) to display only six columns was done for aesthetic purposes, ensuring that the sample data fits neatly within one view.
Calibrating data
QCAMEM$MSG_QUAL_C<- round(calibrate(DataIJIMAnalysis$msg_qual,type = "fuzzy",thresholds = c(2,4,6),logistic = TRUE,idm = 0.95),2)
QCAMEM$BEN_PRSNL_C<- round(calibrate(DataIJIMAnalysis$ben_prsnl,type = "fuzzy",thresholds = c(2,4,6),logistic = TRUE,idm = 0.95),2)
QCAMEM$PRSNL_QUAL_C<- round(calibrate(DataIJIMAnalysis$prsnl_qual,type = "fuzzy",thresholds = c(2,4,6),logistic = TRUE,idm = 0.95),2)
QCAMEM$INT_PURACHSE_C<- round(calibrate(DataIJIMAnalysis$int_purachse,type = "fuzzy",thresholds = c(2,4,6),logistic = TRUE,idm = 0.95),2)
QCAMEM$STRONG_NEG_C<- DataIJIMAnalysis$strong_neg
QCAMEM$WEAK_NEG_C<- round(calibrate(DataIJIMAnalysis$weak_neg,type = "fuzzy",thresholds = c(2,4,6),logistic = TRUE,idm = 0.95),4)
QCAMEM$STRONG_POS_C<- DataIJIMAnalysis$strong_pos
QCAMEM$WEAK_POS_C<- round(calibrate(DataIJIMAnalysis$weak_pos,type = "fuzzy",thresholds = c(2,4,6),logistic = TRUE,idm = 0.95),2)
QCAMEM$MSG_QUAL<- DataIJIMAnalysis$msg_qual
QCAMEM$BEN_PRSNL<- DataIJIMAnalysis$ben_prsnl
QCAMEM$PRSNL_QUAL<- DataIJIMAnalysis$prsnl_qual
QCAMEM$INT_PURACHSE<- DataIJIMAnalysis$int_purachse
QCAMEM$STRONG_NEG<- DataIJIMAnalysis$strong_neg
QCAMEM$WEAK_NEG<- DataIJIMAnalysis$weak_neg
QCAMEM$STRONG_POS<- DataIJIMAnalysis$strong_pos
QCAMEM$WEAK_POS<- DataIJIMAnalysis$weak_pos
head(QCAMEM,5)
## MSG_QUAL BEN_PRSNL PRSNL_QUAL INT_PURACHSE STRONG_NEG WEAK_NEG STRONG_POS
## 1 6.428571 6.6 6.000000 4.333333 0.50 3.166667 0.50
## 2 4.857143 5.8 4.666667 4.666667 0.05 2.166667 0.71
## 3 5.000000 4.6 4.000000 5.000000 0.01 1.000000 0.04
## 4 3.428571 2.6 7.000000 4.000000 0.50 5.000000 0.50
## 5 6.142857 6.4 6.000000 5.666667 0.02 1.166667 0.94
## WEAK_POS MSG_QUAL_C BEN_PRSNL_C PRSNL_QUAL_C INT_PURACHSE_C STRONG_NEG_C
## 1 2.00 0.97 0.98 0.95 0.62 0.50
## 2 4.75 0.78 0.93 0.73 0.73 0.05
## 3 1.50 0.81 0.71 0.50 0.81 0.01
## 4 4.00 0.30 0.11 0.99 0.50 0.50
## 5 6.00 0.96 0.97 0.95 0.92 0.02
## WEAK_NEG_C STRONG_POS_C WEAK_POS_C
## 1 0.2267 0.50 0.05
## 2 0.0630 0.71 0.75
## 3 0.0119 0.04 0.02
## 4 0.8134 0.50 0.50
## 5 0.0152 0.94 0.95
Note: It’s important to note that the function “calibrate()” includes the parameter “idm” (inclusion degree of membership) with a default set at 0.95, similar to Ragin’s FSQCA software. However, it’s worth mentioning that fsQCA and R (QCA) may differ slightly. In FSQCA, values are rounded to two decimal places; therefore we have some the same here too. For example, in Pappas and Woodside’s (2021) paper, Figure 3, “weak_neg_c” (row 1) is actually 0.2267. FSQCA rounds this to 0.22, while R (QCA) rounds it to 0.23. There also could be very minor deviations due to some approximations introduced by Ragin; however authors have claimed them to be insignificant in the context of overall scores and solutions.
Visual Exploration and XY plots
To verify the calibration and visually explore the results, one strategy is to export the data to a CSV file and then utilize tools like FSQCA, Tableau, or Excel for ease of use and flexibility. Here, we can divide the layout for visual exploration using the command par(mfrow = c(2, 3)). As an example, we can plot three relationships here.
Raw vs Calibrated data
par(mfrow = c(1, 3))
plot(QCAMEM$MSG_QUAL, QCAMEM$MSG_QUAL_C, main = "MSG_QUAL_C", xlab = "Raw data",
ylab = "Calibrated data")
plot(QCAMEM$BEN_PRSNL, QCAMEM$BEN_PRSNL_C, main = "BEN_PRSNL", xlab = "Raw data",
ylab = "Calibrated data")
plot(QCAMEM$STRONG_NEG, QCAMEM$STRONG_NEG_C, main = "STRONG_NEG", xlab = "Raw data",
ylab = "Calibrated data")
Outcome Vs Condition: Original and Calibrated
par(mfrow = c(2, 3))
plot(QCAMEM$MSG_QUAL, QCAMEM$INT_PURACHSE, main = "Actual data", xlab = "MSG_QUAL",
ylab = "INT_PURACHSE")
plot(QCAMEM$BEN_PRSNL, QCAMEM$INT_PURACHSE, main = "Actual data", xlab = "BEN_PRSNL",
ylab = "INT_PURACHSE")
plot(QCAMEM$STRONG_NEG, QCAMEM$INT_PURACHSE, main = "Actual data", xlab = "STRONG_NEG",
ylab = "INT_PURACHSE")
plot(QCAMEM$MSG_QUAL_C, QCAMEM$INT_PURACHSE_C, main = "Calibrated data", xlab = "MSG_QUAL_C",
ylab = "INT_PURACHSE_C")
plot(QCAMEM$BEN_PRSNL_C, QCAMEM$INT_PURACHSE_C, main = "Calibrated data", xlab = "BEN_PRSNL_C",
ylab = "INT_PURACHSE_C")
plot(QCAMEM$STRONG_NEG_C, QCAMEM$INT_PURACHSE_C, main = "Calibrated data", xlab = "STRONG_NEG_C",
ylab = "INT_PURACHSE_C")
You could also explore using scatter plot using pairs
par(mfrow= c(1,1))
pairs(~ QCAMEM$INT_PURACHSE_C+QCAMEM$MSG_QUAL_C+QCAMEM$BEN_PRSNL_C+QCAMEM$PRSNL_QUAL_C)
How to replace 0.5 to 0.5001
The process is straightforward: we create a new data frame (let’s call it “QCAMEM_New”) and replace the values in all columns. It’s noteworthy that Pappas and Woodside (2021) have chosen not to adjust the set membership of 0.5, leaving it as is without adding 0.001 as proposed by Per Fiss. Consequently, these rows are eliminated during the truth table formation.You can see this warning() at the bottom of the truth table later.
QCAMEM_New <- QCAMEM[,(9:16)]
QCAMEM_New2 <- QCAMEM_New
QCAMEM_New2[QCAMEM_New2 == 0.5] <- 0.5001
head(QCAMEM_New,n = c(5, 5))
## MSG_QUAL_C BEN_PRSNL_C PRSNL_QUAL_C INT_PURACHSE_C STRONG_NEG_C
## 1 0.97 0.98 0.95 0.62 0.50
## 2 0.78 0.93 0.73 0.73 0.05
## 3 0.81 0.71 0.50 0.81 0.01
## 4 0.30 0.11 0.99 0.50 0.50
## 5 0.96 0.97 0.95 0.92 0.02
head(QCAMEM_New2,n = c(5, 5))
## MSG_QUAL_C BEN_PRSNL_C PRSNL_QUAL_C INT_PURACHSE_C STRONG_NEG_C
## 1 0.97 0.98 0.9500 0.6200 0.5001
## 2 0.78 0.93 0.7300 0.7300 0.0500
## 3 0.81 0.71 0.5001 0.8100 0.0100
## 4 0.30 0.11 0.9900 0.5001 0.5001
## 5 0.96 0.97 0.9500 0.9200 0.0200
Note: Since we are replicating Pappas and Woodside’s (2021) paper, we will continue with the original data (i.e. without changing 0.5 to 0.5001) i.e. “QCAMEM_New” data frame. However, it could be a beneficial exercise to compare the truth table results obtained from “QCAMEM_New2.” Many researchers transition from R to FSQCA at this stage to verify necessary conditions and conduct standard analysis. If you intend to do so, please proceed with running the next line of code; otherwise, you can skip it.
#write.csv(QCAMEM_New, "QCAMEM_FSQCA.csv", row.names=TRUE)
#write.csv(QCAMEM_New2, "QCAMEM_FSQCA2.csv", row.names=TRUE)
Check for necessity and interpret what it means
First approach could be to use the Parameters of fit pof(). We have written an OR expression and the function returns inclusion (consistency) and coverage, plus RoN (relevance of necessity). The function can be used to calculate parameters of fit for single conditions.
pof(setms = "PRSNL_QUAL_C+MSG_QUAL_C+BEN_PRSNL_C+STRONG_NEG_C+WEAK_NEG_C+STRONG_POS_C+WEAK_POS_C", outcome = "INT_PURACHSE_C", relation = "necessity", data = QCAMEM_New)
##
## inclN RoN covN
## ------------------------------------
## 1 PRSNL_QUAL_C 0.871 0.760 0.824
## 2 MSG_QUAL_C 0.835 0.806 0.841
## 3 BEN_PRSNL_C 0.925 0.671 0.796
## 4 STRONG_NEG_C 0.276 0.939 0.767
## 5 WEAK_NEG_C 0.203 0.971 0.827
## 6 STRONG_POS_C 0.591 0.900 0.849
## 7 WEAK_POS_C 0.451 0.943 0.868
## 8 expression 0.983 0.426 0.721
## ------------------------------------
Second approach could be to use the superSubset as shown below.
superSubset(QCAMEM_New, outcome = INT_PURACHSE_C, incl.cut = 0.9)
##
## inclN RoN covN
## -------------------------------------------------------------------------------
## 1 BEN_PRSNL_C 0.925 0.671 0.796
## 2 ~WEAK_NEG_C 0.926 0.341 0.661
## 3 ~MSG_QUAL_C + PRSNL_QUAL_C 0.910 0.539 0.728
## 4 MSG_QUAL_C + PRSNL_QUAL_C 0.941 0.637 0.786
## 5 ~MSG_QUAL_C + ~STRONG_NEG_C 0.921 0.385 0.674
## 6 MSG_QUAL_C + ~STRONG_NEG_C 0.961 0.380 0.694
## 7 MSG_QUAL_C + ~STRONG_POS_C 0.910 0.479 0.703
## 8 MSG_QUAL_C + STRONG_POS_C 0.904 0.712 0.809
## 9 MSG_QUAL_C + ~WEAK_POS_C 0.927 0.389 0.678
## 10 ~BEN_PRSNL_C + PRSNL_QUAL_C 0.901 0.570 0.737
## 11 ~BEN_PRSNL_C + ~STRONG_NEG_C 0.905 0.409 0.674
## 12 ~PRSNL_QUAL_C + ~STRONG_NEG_C 0.909 0.403 0.674
## 13 PRSNL_QUAL_C + ~STRONG_NEG_C 0.975 0.347 0.690
## 14 PRSNL_QUAL_C + ~STRONG_POS_C 0.941 0.446 0.707
## 15 PRSNL_QUAL_C + STRONG_POS_C 0.912 0.667 0.788
## 16 PRSNL_QUAL_C + ~WEAK_POS_C 0.953 0.361 0.683
## 17 PRSNL_QUAL_C + WEAK_POS_C 0.901 0.696 0.799
## 18 ~STRONG_NEG_C + WEAK_NEG_C 0.934 0.421 0.694
## 19 ~STRONG_NEG_C + ~STRONG_POS_C 0.924 0.342 0.660
## 20 ~STRONG_NEG_C + STRONG_POS_C 0.949 0.405 0.696
## 21 ~STRONG_NEG_C + ~WEAK_POS_C 0.939 0.282 0.649
## 22 ~STRONG_NEG_C + WEAK_POS_C 0.935 0.432 0.698
## 23 STRONG_POS_C + ~WEAK_POS_C 0.927 0.371 0.672
## 24 MSG_QUAL_C + ~BEN_PRSNL_C + STRONG_NEG_C 0.903 0.563 0.736
## 25 MSG_QUAL_C + ~BEN_PRSNL_C + WEAK_POS_C 0.918 0.558 0.741
## 26 MSG_QUAL_C + ~PRSNL_QUAL_C + WEAK_POS_C 0.915 0.563 0.741
## 27 MSG_QUAL_C + STRONG_NEG_C + WEAK_POS_C 0.907 0.677 0.792
## 28 MSG_QUAL_C + ~BEN_PRSNL_C + ~PRSNL_QUAL_C + WEAK_NEG_C 0.907 0.525 0.721
## -------------------------------------------------------------------------------
Third approach could be using xy.plot produces XY plots and relevant measures/pof.
xy.plot("BEN_PRSNL_C", "INT_PURACHSE_C", data = QCAMEM_New, labs = NULL, necessity=TRUE,
jitter = TRUE, main = "BEN_PRSNL_C necessary for INT_PURACHSE", xlab = "BEN_PRSNL_C", ylab = "INT_PURACHSE_C")
xy.plot("~WEAK_NEG_C", "INT_PURACHSE_C", data = QCAMEM_New, labs = NULL, necessity=TRUE,
jitter = TRUE, main = "~WEAK_NEG_C necessary for INT_PURACHSE", xlab = "~WEAK_NEG_C", ylab = "INT_PURACHSE_C")
Construct Truth Table
Note: When displaying the truth table in R Markdown, it wasn’t feasible to fit it into one view, so we had to rename the columns accordingly.
names(QCAMEM_New)[names(QCAMEM_New) == "MSG_QUAL_C"] <- "MSG"
names(QCAMEM_New)[names(QCAMEM_New) == "BEN_PRSNL_C"] <- "BEN"
names(QCAMEM_New)[names(QCAMEM_New) == "PRSNL_QUAL_C"] <- "PRSNL"
names(QCAMEM_New)[names(QCAMEM_New) == "STRONG_NEG_C"] <- "S_NEG"
names(QCAMEM_New)[names(QCAMEM_New) == "WEAK_NEG_C"] <- "W_NEG"
names(QCAMEM_New)[names(QCAMEM_New) == "STRONG_POS_C"] <- "S_POS"
names(QCAMEM_New)[names(QCAMEM_New) == "WEAK_POS_C"] <- "W_POS"
names(QCAMEM_New)[names(QCAMEM_New) == "INT_PURACHSE_C"] <- "PUR"
Following the approach outlined by Pappas and Woodside (2021) in section 6.3.4, we’ll initially sort the truth table by “n,” “Incl,” and “PRI” with an inclusion cutoff of 0.75 to evaluate consistency values and PRI. We’ll exclude the remainders by default. If you wish to include the remainders as well, you can add “complete=TRUE” to the following code after the outcome. Please note that since we’re using an inclusion cutoff of 0.5, you may encounter a warning indicating that “Fuzzy causal conditions should not have values of 0.5 in the data” after running the truth table.
TTQCAMEM <-truthTable(QCAMEM_New,outcome = "PUR",incl.cut = 0.75,n.cut = 1, show.cases=FALSE,sort.by = "PRI")
## Warning in truthTable(QCAMEM_New, outcome = "PUR", incl.cut = 0.75, n.cut = 1, :
## Fuzzy causal conditions should not have values of 0.5 in the data.
TTQCAMEM
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## MSG BEN PRSNL S_NEG W_NEG S_POS W_POS OUT n incl PRI
## 116 1 1 1 0 0 1 1 1 65 0.966 0.935
## 114 1 1 1 0 0 0 1 1 4 0.970 0.908
## 115 1 1 1 0 0 1 0 1 28 0.959 0.902
## 52 0 1 1 0 0 1 1 1 8 0.969 0.889
## 128 1 1 1 1 1 1 1 1 15 0.959 0.876
## 50 0 1 1 0 0 0 1 1 2 0.966 0.840
## 124 1 1 1 1 0 1 1 1 1 0.959 0.833
## 120 1 1 1 0 1 1 1 1 1 0.960 0.830
## 113 1 1 1 0 0 0 0 1 99 0.911 0.822
## 123 1 1 1 1 0 1 0 1 4 0.958 0.819
## 118 1 1 1 0 1 0 1 1 1 0.964 0.815
## 127 1 1 1 1 1 1 0 1 1 0.954 0.793
## 51 0 1 1 0 0 1 0 1 3 0.950 0.788
## 100 1 1 0 0 0 1 1 1 4 0.948 0.778
## 55 0 1 1 0 1 1 0 1 1 0.958 0.741
## 121 1 1 1 1 0 0 0 1 2 0.937 0.740
## 99 1 1 0 0 0 1 0 1 2 0.940 0.732
## 58 0 1 1 1 0 0 1 1 1 0.954 0.698
## 97 1 1 0 0 0 0 0 1 15 0.895 0.655
## 20 0 0 1 0 0 1 1 1 4 0.941 0.654
## 106 1 1 0 1 0 0 1 1 1 0.947 0.642
## 49 0 1 1 0 0 0 0 1 14 0.883 0.591
## 61 0 1 1 1 1 0 0 1 2 0.937 0.590
## 57 0 1 1 1 0 0 0 1 4 0.923 0.583
## 19 0 0 1 0 0 1 0 1 3 0.938 0.550
## 32 0 0 1 1 1 1 1 1 1 0.941 0.507
## 47 0 1 0 1 1 1 0 1 2 0.929 0.476
## 105 1 1 0 1 0 0 0 1 2 0.904 0.469
## 81 1 0 1 0 0 0 0 1 4 0.889 0.462
## 33 0 1 0 0 0 0 0 1 3 0.878 0.454
## 109 1 1 0 1 1 0 0 1 1 0.912 0.422
## 41 0 1 0 1 0 0 0 1 1 0.909 0.409
## 67 1 0 0 0 0 1 0 1 2 0.926 0.409
## 45 0 1 0 1 1 0 0 1 1 0.919 0.376
## 65 1 0 0 0 0 0 0 1 4 0.865 0.334
## 17 0 0 1 0 0 0 0 1 10 0.842 0.320
## 4 0 0 0 0 0 1 1 1 6 0.864 0.304
## 29 0 0 1 1 1 0 0 1 1 0.886 0.301
## 25 0 0 1 1 0 0 0 1 3 0.870 0.272
## 3 0 0 0 0 0 1 0 1 1 0.880 0.267
## 11 0 0 0 1 0 1 0 1 1 0.897 0.251
## 2 0 0 0 0 0 0 1 1 3 0.868 0.202
## 13 0 0 0 1 1 0 0 1 1 0.864 0.180
## 1 0 0 0 0 0 0 0 0 39 0.701 0.154
## 9 0 0 0 1 0 0 0 1 6 0.784 0.107
Next, following the approach proposed by Pappas and Woodside (2021), we’ll select the inclusion cutoff and sort the truth table in the order of outcome, n, and inclusion for visual inspection. We’ll then use this truth table to obtain the complex or conservative solution.
TruthTable for outcome
TTQCAMEM <- truthTable(QCAMEM_New,outcome = "PUR",incl.cut = 0.87,n.cut = 3, show.cases=FALSE,sort.by = "out,n,incl")
## Warning in truthTable(QCAMEM_New, outcome = "PUR", incl.cut = 0.87, n.cut = 3, :
## Fuzzy causal conditions should not have values of 0.5 in the data.
TTQCAMEM
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## MSG BEN PRSNL S_NEG W_NEG S_POS W_POS OUT n incl PRI
## 113 1 1 1 0 0 0 0 1 99 0.911 0.822
## 116 1 1 1 0 0 1 1 1 65 0.966 0.935
## 115 1 1 1 0 0 1 0 1 28 0.959 0.902
## 128 1 1 1 1 1 1 1 1 15 0.959 0.876
## 97 1 1 0 0 0 0 0 1 15 0.895 0.655
## 49 0 1 1 0 0 0 0 1 14 0.883 0.591
## 52 0 1 1 0 0 1 1 1 8 0.969 0.889
## 114 1 1 1 0 0 0 1 1 4 0.970 0.908
## 123 1 1 1 1 0 1 0 1 4 0.958 0.819
## 100 1 1 0 0 0 1 1 1 4 0.948 0.778
## 20 0 0 1 0 0 1 1 1 4 0.941 0.654
## 57 0 1 1 1 0 0 0 1 4 0.923 0.583
## 81 1 0 1 0 0 0 0 1 4 0.889 0.462
## 51 0 1 1 0 0 1 0 1 3 0.950 0.788
## 19 0 0 1 0 0 1 0 1 3 0.938 0.550
## 33 0 1 0 0 0 0 0 1 3 0.878 0.454
## 1 0 0 0 0 0 0 0 0 39 0.701 0.154
## 17 0 0 1 0 0 0 0 0 10 0.842 0.320
## 4 0 0 0 0 0 1 1 0 6 0.864 0.304
## 9 0 0 0 1 0 0 0 0 6 0.784 0.107
## 65 1 0 0 0 0 0 0 0 4 0.865 0.334
## 25 0 0 1 1 0 0 0 0 3 0.870 0.272
## 2 0 0 0 0 0 0 1 0 3 0.868 0.202
Next, it’s advisable to examine the truth table of the negated outcome. One reason for this is to check for contradictory rows, i.e., ones that explain both the outcome and its negation.
TruthTable for its negation
TTQCAMEMNEG <- truthTable(QCAMEM_New,outcome = "~PUR",incl.cut = 0.87,n.cut = 3, show.cases=FALSE,sort.by = "out,n,incl")
## Warning in truthTable(QCAMEM_New, outcome = "~PUR", incl.cut = 0.87, n.cut = 3, :
## Fuzzy causal conditions should not have values of 0.5 in the data.
TTQCAMEMNEG
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## MSG BEN PRSNL S_NEG W_NEG S_POS W_POS OUT n incl PRI
## 1 0 0 0 0 0 0 0 1 39 0.940 0.831
## 17 0 0 1 0 0 0 0 1 10 0.919 0.651
## 9 0 0 0 1 0 0 0 1 6 0.972 0.885
## 4 0 0 0 0 0 1 1 1 6 0.941 0.696
## 65 1 0 0 0 0 0 0 1 4 0.932 0.666
## 81 1 0 1 0 0 0 0 1 4 0.904 0.537
## 57 0 1 1 1 0 0 0 1 4 0.892 0.417
## 20 0 0 1 0 0 1 1 1 4 0.889 0.346
## 2 0 0 0 0 0 0 1 1 3 0.962 0.770
## 25 0 0 1 1 0 0 0 1 3 0.940 0.667
## 19 0 0 1 0 0 1 0 1 3 0.923 0.436
## 33 0 1 0 0 0 0 0 1 3 0.898 0.546
## 113 1 1 1 0 0 0 0 0 99 0.571 0.147
## 116 1 1 1 0 0 1 1 0 65 0.501 0.051
## 115 1 1 1 0 0 1 0 0 28 0.619 0.093
## 97 1 1 0 0 0 0 0 0 15 0.796 0.328
## 128 1 1 1 1 1 1 1 0 15 0.711 0.117
## 49 0 1 1 0 0 0 0 0 14 0.825 0.386
## 52 0 1 1 0 0 1 1 0 8 0.754 0.111
## 100 1 1 0 0 0 1 1 0 4 0.816 0.218
## 123 1 1 1 1 0 1 0 0 4 0.805 0.169
## 114 1 1 1 0 0 0 1 0 4 0.707 0.092
## 51 0 1 1 0 0 1 0 0 3 0.811 0.201
Upon visually examining both “TTQCAMEM” and “TTQCAMEMNEG,” we observe that rows 20, 19, and 33 have “out=1” in both truth tables.
Various strategies can be proposed for addressing this observation, including increasing the inclusion cutoff, excluding these rows, revisiting cases, or including the rows if the goal is to identify all possible outcomes. However, for now, we’ll leave them as they are and proceed with the complex solution.
It’s advisable to write out the truth table to further explore it in tools like Excel. If you wish to do so, please proceed with running the next line of code; otherwise, you can skip it. This code will write the output to the folder that has been set with “setwd()”.
#write.table(TTQCAMEM$tt, file = "TruthTable.csv", sep = ";", quote = FALSE)
Obtaining the configurations/solutions
The solutions in this context encompass the complex solution, parsimonious solution, and intermediate solution. In this context, a “solution” denotes a combination of configurations supported by a significant number of cases, where the rule “the combination leads to the outcome” is consistently upheld.
Complex solution
The complex solution entails all possible combinations of conditions when traditional logical operations are applied. We have set “show.cases” to FALSE, but if you have a small sample size and wish to explore individual cases, you can set this parameter to TRUE.
complex_QCAMEM <- minimize(TTQCAMEM,details = TRUE, show.cases=FALSE)
complex_QCAMEM
##
## M1: ~MSG*PRSNL*~S_NEG*~W_NEG*S_POS + MSG*BEN*PRSNL*~S_NEG*~W_NEG +
## BEN*~S_NEG*~W_NEG*~S_POS*~W_POS + ~MSG*BEN*PRSNL*~W_NEG*~S_POS*~W_POS +
## MSG*BEN*PRSNL*~W_NEG*S_POS*~W_POS + MSG*BEN*~S_NEG*~W_NEG*S_POS*W_POS +
## MSG*PRSNL*~S_NEG*~W_NEG*~S_POS*~W_POS +
## MSG*BEN*PRSNL*S_NEG*W_NEG*S_POS*W_POS -> PUR
##
## inclS PRI covS covU
## --------------------------------------------------------------------
## 1 ~MSG*PRSNL*~S_NEG*~W_NEG*S_POS 0.933 0.786 0.264 0.020
## 2 MSG*BEN*PRSNL*~S_NEG*~W_NEG 0.918 0.868 0.691 0.051
## 3 BEN*~S_NEG*~W_NEG*~S_POS*~W_POS 0.838 0.709 0.537 0.045
## 4 ~MSG*BEN*PRSNL*~W_NEG*~S_POS*~W_POS 0.876 0.586 0.290 0.004
## 5 MSG*BEN*PRSNL*~W_NEG*S_POS*~W_POS 0.957 0.898 0.340 0.005
## 6 MSG*BEN*~S_NEG*~W_NEG*S_POS*W_POS 0.951 0.909 0.341 0.007
## 7 MSG*PRSNL*~S_NEG*~W_NEG*~S_POS*~W_POS 0.896 0.797 0.473 0.005
## 8 MSG*BEN*PRSNL*S_NEG*W_NEG*S_POS*W_POS 0.959 0.876 0.135 0.023
## --------------------------------------------------------------------
## M1 0.851 0.774 0.832
Parsimonious solution
The parsimonious solution set is a simplified version of the complex solution, based on simplifying assumptions, and presents the most important conditions which cannot be left out from any solution. These are called “core conditions” (Fiss, 2011).In practice, the parsimonious solution represents a superset of the complex (or conservative) solution. It employs logical remainders as long as they contribute to a simpler solution. Conversely, the complex solution doesn’t utilize remainders at all. To incorporate remainders, you can use “include=?” in the minimize function mentioned earlier.
par_QCAMEM <- minimize(TTQCAMEM,include="?",details = TRUE, show.cases=FALSE)
par_QCAMEM
##
## M1: BEN + MSG*PRSNL + PRSNL*S_POS -> PUR
##
## inclS PRI covS covU
## ------------------------------------------
## 1 BEN 0.796 0.715 0.925 0.134
## 2 MSG*PRSNL 0.897 0.842 0.766 0.008
## 3 PRSNL*S_POS 0.922 0.871 0.551 0.012
## ------------------------------------------
## M1 0.786 0.702 0.949
We can see which remainders we have used to obtain the parsimonious solution. Inspecting the truthtable, we see that 72 out of total 105 remainders have been used.
par_remainders_used <- par_QCAMEM$SA$M1
#par_remainders_used
It’s advisable to write out the remainders to further explore it in tools like Excel. If you wish to do so, please proceed with running the next line of code; otherwise, you can skip it. This code will write the output to the folder that has been set with “setwd()”.
#write.table(par_remainders_used, file = "par_rem_used.csv", sep = ";", quote = FALSE)
Intermediate solution
Finally, the intermediate solution is obtained when performing counterfactual analysis on the complex and parsimonious solutions The intermediate solution uses a subset of those simplifying assumptions used to compute the parsimonious solution, which should be consistent with theoretical and empirical knowledge.Here based on analysis of necessity we set the directional expectations for presence of BEN and absence of W_NEG.
Inermediate_QCAMEM <- minimize(TTQCAMEM,include="?",details = TRUE, show.cases=FALSE, dir.exp = "BEN,~W_NEG")
Inermediate_QCAMEM
##
## From C1P1:
##
## M1: ~MSG*PRSNL*~S_NEG*~W_NEG*S_POS + MSG*BEN*PRSNL*~S_NEG*~W_NEG +
## MSG*BEN*PRSNL*~W_NEG*S_POS + BEN*~S_NEG*~W_NEG*~S_POS*~W_POS +
## ~MSG*BEN*PRSNL*~W_NEG*~S_POS*~W_POS + MSG*BEN*PRSNL*S_NEG*S_POS*W_POS +
## MSG*BEN*~S_NEG*~W_NEG*S_POS*W_POS +
## MSG*PRSNL*~S_NEG*~W_NEG*~S_POS*~W_POS -> PUR
##
## inclS PRI covS covU
## --------------------------------------------------------------------
## 1 ~MSG*PRSNL*~S_NEG*~W_NEG*S_POS 0.933 0.786 0.264 0.020
## 2 MSG*BEN*PRSNL*~S_NEG*~W_NEG 0.918 0.868 0.691 0.034
## 3 MSG*BEN*PRSNL*~W_NEG*S_POS 0.955 0.918 0.467 0.006
## 4 BEN*~S_NEG*~W_NEG*~S_POS*~W_POS 0.838 0.709 0.537 0.045
## 5 ~MSG*BEN*PRSNL*~W_NEG*~S_POS*~W_POS 0.876 0.586 0.290 0.004
## 6 MSG*BEN*PRSNL*S_NEG*S_POS*W_POS 0.959 0.877 0.160 0.023
## 7 MSG*BEN*~S_NEG*~W_NEG*S_POS*W_POS 0.951 0.909 0.341 0.007
## 8 MSG*PRSNL*~S_NEG*~W_NEG*~S_POS*~W_POS 0.896 0.797 0.473 0.005
## --------------------------------------------------------------------
## M1 0.851 0.774 0.835
#To print the easy counterfactuals (or in other words, remainders used, we can display using $i.sol$C1P1$EC). In this case it is only one remainder is used i.e. 124.
Inermediate_QCAMEM$i.sol$C1P1$EC
## MSG BEN PRSNL S_NEG W_NEG S_POS W_POS
## 124 1 1 1 1 0 1 1
#Inermediate_QCAMEM$i.sol$C1P1$DC
Plotting a specific proposition (solutions)
par(mfrow = c(1, 1))
sol2 <- "MSG*BEN*PRSNL*~S_NEG*~W_NEG"
sol3 <- "MSG*BEN*PRSNL*~W_NEG*S_POS"
sol4 <- "BEN*~S_NEG*~W_NEG*~S_POS*~W_POS"
XYplot(sol2, "PUR", data = QCAMEM_New, enhance = FALSE, model = TRUE, xlab = sol2)
XYplot(sol3, "PUR", data = QCAMEM_New, enhance = FALSE, model = TRUE, xlab = sol3)
XYplot(sol4, "PUR", data = QCAMEM_New, enhance = FALSE, model = TRUE, xlab = sol4)
Enhanced Standard Analysis
According to Enhanced Standard Analysis by Schiender and Wagemann, some simplifying assumptions should be avoided and not included in the minimization process. Please read about type 1, 2 and 3. You could use findRows() to identify these rows and exclude them using exclude = contradictory_rows. Depending on need, one could tweak the code and repeat the above steps.
#contradictory_rows1 <- findRows(obj = TTQCAMEM, type= 1)
#contradictory_rows2 <- findRows(obj = TTQCAMEM, type= 2)
#contradictory_rows3 <- findRows(obj = TTQCAMEM, type= 3)
#manual <- c()
#as.vector(manual)
#contradictory_rows <- c(contradictory_rows1, contradictory_rows2, contradictory_rows3, manual)
#par_QCAMEM_exclude <- minimize(TTQCAMEM,include="?",details = TRUE, show.cases=FALSE,exclude = contradictory_rows)
#par_QCAMEM_exclude
#Inermediate_QCAMEM_exclude <- minimize(TTQCAMEM,include="?",details = TRUE, show.cases=FALSE, dir.exp = "BEN,~W_NEG", exclude = contradictory_rows)
#Inermediate_QCAMEM_exclude
model_NCA <- nca_analysis(DataIJIMAnalysis, c(1,2,3,5,6,7,8),4,bottleneck.x='actual', bottleneck.y='actual',steps=10, test.rep = 10000)
## Preparing the analysis, this might take a few seconds...
## Do test for : ce_fdh - msg_qualDo test for : cr_fdh - msg_qualDo test for : ce_fdh - ben_prsnlDo test for : cr_fdh - ben_prsnlDo test for : ce_fdh - prsnl_qualDo test for : cr_fdh - prsnl_qualDo test for : ce_fdh - strong_negDo test for : cr_fdh - strong_negDo test for : ce_fdh - weak_negDo test for : cr_fdh - weak_negDo test for : ce_fdh - strong_posDo test for : cr_fdh - strong_posDo test for : ce_fdh - weak_posDo test for : cr_fdh - weak_pos
#options for displaying the independent variables in the bottleneck table; e.g. ’percentage.range’ to display the percentage of range, ’actual’ to display the actual values, ’percentile’ to display the percentiles.
nca_output(model_NCA , plots=TRUE, bottlenecks=TRUE, pdf=FALSE)
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
## Y 1 2 3 4 5 6 7
## 1.0 NN NN NN NN NN NN NN
## 1.6 NN NN NN NN NN NN NN
## 2.2 NN 1.200 NN NN NN NN NN
## 2.8 1.857 1.200 NN NN NN NN NN
## 3.4 2.000 1.200 NN NN NN NN NN
## 4.0 2.286 2.400 NN NN NN NN NN
## 4.6 2.286 2.600 NN NN NN NN NN
## 5.2 2.286 2.600 2.000 NN NN NN NN
## 5.8 2.286 3.800 3.000 NN NN NN NN
## 6.4 3.000 4.800 3.333 NN NN 0.030 NN
## 7.0 4.000 4.800 4.667 NN NN 0.030 NN
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
## Y 1 2 3 4 5 6 7
## 1.0 NN NN NN NN NN NN NN
## 1.6 NN NN NN NN NN NN NN
## 2.2 NN NN NN NN NN NN NN
## 2.8 NN NN NN NN NN NN NN
## 3.4 1.604 1.450 NN NN NN NN NN
## 4.0 1.936 1.964 NN NN NN NN NN
## 4.6 2.269 2.478 NN NN NN NN NN
## 5.2 2.601 2.992 1.627 NN NN NN NN
## 5.8 2.933 3.505 2.510 NN NN NN NN
## 6.4 3.266 4.019 3.392 NN NN 0.018 NN
## 7.0 3.598 4.533 4.275 NN NN 0.030 NN
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 33.429
## Xmin 1.429
## Xmax 7.000
## Ymin 1.000
## Ymax 7.000
##
## ce_fdh cr_fdh
## Ceiling zone 4.381 4.248
## Effect size 0.131 0.127
## # above 0 5
## c-accuracy 100% 99.1%
## Fit 100% 97.0%
## p-value 0.000 0.000
## p-accuracy 0.000 0.000
##
## Slope 1.805
## Intercept 0.505
## Abs. ineff. 22.286 24.932
## Rel. ineff. 66.667 74.584
## Condition ineff. 53.846 61.059
## Outcome ineff. 27.778 34.731
##
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 36
## Xmin 1
## Xmax 7
## Ymin 1
## Ymax 7
##
## ce_fdh cr_fdh
## Ceiling zone 8.267 7.288
## Effect size 0.230 0.202
## # above 0 3
## c-accuracy 100% 99.5%
## Fit 100% 88.2%
## p-value 0.000 0.000
## p-accuracy 0.000 0.000
##
## Slope 1.168
## Intercept 1.707
## Abs. ineff. 17.000 21.424
## Rel. ineff. 47.222 59.512
## Condition ineff. 36.667 41.112
## Outcome ineff. 16.667 31.246
##
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 36
## Xmin 1
## Xmax 7
## Ymin 1
## Ymax 7
##
## ce_fdh cr_fdh
## Ceiling zone 4.444 3.646
## Effect size 0.123 0.101
## # above 0 2
## c-accuracy 100% 99.7%
## Fit 100% 82.0%
## p-value 0.000 0.000
## p-accuracy 0.000 0.000
##
## Slope 0.680
## Intercept 4.093
## Abs. ineff. 27.444 28.709
## Rel. ineff. 76.235 79.747
## Condition ineff. 38.889 45.425
## Outcome ineff. 61.111 62.889
##
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 5.88
## Xmin 0.00
## Xmax 0.99
## Ymin 1.00
## Ymax 7.00
##
## ce_fdh cr_fdh
## Ceiling zone 0.000 0.000
## Effect size 0.000 0.000
## # above 0 0
## c-accuracy 100% 100%
## Fit 100% NA
## p-value 1.000 1.000
## p-accuracy 0.000 0.000
##
## Slope
## Intercept
## Abs. ineff.
## Rel. ineff.
## Condition ineff.
## Outcome ineff.
##
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 34.000
## Xmin 1.000
## Xmax 6.667
## Ymin 1.000
## Ymax 7.000
##
## ce_fdh cr_fdh
## Ceiling zone 0.000 0.000
## Effect size 0.000 0.000
## # above 0 0
## c-accuracy 100% 100%
## Fit 100% NA
## p-value 1.000 1.000
## p-accuracy 0.000 0.000
##
## Slope
## Intercept
## Abs. ineff.
## Rel. ineff.
## Condition ineff.
## Outcome ineff.
##
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 5.88
## Xmin 0.00
## Xmax 0.99
## Ymin 1.00
## Ymax 7.00
##
## ce_fdh cr_fdh
## Ceiling zone 0.020 0.010
## Effect size 0.003 0.002
## # above 0 0
## c-accuracy 100% 100%
## Fit 100% 50.0%
## p-value 0.022 0.031
## p-accuracy 0.003 0.003
##
## Slope 50.000
## Intercept 5.500
## Abs. ineff. 5.860 5.860
## Rel. ineff. 99.660 99.660
## Condition ineff. 97.959 97.959
## Outcome ineff. 83.333 83.333
##
##
## --------------------------------------------------------------------------------
## --------------------------------------------------------------------------------
##
## Number of observations 582
## Scope 36
## Xmin 1
## Xmax 7
## Ymin 1
## Ymax 7
##
## ce_fdh cr_fdh
## Ceiling zone 0.000 0.000
## Effect size 0.000 0.000
## # above 0 0
## c-accuracy 100% 100%
## Fit 100% NA
## p-value 1.000 1.000
## p-accuracy 0.000 0.000
##
## Slope
## Intercept
## Abs. ineff.
## Rel. ineff.
## Condition ineff.
## Outcome ineff.
#if you have pdf=TRUE, then setwd(), wherein you wish to write the output.
#Calculate the p-value
NCAtest <- nca_analysis(DataIJIMAnalysis, c(1,2,3,5,6,7,8),4, test.rep = 10000)
## Preparing the analysis, this might take a few seconds...
## Do test for : ce_fdh - msg_qualDo test for : cr_fdh - msg_qualDo test for : ce_fdh - ben_prsnlDo test for : cr_fdh - ben_prsnlDo test for : ce_fdh - prsnl_qualDo test for : cr_fdh - prsnl_qualDo test for : ce_fdh - strong_negDo test for : cr_fdh - strong_negDo test for : ce_fdh - weak_negDo test for : cr_fdh - weak_negDo test for : ce_fdh - strong_posDo test for : cr_fdh - strong_posDo test for : ce_fdh - weak_posDo test for : cr_fdh - weak_pos
NCAtest
##
## --------------------------------------------------------------------------------
## ce_fdh p cr_fdh p
## msg_qual 0.13 0.000 0.13 0.000
## ben_prsnl 0.23 0.000 0.20 0.000
## prsnl_qual 0.12 0.000 0.10 0.000
## strong_neg 0.00 1.000 0.00 1.000
## weak_neg 0.00 1.000 0.00 1.000
## strong_pos 0.00 0.022 0.00 0.031
## weak_pos 0.00 1.000 0.00 1.000
## --------------------------------------------------------------------------------