Solo para recapitular (y en caso de que los hipervínculos OP fallen en el futuro), estamos viendo un conjunto de datos hsb2
como tal:
id female race ses schtyp prog read write math science socst
1 70 0 4 1 1 1 57 52 41 47 57
2 121 1 4 2 1 3 68 59 53 63 61
...
199 118 1 4 2 1 1 55 62 58 58 61
200 137 1 4 3 1 2 63 65 65 53 61
que se puede importar aquí .
Convertimos la variable read
en variable ordenada / ordinal:
hsb2$readcat<-cut(hsb2$read, 4, ordered = TRUE)
(means = tapply(hsb2$write, hsb2$readcat, mean))
(28,40] (40,52] (52,64] (64,76]
42.77273 49.97849 56.56364 61.83333
Ahora estamos todo listo para ejecutar un solo ANOVA regulares - sí, es R, y que básicamente tienen una variable dependiente continua, write
y una variable explicativa con múltiples niveles, readcat
. En R podemos usarlm(write ~ readcat, hsb2)
1. Generando la matriz de contraste:
Hay cuatro niveles diferentes para la variable ordenada readcat
, por lo que tendremos contrastes.n−1=3
table(hsb2$readcat)
(28,40] (40,52] (52,64] (64,76]
22 93 55 30
Primero, vamos por el dinero y echemos un vistazo a la función R incorporada:
contr.poly(4)
.L .Q .C
[1,] -0.6708204 0.5 -0.2236068
[2,] -0.2236068 -0.5 0.6708204
[3,] 0.2236068 -0.5 -0.6708204
[4,] 0.6708204 0.5 0.2236068
Ahora analicemos lo que sucedió debajo del capó:
scores = 1:4 # 1 2 3 4 These are the four levels of the explanatory variable.
y = scores - mean(scores) # scores - 2.5
y=[−1.5,−0.5,0.5,1.5]
seq_len(n) - 1=[0,1,2,3]
n = 4; X <- outer(y, seq_len(n) - 1, "^") # n = 4 in this case
⎡⎣⎢⎢⎢⎢1111−1.5−0.50.51.52.250.250.252.25−3.375−0.1250.1253.375⎤⎦⎥⎥⎥⎥
¿Que paso ahi? el outer(a, b, "^")
eleva los elementos de a
a los elementos de b
, de modo que la primera columna resulta de las operaciones, , ( - 0.5 ) 0 , 0.5 0 y 1.5 0 ; la segunda columna de ( - 1.5 ) 1 , ( - 0.5 ) 1 , 0.5 1 y 1.5 1 ; el tercero de ( - 1.5 ) 2 = 2.25(−1.5)0(−0.5)00.501.50(−1.5)1(−0.5)10.511.51(−1.5)2=2.25, , 0.5 2 = 0.25 y 1.5 2 = 2.25 ; y el cuarto, ( - 1.5 ) 3 = - 3.375 , ( - 0.5 ) 3 = - 0.125 , 0.5 3 = 0.125 y 1.5 3 = 3.375 .(−0.5)2=0.250.52=0.251.52=2.25(−1.5)3=−3.375(−0.5)3=−0.1250.53=0.1251.53=3.375
A continuación, hacemos una descomposición ortonormal de esta matriz y tomamos la representación compacta de Q ( ). Algunas de las funciones internas de las funciones utilizadas en la factorización QR en R utilizadas en esta publicación se explican más aquí .QRc_Q = qr(X)$qr
⎡⎣⎢⎢⎢⎢−20.50.50.50−2.2360.4470.894−2.502−0.92960−4.5840−1.342⎤⎦⎥⎥⎥⎥
... de los cuales solo guardamos la diagonal ( z = c_Q * (row(c_Q) == col(c_Q))
). Lo que se encuentra en la diagonal: solo las entradas "inferiores" de la parte de la descomposición Q R. ¿Sólo? bueno, no ... ¡Resulta que la diagonal de una matriz triangular superior contiene los valores propios de la matriz!RQR
A continuación, llamamos a la siguiente función: raw = qr.qy(qr(X), z)
cuyo resultado puede replicarse "manualmente" mediante dos operaciones: 1. Convertir la forma compacta de , es decir , en Q , una transformación que se puede lograr con , y 2. Llevar a cabo el multiplicación matricial Q z , como en .Qqr(X)$qr
QQ = qr.Q(qr(X))
QzQ %*% z
Crucialmente, multiplicar por los valores propios de R no cambia la ortogonalidad de los vectores de la columna constituyente, pero dado que el valor absoluto de los valores propios aparece en orden decreciente desde la parte superior izquierda a la parte inferior derecha, la multiplicación de Q z tenderá a disminuir valores en las columnas polinómicas de orden superior:QRQz
Matrix of Eigenvalues of R
[,1] [,2] [,3] [,4]
[1,] -2 0.000000 0 0.000000
[2,] 0 -2.236068 0 0.000000
[3,] 0 0.000000 2 0.000000
[4,] 0 0.000000 0 -1.341641
Compare los valores en los vectores de columna posteriores (cuadráticos y cúbicos) antes y después de las operaciones de factorización , y con las primeras dos columnas no afectadas.QR
Before QR factorization operations (orthogonal col. vec.)
[,1] [,2] [,3] [,4]
[1,] 1 -1.5 2.25 -3.375
[2,] 1 -0.5 0.25 -0.125
[3,] 1 0.5 0.25 0.125
[4,] 1 1.5 2.25 3.375
After QR operations (equally orthogonal col. vec.)
[,1] [,2] [,3] [,4]
[1,] 1 -1.5 1 -0.295
[2,] 1 -0.5 -1 0.885
[3,] 1 0.5 -1 -0.885
[4,] 1 1.5 1 0.295
Finalmente llamamos a (Z <- sweep(raw, 2L, apply(raw, 2L, function(x) sqrt(sum(x^2))), "/", check.margin = FALSE))
convertir la matriz raw
en vectores ortonormales :
Orthonormal vectors (orthonormal basis of R^4)
[,1] [,2] [,3] [,4]
[1,] 0.5 -0.6708204 0.5 -0.2236068
[2,] 0.5 -0.2236068 -0.5 0.6708204
[3,] 0.5 0.2236068 -0.5 -0.6708204
[4,] 0.5 0.6708204 0.5 0.2236068
Esta función simplemente "normaliza" la matriz dividiendo ( "/"
) en columna cada elemento por el . Por lo tanto, se puede descomponer en dos pasos:(i), lo que resulta en, que son los denominadores de cada columna en(ii),donde cada elemento de una columna se divide por el valor correspondiente de(i).∑col.x2i−−−−−−−√(i) apply(raw, 2, function(x)sqrt(sum(x^2)))
2 2.236 2 1.341
(ii)(i)
En este punto, los vectores de columna forman una base ortonormal de , hasta que nos deshacemos de la primera columna, que será la intersección, y hemos reproducido el resultado de :R4contr.poly(4)
⎡⎣⎢⎢⎢⎢−0.6708204−0.22360680.22360680.67082040.5−0.5−0.50.5−0.22360680.6708204−0.67082040.2236068⎤⎦⎥⎥⎥⎥
Las columnas de esta matriz son ortonormales , como se puede mostrar por (sum(Z[,3]^2))^(1/4) = 1
y z[,3]%*%z[,4] = 0
, por ejemplo (por cierto, lo mismo ocurre con las filas). Y, cada columna es el resultado de elevar las iniciales , es decir, la media de la potencia 1 , 2 y 3 , respectivamente, es decir , lineal, cuadrática y cúbica .scores - mean123
2. ¿Qué contrastes (columnas) contribuyen significativamente a explicar las diferencias entre niveles en la variable explicativa?
Podemos ejecutar el ANOVA y ver el resumen ...
summary(lm(write ~ readcat, hsb2))
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 52.7870 0.6339 83.268 <2e-16 ***
readcat.L 14.2587 1.4841 9.607 <2e-16 ***
readcat.Q -0.9680 1.2679 -0.764 0.446
readcat.C -0.1554 1.0062 -0.154 0.877
... para ver que hay un efecto lineal de readcat
on write
, de modo que los valores originales (en el tercer fragmento de código al comienzo de la publicación) se pueden reproducir como:
coeff = coefficients(lm(write ~ readcat, hsb2))
C = contr.poly(4)
(recovered = c(coeff %*% c(1, C[1,]),
coeff %*% c(1, C[2,]),
coeff %*% c(1, C[3,]),
coeff %*% c(1, C[4,])))
[1] 42.77273 49.97849 56.56364 61.83333
... o ...
... o mucho mejor ...
Al ser contrastes ortogonales, la suma de sus componentes se suma a cero para a 1 , ⋯ , a t constantes, y el producto punto de cualquiera de los dos es cero. Si pudiéramos visualizarlos, se verían así:∑i=1tai=0a1,⋯,at
La idea detrás del contraste ortogonal es que las inferencias que podemos extraer (en este caso generar coeficientes mediante una regresión lineal) serán el resultado de aspectos independientes de los datos. Este no sería el caso si simplemente usáramos como contrastes.X0,X1,⋯.Xn
Gráficamente, esto es mucho más fácil de entender. Compare las medias reales por grupos en grandes bloques negros cuadrados con los valores pronosticados, y vea por qué una aproximación de línea recta con una contribución mínima de polinomios cuadráticos y cúbicos (con curvas solo aproximadas con loess) es óptima:
Si, solo por efecto, los coeficientes del ANOVA hubieran sido tan grandes para el contraste lineal para las otras aproximaciones (cuadrática y cúbica), la gráfica sin sentido que sigue representaría más claramente las gráficas polinómicas de cada "contribución":
El codigo esta aqui .