Este laboratorio lleva a R el capítulo 4 de Oana, Schneider y Thomann (2021), que es el corazón de QCA: el análisis de suficiencia. Seguimos el orden del libro y usamos sus mismos ejemplos, con el script que los autores publicaron como referencia. Arrancamos con una condición a la vez. Después armamos la tabla de verdad y la minimizamos. Cerramos con el problema de la diversidad limitada, que el libro resuelve con el análisis estándar (SA) y el análisis estándar mejorado (ESA).
Necesitan los paquetes QCA y SetMethods. Si
no los tienen:
install.packages("SetMethods") # instala también QCA como dependencia
library(QCA)
library(SetMethods)
Todo el capítulo gira alrededor de una pregunta: ¿por qué algunos
jugadores del equipo de la coach Susan llegan a tiempo al entrenamiento
y otros no? El resultado es llegar a tiempo
(Y). Las condiciones cambian de sección en sección, pero
siempre salen de esta lista:
X: vive cerca de la cancha.A: llega en bicicleta y no en el carro de sus
papás.C: viene de una familia donde la puntualidad es una
norma.D: es un jugador con experiencia.E: es portero.El libro usa tres versiones de los datos. Para la suficiencia de una
sola condición trabaja con equipos pequeños e hipotéticos
(SF1 a SF4). Para la tabla de verdad usa 13
jugadores con tres condiciones (SF5). Para la diversidad
limitada usa 131 jugadores con las cinco condiciones, que vienen en
SetMethods con el nombre STUF. Vamos a
construir cada uno cuando lo necesitemos.
Evaluamos condiciones suficientes una por una con
xy.plot() y QCAfit(), cuidando la
consistencia, los casos DCK, el PRI y la cobertura. Construimos una
tabla de verdad con truthTable() y la minimizamos con
minimize(). Leímos la solución en los casos con
pimplot() y vimos que a veces hay más de un modelo. Con los
131 jugadores aparecieron los remanentes lógicos: el análisis estándar
da tres soluciones y el ESA, con LR.intersect() y
esa(), bloquea los supuestos que contradicen lo que
afirmamos en otras partes del análisis.
Una condición \(X\) es suficiente para \(Y\) si siempre que aparece \(X\) aparece también \(Y\). En conjuntos, \(X\) es un subconjunto de \(Y\) (\(X \subseteq Y\)), y lo escribimos \(X \rightarrow Y\). Con conjuntos difusos eso quiere decir que la pertenencia de cada caso en \(X\) es menor o igual a su pertenencia en \(Y\).
Empezamos con siete jugadores. X es vivir cerca y
Y es llegar a tiempo.
Name <- c("Amanda", "Bob", "Carl", "Cecilia", "Ana", "Alex", "Kim")
X <- c(0.6, 0.6, 0.2, 0.1, 0.9, 0.7, 0.3)
Y <- c(0.7, 0.6, 0.9, 0.3, 1, 0.8, 0.4)
SF1 <- data.frame(X, Y)
row.names(SF1) <- Name
La función xy.plot() de SetMethods pone la
condición en el eje horizontal y el resultado en el vertical. El
argumento clave es necessity = FALSE: le dice a R que
evalúe suficiencia y que calcule los parámetros que corresponden.
xy.plot("X", "Y",
data = SF1,
main = "XY plot - suficiencia",
ylab = "Llega a tiempo",
xlab = "Vive cerca",
necessity = FALSE,
fontsize = 6)
Todos los jugadores quedan sobre o encima de la diagonal: ninguno vive más cerca de lo que llega a tiempo. Esa es la huella de la suficiencia. Fíjense que en necesidad buscábamos lo contrario, casos debajo de la diagonal.
Agregamos tres jugadores a SF1: Mark, Dee y Fiona.
SF2 <- rbind(SF1, c(0.75, 0.65), c(0.55, 0.45), c(0.35, 0.25))
row.names(SF2)[8:10] <- c("Mark", "Dee", "Fiona")
xy.plot("X", "Y",
data = SF2,
main = "XY plot - suficiencia",
ylab = "Llega a tiempo",
xlab = "Vive cerca",
necessity = FALSE,
fontsize = 6)
Los tres nuevos quedan debajo de la diagonal. La consistencia de suficiencia resume cuánto se aparta el patrón de un subconjunto perfecto:
\[\text{Cons}_{suf} = \frac{\sum \min(X_i, Y_i)}{\sum X_i}\]
El numerador es el mismo que en necesidad. Cambia el denominador:
ahora sumamos la condición. QCAfit() la calcula si le
pedimos necessity = FALSE:
QCAfit(SF2$X, SF2$Y, cond.lab = "X", necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## 0.941 0.785 0.885
Una consistencia de 0.941 está muy por encima del umbral usual de 0.8 (o 0.75). Pero el número solo no basta. Los tres casos se alejan de la diagonal por la misma distancia (0.1) y aun así Dee pesa más en contra. Dee vive cerca (\(X > 0.5\)) y llega tarde (\(Y < 0.5\)): es un caso desviado de consistencia en tipo (DCK). Mark y Fiona tienen \(X\) y \(Y\) del mismo lado de 0.5, así que solo se desvían en grado (DCD).
Con conjuntos difusos, una misma condición puede verse suficiente para \(Y\) y para \(\sim Y\) al mismo tiempo. El libro lo muestra con un equipo donde casi nadie vive cerca.
Name <- c("Dan", "Claire", "Amanda", "Bob", "Carl", "Cecilia",
"Ana", "Mark", "Jim", "Joe")
X <- c(0.45, 0.2, 0.1, 0.2, 0.1, 0.4, 0.6, 0.25, 0.65, 0.6)
Y <- c(0.55, 0.9, 0.7, 0.4, 0.3, 0.3, 0.7, 0.1, 0.65, 0.35)
SF3 <- data.frame(X, Y)
row.names(SF3) <- Name
Graficamos X contra Y y luego contra
~Y. En xy.plot() basta con escribir la tilde
delante del nombre del resultado.
xy.plot("X", "Y",
data = SF3,
main = "Resultado Y",
ylab = "Llega a tiempo",
xlab = "Vive cerca",
necessity = FALSE,
fontsize = 6)
xy.plot("X", "~Y",
data = SF3,
main = "Resultado ~Y",
ylab = "NO llega a tiempo",
xlab = "Vive cerca",
necessity = FALSE,
fontsize = 6)
Los dos gráficos se parecen: como X es un conjunto tan
chico, casi todos los casos quedan arriba de la diagonal en ambos.
Veamos los números. Para ~Y le pasamos a
QCAfit() el complemento, 1 - SF3$Y:
QCAfit(SF3$X, SF3$Y, cond.lab = "X -> Y", necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## 0.859 0.616 0.583
QCAfit(SF3$X, 1 - SF3$Y, cond.lab = "X -> ~Y", necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## 0.803 0.564 0.417
Las dos consistencias pasan 0.8. Afirmar que vivir cerca basta para llegar a tiempo y también para llegar tarde sería absurdo. El PRI (reducción proporcional de la inconsistencia) resuelve el empate:
\[\text{PRI} = \frac{\sum \min(X_i, Y_i) - \sum \min(X_i, Y_i, \sim Y_i)}{\sum X_i - \sum \min(X_i, Y_i, \sim Y_i)}\]
Para Y el PRI es 0.583 y para ~Y es 0.417.
Sumen los dos: dan 1. El PRI es simétrico, así que mientras más se
acerca a 0.5, menos podemos decidir. Una condición con PRI menor a 0.5
no debe declararse suficiente. Los casos cuentan la misma historia: para
X -> Y hay un DCK (Joe) y para X -> ~Y
hay dos (Ana y Jim).
Una vez que una condición pasa consistencia, casos DCK y PRI, preguntamos cuánto del resultado explica. Esa es la cobertura de suficiencia:
\[\text{Cov}_{suf} = \frac{\sum \min(X_i, Y_i)}{\sum Y_i}\]
Comparamos vivir cerca (SF3) con llegar en bicicleta
(SF4). El resultado es el mismo en los dos casos.
X4 <- c(0, 0, 0.1, 0.05, 0.1, 0, 0.6, 0.25, 0.45, 0.1)
Y4 <- c(0.55, 0.9, 0.7, 0.4, 0.3, 0.3, 0.7, 0.1, 0.65, 0.35)
SF4 <- data.frame(X4, Y4)
row.names(SF4) <- Name
xy.plot("X", "Y",
data = SF3,
main = "Cobertura alta",
ylab = "Llega a tiempo",
xlab = "Vive cerca",
necessity = FALSE,
fontsize = 6)
xy.plot("X4", "Y4",
data = SF4,
main = "Cobertura baja",
ylab = "Llega a tiempo",
xlab = "Llega en bicicleta",
necessity = FALSE,
fontsize = 6)
QCAfit(SF3$X, SF3$Y, cond.lab = "Vive cerca", necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## 0.859 0.616 0.583
QCAfit(SF4$X4, SF4$Y4, cond.lab = "Bicicleta", necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## 0.909 0.303 0.727
La bicicleta es más consistente (0.909) pero cubre mucho menos (0.303 frente a 0.616). En el gráfico se nota porque los casos quedan lejos, arriba de la diagonal. Los más problemáticos son los de la esquina superior izquierda (\(X < 0.5\), \(Y > 0.5\)): Claire, Amanda, Dan y Jim llegan a tiempo, pero no en bicicleta. Son casos desviados de cobertura, puntualidad que esta condición no explica.
QCAfit()Ahora cargamos los datos grandes del libro. STUF trae
las columnas con nombres genéricos, así que las renombramos una vez con
las letras del capítulo. Así los nombres coinciden con los del resto del
laboratorio.
data("STUF")
names(STUF) <- c("X", "A", "C", "D", "E", "Y")
head(STUF)
QCAfit() funciona igual que en necesidad. Para una
condición:
QCAfit(STUF$X, STUF$Y, cond.lab = "X", necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## 0.800 0.885 0.724
Si le pasamos varias columnas, evalúa cada condición y su negación:
QCAfit(x = STUF[, 1:5], y = STUF$Y, necessity = FALSE)
## Cons.Suf Cov.Suf PRI
## X 0.8000 0.8853 0.7244
## A 0.7606 0.8577 0.6864
## C 0.7565 0.7896 0.6831
## D 0.3827 0.4333 0.1888
## E 0.7994 0.8975 0.7259
## ~X 0.2797 0.3382 0.0677
## ~A 0.2876 0.3417 0.0599
## ~C 0.3005 0.3822 0.0982
## ~D 0.5947 0.7039 0.5013
## ~E 0.2846 0.3396 0.0537
Ninguna condición sola destaca. X queda justo en 0.8 y
E se queda un milésimo abajo. Comparen X con
su negación:
xy.plot(x = "X", y = "Y",
data = STUF,
necessity = FALSE,
jitter = TRUE,
xlab = "X", ylab = "Y",
main = "Suficiencia de X para Y")
xy.plot(x = "~X", y = "Y",
data = STUF,
necessity = FALSE,
jitter = TRUE,
xlab = "~X", ylab = "Y",
main = "Suficiencia de ~X para Y")
Con 131 casos, jitter = TRUE mueve un poco las etiquetas
para que no se encimen. Que ninguna condición alcance por sí sola nos
empuja a mirar combinaciones de condiciones. Para eso
está la tabla de verdad.
Susan junta datos de 13 jugadores nuevos con tres condiciones: vivir
cerca (X), bicicleta (A) y familia puntual
(C). Es la tabla 4.3 del libro.
Name <- c("Amanda", "Bob", "Carl", "Cecilia", "Ana", "Alex", "Mark",
"Dee", "Fiona", "Kim", "Joe", "Jess", "Frank")
X <- c(0.6, 0.3, 0.8, 0, 0.9, 0.7, 0.75, 0.55, 0.35, 0.4, 0.4, 0.45, 0.6)
A <- c(0.8, 0.7, 1, 0.6, 0.1, 0.2, 0.7, 0.3, 0.7, 0.9, 0.4, 0.45, 0.35)
C <- c(0.7, 0.7, 0.3, 0.2, 0.2, 0.4, 0.6, 0.3, 0.2, 0.6, 0.4, 0.55, 0.6)
Y <- c(0.7, 0.8, 0.9, 0.3, 1, 0.8, 0.65, 0.45, 0.75, 0.6, 0.2, 0.2, 0.2)
SF5 <- data.frame(X, A, C, Y)
row.names(SF5) <- Name
truthTable()Todo lo que hicimos a mano en la sesión teórica (escribir las \(2^3 = 8\) filas, asignar cada caso a la
fila donde tiene pertenencia mayor a 0.5, decidir qué filas son
suficientes) lo hace truthTable() del paquete
QCA en un solo paso.
TT_y <- truthTable(data = SF5,
outcome = "Y",
conditions = c("X", "A", "C"),
incl.cut = 0.86,
pri.cut = 0.51,
n.cut = 1,
sort.by = "incl",
show.cases = TRUE)
TT_y
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C OUT n incl PRI cases
## 4 0 1 1 1 2 0.862 0.625 Bob,Kim
## 7 1 1 0 0 1 0.859 0.613 Carl
## 8 1 1 1 0 2 0.857 0.613 Amanda,Mark
## 5 1 0 0 0 3 0.830 0.643 Ana,Alex,Dee
## 3 0 1 0 0 2 0.800 0.438 Cecilia,Fiona
## 2 0 0 1 0 1 0.773 0.286 Jess
## 6 1 0 1 0 1 0.754 0.370 Frank
## 1 0 0 0 0 1 0.753 0.269 Joe
Cómo leer la salida:
000, la fila 8 es 111. Esta
numeración no coincide con la de la tabla 4.6 del libro, que empieza por
111.OUT dice si la fila es suficiente (1) o no (0).n cuenta los casos en la fila. incl es la
consistencia y PRI el PRI.Cuatro filas son suficientes para llegar a tiempo. Miren la fila 3
(~X*A*~C): su consistencia es exactamente 0.800, pero su
PRI (0.438) queda debajo de 0.51. R le asigna OUT = 0.
Con dcc = TRUE en vez de show.cases = TRUE,
la tabla muestra solo los casos desviados de consistencia en tipo de
cada fila:
truthTable(data = SF5,
outcome = "Y",
conditions = c("X", "A", "C"),
incl.cut = 0.8,
pri.cut = 0.51,
sort.by = "incl",
dcc = TRUE)
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C OUT n incl PRI
## 4 0 1 1 1 2 0.862 0.625
## 7 1 1 0 1 1 0.859 0.613
## 8 1 1 1 1 2 0.857 0.613
## 5 1 0 0 1 3 0.830 0.643
## 3 0 1 0 0 2 0.800 0.438
## 2 0 0 1 0 1 0.773 0.286
## 6 1 0 1 0 1 0.754 0.370
## 1 0 0 0 0 1 0.753 0.269
Dee aparece en la fila 5 (X*~A*~C). Esa fila entra como
suficiente con un DCK de tres casos. Cecilia es el DCK de la fila 3, la
que acabamos de dejar fuera.
Para exportar la tabla a un archivo, el libro sugiere dos caminos:
write.csv(TT_y$tt, "myTT.csv") # hoja de cálculo
stargazerTT(truthtable = TT_y, show.cases = TRUE,
type = "text", out = "myTT.txt") # texto, LaTeX o HTML
minimize()La tabla nos dice que cuatro combinaciones bastan para llegar a
tiempo. Escrita tal cual, la respuesta es larga y redundante.
minimize() aplica la regla de una diferencia y elimina los
implicantes primos que sobran.
sol_y <- minimize(input = TT_y,
details = TRUE)
sol_y
##
## M1: ~X*A*C -> Y
##
## inclS PRI covS covU cases
## ----------------------------------------------
## 1 ~X*A*C 0.862 0.625 0.497 - Bob,Kim
## ----------------------------------------------
## M1 0.862 0.625 0.497
La solución es X*~C + A*C -> Y: llega a tiempo quien
vive cerca y no viene de familia puntual, o quien llega en bicicleta y
sí viene de familia puntual. Para cada término, R reporta:
inclS: consistencia de suficiencia.PRI.covS: cobertura bruta.covU: cobertura única, la parte del resultado que solo
ese término explica.cases: casos con pertenencia mayor a 0.5 en el término.
La coma separa casos de la misma fila y el punto y coma separa filas
distintas.La última línea (M1) da los parámetros de la solución
completa. La cobertura de 0.834 nos dice que queda algo de puntualidad
sin explicar.
minimize() guarda la tabla de implicantes primos dentro
del objeto:
sol_y$PIchart
##
## 4
## ~X*A*C x
Las columnas son las filas suficientes de la tabla de verdad. Las
filas son los implicantes primos. X*A cubre las filas 7 y
8, pero X*~C ya cubre la 7 y A*C ya cubre la
8. Por eso X*A es redundante y no aparece en la
solución.
QCA es un método orientado a casos, y la solución hay que devolverla
a los casos. pimplot() de SetMethods hace
gráficos XY de filas de la tabla, de cada término suficiente y de la
solución completa. Necesita los datos, el resultado y el objeto que
produjo minimize().
Primero, la fila 5 (X*~A*~C), que el libro usa para
ilustrar el paso 4 del algoritmo:
pimplot(data = SF5,
outcome = "Y",
results = sol_y,
all_labels = TRUE,
ttrows = "5",
jitter = TRUE)
Ahora la fila 3 (~X*A*~C), la que quedó fuera por su
PRI:
pimplot(data = SF5,
outcome = "Y",
results = sol_y,
all_labels = TRUE,
ttrows = "3",
jitter = TRUE,
fontsize = 6)
Fiona confirma la suficiencia de esa fila y Cecilia la contradice. La decisión de dejarla fuera se sostiene.
Sin ttrows, pimplot() grafica cada término
y la solución completa:
pimplot(data = SF5,
outcome = "Y",
results = sol_y,
all_labels = TRUE,
jitter = TRUE,
fontsize = 6)
En los gráficos buscamos tres tipos de casos:
A*C. Alex, Ana y
Carl para X*~C.X*~C.A veces la minimización produce más de una fórmula igual de corta. El
libro lo ilustra con 47 jugadores de varios equipos y el resultado
no llegar a tiempo (~Y), en la tabla 4.9.
Los datos originales de esos 47 jugadores no vienen en
SetMethods. Reconstruimos solo lo que la minimización
necesita: las ocho combinaciones de X, A y
C, cada una con el OUT de la tabla 4.9.
TT47 <- data.frame(
X = c(0, 0, 0, 0, 1, 1, 1, 1),
A = c(0, 0, 1, 1, 0, 0, 1, 1),
C = c(0, 1, 0, 1, 0, 1, 0, 1),
Y = c(0, 0, 0, 1, 1, 0, 0, 1) # Y = 0 en las filas suficientes para ~Y
)
tt_ny <- truthTable(TT47,
outcome = "~Y",
conditions = c("X", "A", "C"),
incl.cut = 0.8)
tt_ny
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C OUT n incl PRI
## 1 0 0 0 1 1 1.000 1.000
## 2 0 0 1 1 1 1.000 1.000
## 3 0 1 0 1 1 1.000 1.000
## 4 0 1 1 0 1 0.000 0.000
## 5 1 0 0 0 1 0.000 0.000
## 6 1 0 1 1 1 1.000 1.000
## 7 1 1 0 1 1 1.000 1.000
## 8 1 1 1 0 1 0.000 0.000
Como cada fila tiene un solo caso crisp, las consistencias salen 1 o
0. Los valores de incl y PRI no son los del
libro. Las filas suficientes (1, 2, 3, 6 y 7) sí lo son, y eso es lo que
decide la solución.
sol_ny <- minimize(tt_ny)
sol_ny
##
## M1: ~A*C + A*~C + (~X*~A) <-> ~Y
## M2: ~A*C + A*~C + (~X*~C) <-> ~Y
R devuelve dos modelos, M1 y M2. Coinciden
en ~A*C y A*~C. El término entre paréntesis es
el que cambia: ~X*~A en uno, ~X*~C en el otro.
La flecha doble (<->) aparece porque en nuestra
reconstrucción la solución cubre todo el resultado sin inconsistencias;
con los datos del libro la flecha es simple.
sol_ny$PIchart
##
## 1 2 3 6 7
## ~X*~A x x - - -
## ~X*~C x - x - -
## ~A*C - x - x -
## A*~C - - x - x
~X*~A cubre las expresiones 1 y 2; ~X*~C
cubre la 1 y la 3. La 2 ya la cubre ~A*C y la 3 ya la cubre
A*~C. Cualquiera de los dos sirve y ninguno hace falta
junto al otro. Los datos no pueden decidir entre M1 y
M2: esa decisión viene de la teoría o de evidencia dentro
de los casos. Y la ambigüedad siempre se reporta.
Volvemos a STUF. Susan y otros entrenadores quieren
explicar ahora por qué algunos jugadores llegan tarde
(~Y). Con cinco condiciones hay \(2^5 = 32\) filas posibles.
conds <- c("X", "A", "C", "D", "E")
TT_ny <- truthTable(data = STUF,
outcome = "~Y",
conditions = conds,
incl.cut = 0.8,
n.cut = 2,
pri.cut = 0.5,
sort.by = c("OUT", "incl"),
complete = TRUE)
TT_ny
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C D E OUT n incl PRI
## 19 1 0 0 1 0 1 2 0.960 0.827
## 5 0 0 1 0 0 1 4 0.954 0.892
## 11 0 1 0 1 0 1 7 0.954 0.908
## 25 1 1 0 0 0 1 2 0.953 0.803
## 3 0 0 0 1 0 1 30 0.951 0.929
## 1 0 0 0 0 0 1 16 0.950 0.926
## 7 0 0 1 1 0 1 2 0.947 0.849
## 20 1 0 0 1 1 1 2 0.946 0.691
## 22 1 0 1 0 1 1 2 0.935 0.791
## 4 0 0 0 1 1 1 4 0.934 0.781
## 12 0 1 0 1 1 1 6 0.912 0.752
## 24 1 0 1 1 1 0 2 0.894 0.490
## 28 1 1 0 1 1 0 4 0.806 0.405
## 32 1 1 1 1 1 0 5 0.650 0.175
## 30 1 1 1 0 1 0 36 0.285 0.055
## 17 1 0 0 0 0 ? 1 0.969 0.890
## 21 1 0 1 0 0 ? 1 0.963 0.855
## 13 0 1 1 0 0 ? 1 0.950 0.752
## 15 0 1 1 1 0 ? 1 0.940 0.755
## 6 0 0 1 0 1 ? 1 0.927 0.772
## 26 1 1 0 0 1 ? 1 0.910 0.611
## 14 0 1 1 0 1 ? 1 0.898 0.565
## 2 0 0 0 0 1 ? 0 - -
## 8 0 0 1 1 1 ? 0 - -
## 9 0 1 0 0 0 ? 0 - -
## 10 0 1 0 0 1 ? 0 - -
## 16 0 1 1 1 1 ? 0 - -
## 18 1 0 0 0 1 ? 0 - -
## 23 1 0 1 1 0 ? 0 - -
## 27 1 1 0 1 0 ? 0 - -
## 29 1 1 1 0 0 ? 0 - -
## 31 1 1 1 1 0 ? 0 - -
Dos argumentos nuevos:
n.cut = 2: con 131 jugadores, los entrenadores exigen
al menos dos casos por fila. Una fila con un solo caso pasa a ser
remanente.complete = TRUE: imprime también las filas sin
casos.Las filas con OUT = ? son remanentes
lógicos. Hay 17: siete con un caso y diez sin ninguno. No
sabemos si esas combinaciones bastarían para llegar tarde. El análisis
estándar produce tres soluciones que se distinguen por qué hacen con
esos remanentes.
sol_nyc <- minimize(TT_ny,
details = TRUE)
sol_nyc
##
## M1: ~X*~A*~E + ~X*~C*D + ~A*~C*D + X*~A*C*~D*E + X*A*~C*~D*~E -> ~Y
##
## inclS PRI covS covU
## -------------------------------------------
## 1 ~X*~A*~E 0.942 0.922 0.674 0.182
## 2 ~X*~C*D 0.913 0.881 0.541 0.065
## 3 ~A*~C*D 0.905 0.862 0.504 0.020
## 4 X*~A*C*~D*E 0.935 0.791 0.125 0.022
## 5 X*A*~C*~D*~E 0.953 0.803 0.114 0.008
## -------------------------------------------
## M1 0.900 0.868 0.823
Cinco caminos para llegar tarde, algunos con cinco condiciones. Es la
descripción más detallada y la más difícil de interpretar. Guarden el
término 5 (X*A*~C*~D*~E): contiene X*A y va a
volver en la sección 7.
Con include = "?", R puede tratar como suficiente
cualquier remanente que ayude a simplificar. De los 17 remanentes, R
elige solo los que acortan la fórmula.
sol_nyp <- minimize(TT_ny,
details = TRUE,
include = "?",
row.dom = TRUE)
sol_nyp
##
## M1: ~X + ~E + ~A*~C + ~A*~D -> ~Y
##
## inclS PRI covS covU
## ------------------------------------
## 1 ~X 0.905 0.877 0.832 0.033
## 2 ~E 0.914 0.886 0.829 0.024
## 3 ~A*~C 0.905 0.870 0.676 0.007
## 4 ~A*~D 0.927 0.897 0.398 0.021
## ------------------------------------
## M1 0.875 0.839 0.929
row.dom = TRUE evita aquí la ambigüedad de modelos; no
tiene que ver con los remanentes. Los remanentes que R usó son los
supuestos simplificadores:
sol_nyp$SA
## $M1
## X A C D E
## 2 0 0 0 0 1
## 6 0 0 1 0 1
## 8 0 0 1 1 1
## 9 0 1 0 0 0
## 10 0 1 0 0 1
## 13 0 1 1 0 0
## 14 0 1 1 0 1
## 15 0 1 1 1 0
## 16 0 1 1 1 1
## 17 1 0 0 0 0
## 18 1 0 0 0 1
## 21 1 0 1 0 0
## 23 1 0 1 1 0
## 27 1 1 0 1 0
## 29 1 1 1 0 0
## 31 1 1 1 1 0
Dieciséis supuestos. Cada uno afirma que un tipo de jugador que no observamos llegaría tarde.
La solución intermedia usa solo los supuestos simplificadores que
coinciden con nuestras expectativas direccionales. Los
entrenadores esperan que todas las condiciones contribuyan a llegar
tarde cuando están ausentes, salvo D: creen que los
jugadores con experiencia llegan tarde porque saben que no hay
sanciones. Eso se escribe en dir.exp:
sol_nyi <- minimize(TT_ny,
details = TRUE,
include = "?",
dir.exp = "~X, ~A, ~C, ~D, ~E",
row.dom = TRUE)
sol_nyi
##
## From C1P1:
##
## M1: ~X*~C + ~A*~C + ~A*~D + ~X*~A*~E + ~C*~D*~E -> ~Y
##
## inclS PRI covS covU
## ---------------------------------------
## 1 ~X*~C 0.911 0.884 0.730 0.089
## 2 ~A*~C 0.905 0.870 0.676 0.022
## 3 ~A*~D 0.927 0.897 0.398 0.043
## 4 ~X*~A*~E 0.942 0.922 0.674 0.018
## 5 ~C*~D*~E 0.937 0.904 0.312 0.011
## ---------------------------------------
## M1 0.892 0.860 0.889
Los supuestos que pasaron el filtro son los contrafácticos fáciles:
sol_nyi$i.sol$C1P1$EC
Siete de los 16. C1P1 es el nombre que R le da a la
combinación de la solución conservadora 1 con la parsimoniosa 1. Las
tres soluciones están en una relación de subconjuntos: la conservadora
está dentro de la intermedia, y la intermedia dentro de la más
parsimoniosa.
El análisis de suficiencia es asimétrico, así que Y y
~Y se analizan por separado. Necesitamos las soluciones
para Y antes de pasar al ESA.
TT_y <- truthTable(data = STUF,
outcome = "Y",
conditions = conds,
incl.cut = 0.8,
n.cut = 2,
pri.cut = 0.5,
sort.by = c("OUT", "incl"),
complete = TRUE,
show.cases = FALSE)
TT_y
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C D E OUT n incl PRI
## 30 1 1 1 0 1 1 36 0.929 0.906
## 32 1 1 1 1 1 1 5 0.925 0.824
## 28 1 1 0 1 1 1 4 0.846 0.525
## 24 1 0 1 1 1 0 2 0.890 0.470
## 20 1 0 0 1 1 0 2 0.873 0.281
## 19 1 0 0 1 0 0 2 0.809 0.173
## 25 1 1 0 0 0 0 2 0.807 0.192
## 4 0 0 0 1 1 0 4 0.765 0.219
## 22 1 0 1 0 1 0 2 0.753 0.209
## 12 0 1 0 1 1 0 6 0.734 0.248
## 7 0 0 1 1 0 0 2 0.703 0.151
## 5 0 0 1 0 0 0 4 0.617 0.100
## 11 0 1 0 1 0 0 7 0.537 0.083
## 1 0 0 0 0 0 0 16 0.361 0.060
## 3 0 0 0 1 0 0 30 0.332 0.036
## 14 0 1 1 0 1 ? 1 0.868 0.435
## 26 1 1 0 0 1 ? 1 0.855 0.375
## 13 0 1 1 0 0 ? 1 0.850 0.248
## 15 0 1 1 1 0 ? 1 0.806 0.215
## 21 1 0 1 0 0 ? 1 0.785 0.145
## 6 0 0 1 0 1 ? 1 0.755 0.228
## 17 1 0 0 0 0 ? 1 0.747 0.110
## 2 0 0 0 0 1 ? 0 - -
## 8 0 0 1 1 1 ? 0 - -
## 9 0 1 0 0 0 ? 0 - -
## 10 0 1 0 0 1 ? 0 - -
## 16 0 1 1 1 1 ? 0 - -
## 18 1 0 0 0 1 ? 0 - -
## 23 1 0 1 1 0 ? 0 - -
## 27 1 1 0 1 0 ? 0 - -
## 29 1 1 1 0 0 ? 0 - -
## 31 1 1 1 1 0 ? 0 - -
El PRI de corte baja a 0.45 para que la fila 24 entre. Los autores lo
hacen con fines de presentación: con 0.5 la solución para Y
queda demasiado simple.
sol_yc <- minimize(TT_y,
details = TRUE)
sol_yc
##
## M1: X*A*C*E + X*A*D*E -> Y
##
## inclS PRI covS covU
## --------------------------------------
## 1 X*A*C*E 0.921 0.894 0.724 0.503
## 2 X*A*D*E 0.882 0.742 0.290 0.070
## --------------------------------------
## M1 0.905 0.870 0.794
sol_yp <- minimize(TT_y,
details = TRUE,
include = "?",
row.dom = TRUE)
sol_yp
##
## M1: X*A*E -> Y
##
## inclS PRI covS covU
## ------------------------------------
## 1 X*A*E 0.901 0.864 0.800 -
## ------------------------------------
## M1 0.901 0.864 0.800
sol_yp$SA
## $M1
## X A C D E
## 26 1 1 0 0 1
sol_yi <- minimize(TT_y,
details = TRUE,
include = "?",
dir.exp = c(1, 1, 1, 0, 1))
sol_yi
##
## From C1P1:
##
## M1: X*A*E -> Y
##
## inclS PRI covS covU
## ------------------------------------
## 1 X*A*E 0.901 0.864 0.800 -
## ------------------------------------
## M1 0.901 0.864 0.800
Aquí las expectativas direccionales van como vector de unos y ceros,
en el orden de conds. Es otra forma de escribir lo mismo.
Revisamos los contrafácticos fáciles de los dos caminos:
sol_yi$i.sol$C1P1$EC
sol_yi$i.sol$C1P2$EC
## NULL
Hay uno solo, la fila 26.
El análisis estándar no impide que hagamos supuestos insostenibles sobre los remanentes. El ESA los identifica y los bloquea antes de minimizar. Hay tres fuentes, y las vamos a ir sumando una por una.
Un remanente usado como supuesto simplificador para Y y
para ~Y a la vez implica que esa combinación basta para
llegar a tiempo y para llegar tarde. LR.intersect() busca
los remanentes compartidos entre dos soluciones:
CSA <- LR.intersect(sol_nyp, sol_yp)
CSA
## character(0)
Es lo mismo que intersectar a mano los nombres de las filas:
intersect(rownames(sol_nyp$SA$M1), rownames(sol_yp$SA$M1))
## character(0)
Las filas 23 y 31 son supuestos simplificadores contradictorios (CSA). Para las soluciones intermedias revisamos los contrafácticos fáciles:
LR.intersect(sol_nyi, sol_yi)
## character(0)
Ninguno. La intermedia para Y usa un solo contrafáctico
fácil (la fila 26) y no aparece en la de ~Y.
esa() de SetMethods crea una tabla nueva
donde las filas bloqueadas pasan a OUT = 0. Como nos
interesa sobre todo ~Y, bloqueamos los CSA ahí:
TT_nyesa <- esa(oldtt = TT_ny,
contrad_rows = c(CSA))
TT_nyesa
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C D E OUT n incl PRI
## 19 1 0 0 1 0 1 2 0.960 0.827
## 5 0 0 1 0 0 1 4 0.954 0.892
## 11 0 1 0 1 0 1 7 0.954 0.908
## 25 1 1 0 0 0 1 2 0.953 0.803
## 3 0 0 0 1 0 1 30 0.951 0.929
## 1 0 0 0 0 0 1 16 0.950 0.926
## 7 0 0 1 1 0 1 2 0.947 0.849
## 20 1 0 0 1 1 1 2 0.946 0.691
## 22 1 0 1 0 1 1 2 0.935 0.791
## 4 0 0 0 1 1 1 4 0.934 0.781
## 12 0 1 0 1 1 1 6 0.912 0.752
## 24 1 0 1 1 1 0 2 0.894 0.490
## 28 1 1 0 1 1 0 4 0.806 0.405
## 32 1 1 1 1 1 0 5 0.650 0.175
## 30 1 1 1 0 1 0 36 0.285 0.055
## 17 1 0 0 0 0 ? 1 0.969 0.890
## 21 1 0 1 0 0 ? 1 0.963 0.855
## 13 0 1 1 0 0 ? 1 0.950 0.752
## 15 0 1 1 1 0 ? 1 0.940 0.755
## 6 0 0 1 0 1 ? 1 0.927 0.772
## 26 1 1 0 0 1 ? 1 0.910 0.611
## 14 0 1 1 0 1 ? 1 0.898 0.565
## 2 0 0 0 0 1 ? 0 - -
## 8 0 0 1 1 1 ? 0 - -
## 9 0 1 0 0 0 ? 0 - -
## 10 0 1 0 0 1 ? 0 - -
## 16 0 1 1 1 1 ? 0 - -
## 18 1 0 0 0 1 ? 0 - -
## 23 1 0 1 1 0 ? 0 - -
## 27 1 1 0 1 0 ? 0 - -
## 29 1 1 1 0 0 ? 0 - -
## 31 1 1 1 1 0 ? 0 - -
Comparen con TT_ny: las filas 23 y 31 pasaron de
? a 0.
Lo mismo puede pasar con filas observadas: una fila con
OUT = 1 para Y y para ~Y. Lo
revisamos:
SSR <- intersect(rownames(TT_ny$tt)[TT_ny$tt$OUT == 1],
rownames(TT_y$tt)[TT_y$tt$OUT == 1])
SSR
## character(0)
No hay ninguna, gracias al corte de PRI. Si las hubiera, se bloquean
también con contrad_rows.
Los entrenadores hicieron antes el análisis de necesidad para
~Y, con superSubset() (capítulo 3):
SUIN_ny <- superSubset(data = STUF,
outcome = "~Y",
conditions = conds,
incl.cut = 0.906,
cov.cut = 0.8,
ron.cut = 0.8,
depth = 2)
SUIN_ny
##
## inclN RoN covN
## -------------------------------
## 1 ~X + ~A 0.910 0.839 0.869
## -------------------------------
~X + ~A es necesaria para llegar tarde. Las dos
condiciones son SUIN y las interpretamos como un concepto más amplio: la
cancha no queda a mano.
pimplot(data = STUF,
outcome = "~Y",
results = SUIN_ny,
necessity = TRUE)
Si ~X + ~A es necesaria, ningún camino suficiente hacia
~Y puede prescindir de ella. Las filas que la contradicen
son las que tienen X*A. Entre los supuestos simplificadores
hay tres así (27, 29 y 31), y la fila 27 aparece incluso entre los
contrafácticos fáciles. Además, la fila observada 25
(X*A*~C*~D*~E) tiene OUT = 1: es el término 5
de la solución conservadora.
El argumento nec_cond pone en OUT = 0 todas
las filas con X*A, remanentes y observadas:
TT_nyesa <- esa(TT_ny,
contrad_rows = c(CSA),
nec_cond = "~X+~A")
TT_nyesa
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C D E OUT n incl PRI
## 19 1 0 0 1 0 1 2 0.960 0.827
## 5 0 0 1 0 0 1 4 0.954 0.892
## 11 0 1 0 1 0 1 7 0.954 0.908
## 25 1 1 0 0 0 0 2 0.953 0.803
## 3 0 0 0 1 0 1 30 0.951 0.929
## 1 0 0 0 0 0 1 16 0.950 0.926
## 7 0 0 1 1 0 1 2 0.947 0.849
## 20 1 0 0 1 1 1 2 0.946 0.691
## 22 1 0 1 0 1 1 2 0.935 0.791
## 4 0 0 0 1 1 1 4 0.934 0.781
## 12 0 1 0 1 1 1 6 0.912 0.752
## 24 1 0 1 1 1 0 2 0.894 0.490
## 28 1 1 0 1 1 0 4 0.806 0.405
## 32 1 1 1 1 1 0 5 0.650 0.175
## 30 1 1 1 0 1 0 36 0.285 0.055
## 17 1 0 0 0 0 ? 1 0.969 0.890
## 21 1 0 1 0 0 ? 1 0.963 0.855
## 13 0 1 1 0 0 ? 1 0.950 0.752
## 15 0 1 1 1 0 ? 1 0.940 0.755
## 6 0 0 1 0 1 ? 1 0.927 0.772
## 26 1 1 0 0 1 0 1 0.910 0.611
## 14 0 1 1 0 1 ? 1 0.898 0.565
## 2 0 0 0 0 1 ? 0 - -
## 8 0 0 1 1 1 ? 0 - -
## 9 0 1 0 0 0 ? 0 - -
## 10 0 1 0 0 1 ? 0 - -
## 16 0 1 1 1 1 ? 0 - -
## 18 1 0 0 0 1 ? 0 - -
## 23 1 0 1 1 0 ? 0 - -
## 27 1 1 0 1 0 0 0 - -
## 29 1 1 1 0 0 0 0 - -
## 31 1 1 1 1 0 0 0 - -
Algunas combinaciones no pueden existir. Para el ejemplo, el libro
supone que es imposible un jugador que no vive cerca, llega en
bicicleta, no viene de familia puntual y no tiene experiencia
(~X*A*~C*~D). El argumento untenable_LR
bloquea esas filas:
TT_nyesa <- esa(TT_ny,
contrad_rows = c(CSA),
nec_cond = "~X+~A",
untenable_LR = "~X*A*~C*~D")
TT_nyesa
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C D E OUT n incl PRI
## 19 1 0 0 1 0 1 2 0.960 0.827
## 5 0 0 1 0 0 1 4 0.954 0.892
## 11 0 1 0 1 0 1 7 0.954 0.908
## 25 1 1 0 0 0 0 2 0.953 0.803
## 3 0 0 0 1 0 1 30 0.951 0.929
## 1 0 0 0 0 0 1 16 0.950 0.926
## 7 0 0 1 1 0 1 2 0.947 0.849
## 20 1 0 0 1 1 1 2 0.946 0.691
## 22 1 0 1 0 1 1 2 0.935 0.791
## 4 0 0 0 1 1 1 4 0.934 0.781
## 12 0 1 0 1 1 1 6 0.912 0.752
## 24 1 0 1 1 1 0 2 0.894 0.490
## 28 1 1 0 1 1 0 4 0.806 0.405
## 32 1 1 1 1 1 0 5 0.650 0.175
## 30 1 1 1 0 1 0 36 0.285 0.055
## 17 1 0 0 0 0 ? 1 0.969 0.890
## 21 1 0 1 0 0 ? 1 0.963 0.855
## 13 0 1 1 0 0 ? 1 0.950 0.752
## 15 0 1 1 1 0 ? 1 0.940 0.755
## 6 0 0 1 0 1 ? 1 0.927 0.772
## 26 1 1 0 0 1 0 1 0.910 0.611
## 14 0 1 1 0 1 ? 1 0.898 0.565
## 2 0 0 0 0 1 ? 0 - -
## 8 0 0 1 1 1 ? 0 - -
## 9 0 1 0 0 0 0 0 - -
## 10 0 1 0 0 1 0 0 - -
## 16 0 1 1 1 1 ? 0 - -
## 18 1 0 0 0 1 ? 0 - -
## 23 1 0 1 1 0 ? 0 - -
## 27 1 1 0 1 0 0 0 - -
## 29 1 1 1 0 0 0 0 - -
## 31 1 1 1 1 0 0 0 - -
De los 17 remanentes originales quedan 10 disponibles. Con
stargazerTT() esta tabla se exporta igual que cualquier
otra.
Empezamos por la conservadora. Parece extraño, porque la conservadora
no usa remanentes, pero nec_cond también apagó la fila
observada 25.
sol_nycesa <- minimize(TT_nyesa,
details = TRUE)
sol_nycesa
##
## M1: ~X*~A*~E + ~X*~C*D + ~A*~C*D + X*~A*C*~D*E -> ~Y
##
## inclS PRI covS covU
## ------------------------------------------
## 1 ~X*~A*~E 0.942 0.922 0.674 0.191
## 2 ~X*~C*D 0.913 0.881 0.541 0.067
## 3 ~A*~C*D 0.905 0.862 0.504 0.024
## 4 X*~A*C*~D*E 0.935 0.791 0.125 0.024
## ------------------------------------------
## M1 0.901 0.868 0.815
Quedan cuatro términos y todos contienen ~X o
~A. El término con X*A desapareció.
sol_nypesa <- minimize(TT_nyesa,
details = TRUE,
include = "?",
row.dom = TRUE)
sol_nypesa
##
## M1: ~X*D + ~A*~C + ~A*~D -> ~Y
##
## inclS PRI covS covU
## ------------------------------------
## 1 ~X*D 0.908 0.874 0.571 0.092
## 2 ~A*~C 0.905 0.870 0.676 0.072
## 3 ~A*~D 0.927 0.897 0.398 0.085
## ------------------------------------
## M1 0.893 0.860 0.858
sol_nypesa$SA
## $M1
## X A C D E
## 2 0 0 0 0 1
## 6 0 0 1 0 1
## 8 0 0 1 1 1
## 15 0 1 1 1 0
## 16 0 1 1 1 1
## 17 1 0 0 0 0
## 18 1 0 0 0 1
## 21 1 0 1 0 0
Ningún supuesto simplificador contiene X*A, ninguno es
contradictorio y ninguno cae en ~X*A*~C*~D.
sol_nyiesa <- minimize(TT_nyesa,
details = TRUE,
include = "?",
dir.exp = "~X, ~A, ~C, D, ~E")
sol_nyiesa
##
## From C1P1:
##
## M1: ~A*~C + ~A*~D + ~X*~A*~E + ~X*~C*D -> ~Y
##
## inclS PRI covS covU
## ---------------------------------------
## 1 ~A*~C 0.905 0.870 0.676 0.026
## 2 ~A*~D 0.927 0.897 0.398 0.043
## 3 ~X*~A*~E 0.942 0.922 0.674 0.018
## 4 ~X*~C*D 0.913 0.881 0.541 0.067
## ---------------------------------------
## M1 0.894 0.862 0.851
sol_nyiesa$i.sol$C1P1$EC
Para ver qué contrafácticos fáciles sobrevivieron al ESA, intersectamos la intermedia estándar con la mejorada:
tenable_EC <- LR.intersect(sol_nyi, sol_nyiesa)
tenable_EC
## [1] "2" "6" "17" "18" "21"
Las filas 2, 6, 17, 18 y 21 eran contrafácticos fáciles y sostenibles. Las filas 9 y 27 eran fáciles, pero insostenibles: la 27 contradice la necesidad y la 9 es un remanente imposible. El ESA las sacó.
SF5, suban incl.cut a 0.86. ¿Qué filas
dejan de ser suficientes? ¿Cómo cambia la solución y qué pasa con su
cobertura?SF5, analicen ~Y con los mismos
umbrales. ¿Qué fila es suficiente para llegar tarde y no lo era para
llegar a tiempo? ¿Qué casos tiene?STUF, repitan TT_y con
pri.cut = 0.5. ¿Qué fila sale y cómo queda la solución más
parsimoniosa?D a ~D en
sol_nyi. ¿Qué contrafácticos fáciles aparecen o
desaparecen? ¿Alguno contradice la necesidad de
~X + ~A?Oana, I.-E., Schneider, C. Q., y Thomann, E. (2021). Qualitative Comparative Analysis Using R: A Beginner’s Guide, cap. 4. Cambridge University Press.
tt_tarde <- truthTable(
data = SF5,
outcome = "~Y",
conditions = c("X", "A", "C"),
incl.cut = 0.86,
pri.cut = 0.51,
show.cases = TRUE
)
tt_tarde
##
## OUT: output value
## n: number of cases in configuration
## incl: sufficiency inclusion score
## PRI: proportional reduction in inconsistency
##
## X A C OUT n incl PRI cases
## 1 0 0 0 1 1 0.909 0.731 Joe
## 2 0 0 1 1 1 0.909 0.714 Jess
## 3 0 1 0 0 2 0.844 0.562 Cecilia,Fiona
## 4 0 1 1 0 2 0.770 0.375 Bob,Kim
## 5 1 0 0 0 3 0.693 0.357 Ana,Alex,Dee
## 6 1 0 1 0 1 0.855 0.630 Frank
## 7 1 1 0 0 1 0.776 0.387 Carl
## 8 1 1 1 0 2 0.774 0.387 Amanda,Mark