Prueba de dependencia lineal entre las columnas de una matriz


26

Tengo una matriz de correlación de retornos de seguridad cuyo determinante es cero. (Esto es un poco sorprendente ya que la matriz de correlación de la muestra y la matriz de covarianza correspondiente deberían ser teóricamente definitivas positivas).

Mi hipótesis es que al menos un valor depende linealmente de otros valores. ¿Existe una función en R que pruebe secuencialmente cada columna una matriz para la dependencia lineal?

Por ejemplo, un enfoque sería construir una matriz de correlación una seguridad a la vez y calcular el determinante en cada paso. Cuando el determinante = 0, deténgase cuando haya identificado el valor que es una combinación lineal de otros valores.

Se aprecia cualquier otra técnica para identificar la dependencia lineal en dicha matriz.


Su matriz es positiva semi-definida, aunque no es positiva definida, porque es singular.
ttnphns 01 de

¿Cuáles son las dimensiones (no. Variables; no. Muestras)?
Karl

Número de columnas = 480. # de filas para cada serie de tiempo = 502. En general, se encuentra que cuanto mayor es la serie de tiempo, la matriz de covarianza de la muestra tiende a ser positiva definida. Sin embargo, hay muchos casos en los que le gustaría usar un valor sustancialmente menor de T (o peso exponencial) para reflejar las condiciones recientes del mercado.
Ram Ahluwalia

3
La pregunta está mal planteada. Si su matriz de datos es 480 por 502, entonces decir que la matriz tiene un rango q<480 (el espacio de la columna de la matriz tiene una dimensión q<480 ) es matemáticamente equivalente a decir que una columna es una combinación lineal de las otras, pero puede No seleccione una columna y diga que esta es la columna que depende linealmente. Por lo tanto, no hay ningún procedimiento para hacerlo, y el procedimiento sugerido elegirá una seguridad bastante arbitraria dependiendo del orden en que se incluyan.
NRH

La matriz de covarianza es simétrica. Se genera por transposición (A) * A. La matriz A tiene dimensiones 480x502. Sin embargo, la matriz de covarianza es 480x480
Ram Ahluwalia

Respuestas:


6

Parece hacer una pregunta realmente provocativa: cómo detectar, dada una matriz de correlación singular (o covarianza, o suma de cuadrados y productos cruzados), qué columna depende linealmente de cuál. Supongo tentativamente que la operación de barrido podría ayudar. Aquí está mi sonda en SPSS (no R) para ilustrar.

Generemos algunos datos:

        v1        v2        v3         v4          v5
    -1.64454    .35119   -.06384    -1.05188     .25192
    -1.78520   -.21598   1.20315      .40267    1.14790
     1.36357   -.96107   -.46651      .92889   -1.38072
     -.31455   -.74937   1.17505     1.27623   -1.04640
     -.31795    .85860    .10061      .00145     .39644
     -.97010    .19129   2.43890     -.83642    -.13250
     -.66439    .29267   1.20405      .90068   -1.78066
      .87025   -.89018   -.99386    -1.80001     .42768
    -1.96219   -.27535    .58754      .34556     .12587
    -1.03638   -.24645   -.11083      .07013    -.84446

Creemos una dependencia lineal entre V2, V4 y V5:

compute V4 = .4*V2+1.2*V5.
execute.

Entonces, modificamos nuestra columna V4.

matrix.
get X. /*take the data*/
compute M = sscp(X). /*SSCP matrix, X'X; it is singular*/
print rank(M). /*with rank 5-1=4, because there's 1 group of interdependent columns*/
loop i= 1 to 5. /*Start iterative sweep operation on M from column 1 to column 5*/
-compute M = sweep(M,i).
-print M. /*That's printout we want to trace*/
end loop.
end matrix.

Las impresiones de M en 5 iteraciones:

M
     .06660028    -.12645565    -.54275426    -.19692972    -.12195621
     .12645565    3.20350385    -.08946808    2.84946215    1.30671718
     .54275426    -.08946808    7.38023317   -3.51467361   -2.89907198
     .19692972    2.84946215   -3.51467361   13.88671851   10.62244471
     .12195621    1.30671718   -2.89907198   10.62244471    8.41646486

M
     .07159201     .03947417    -.54628594    -.08444957    -.07037464
     .03947417     .31215820    -.02792819     .88948298     .40790248
     .54628594     .02792819    7.37773449   -3.43509328   -2.86257773
     .08444957    -.88948298   -3.43509328   11.35217042    9.46014202
     .07037464    -.40790248   -2.86257773    9.46014202    7.88345168

M
    .112041875    .041542117    .074045215   -.338801789   -.282334825
    .041542117    .312263922    .003785470    .876479537    .397066281
    .074045215    .003785470    .135542964   -.465602725   -.388002270
    .338801789   -.876479537    .465602725   9.752781632   8.127318027
    .282334825   -.397066281    .388002270   8.127318027   6.772765022

M
   .1238115070   .0110941027   .0902197842   .0347389906   .0000000000
   .0110941027   .3910328733  -.0380581058  -.0898696977  -.3333333333
   .0902197842  -.0380581058   .1577710733   .0477405054   .0000000000
   .0347389906  -.0898696977   .0477405054   .1025348498   .8333333333
   .0000000000   .3333333333   .0000000000  -.8333333333   .0000000000

M
   .1238115070   .0110941027   .0902197842   .0347389906   .0000000000
   .0110941027   .3910328733  -.0380581058  -.0898696977   .0000000000
   .0902197842  -.0380581058   .1577710733   .0477405054   .0000000000
   .0347389906  -.0898696977   .0477405054   .1025348498   .0000000000
   .0000000000   .0000000000   .0000000000   .0000000000   .0000000000

Observe que finalmente la columna 5 se llenó de ceros. Esto significa (según tengo entendido) que V5 está ligado linealmente con algunas de las columnas anteriores . Que columnas Mire la iteración donde la columna 5 no está llena de ceros - iteración 4. Vemos que V5 está vinculado con V2 y V4 con coeficientes -.3333 y .8333: V5 = -.3333 * V2 + .8333 * V4, que corresponde a lo que hemos hecho con los datos: V4 = .4 * V2 + 1.2 * V5.

Así es como supimos qué columna está ligada linealmente con qué otra. No verifiqué cuán útil es el enfoque anterior en un caso más general con muchos grupos de interdependencias en los datos. Sin embargo, en el ejemplo anterior parecía útil.


¿No es esta la forma escalonada de fila reducida? Si es así, ¿no hay paquetes / funciones disponibles en R?
Arun

@Arun, no soy usuario de R, así que no puedo saberlo.
ttnphns

25

Aquí hay un enfoque sencillo: calcule el rango de la matriz que resulta de eliminar cada una de las columnas. Las columnas que, cuando se eliminan, dan como resultado el rango más alto son las linealmente dependientes (ya que eliminarlas no disminuye el rango, mientras que eliminar una columna linealmente independiente sí lo hace).

En R:

rankifremoved <- sapply(1:ncol(your.matrix), function (x) qr(your.matrix[,-x])$rank)
which(rankifremoved == max(rankifremoved))

1
Respuesta enormemente útil para determinar la columna ofensiva en una matriz de regresión donde recibí el error system is exactly singular: U[5,5] = 0 , que ahora sé que significa que la columna 5 fue el problema (¡parece obvio en retrospectiva ya que es una columna de ceros!)
Matt Weller

En el comentario de James, publicó el script: rankifremoved <- sapply (1: ncol (your.matrix), function (x) qr (your.matrix [, - x]) $ rank) que (rankifremoved == max ( rankifremoved)) Hice una prueba en una matriz, me gustaría saber sobre la salida de R. ¿Las columnas de la salida son linealmente dependientes? ¡Gracias!

@ EltonAraújo: La salida será un vector que dará los índices de las columnas linealmente dependientes: entonces (2,4,5) para el ejemplo en la respuesta de ttnphns. Pero me pregunto cómo los problemas de precisión numérica afectarán este método.
Scortchi - Restablece a Monica

rankifremoved contiene todas las columnas que dependen linealmente entre ellas o entre ellas. En alguna aplicación, es posible que queramos retener una columna o algunas columnas y no
eliminarlas

¿No debería esto devolver un conjunto vacío para your.matrix = matrix(1:4, 2)?
Holger Brandl

15

La pregunta se refiere a "identificar relaciones subyacentes [lineales]" entre variables.

La manera rápida y fácil de detectar relaciones es hacer retroceder cualquier otra variable (use una constante, incluso) contra esas variables usando su software favorito: cualquier buen procedimiento de regresión detectará y diagnosticará colinealidad. (Ni siquiera se molestará en mirar los resultados de la regresión: solo confiamos en un efecto secundario útil de configurar y analizar la matriz de regresión).

Sin embargo, suponiendo que se detecte la colinealidad, ¿qué sigue? El análisis de componentes principales (PCA) es exactamente lo que se necesita: sus componentes más pequeños corresponden a relaciones casi lineales. Estas relaciones se pueden leer directamente de las "cargas", que son combinaciones lineales de las variables originales. Las pequeñas cargas (es decir, las asociadas con pequeños valores propios) corresponden a casi colinealidades. Un valor propio de correspondería a una relación lineal perfecta. Valores propios ligeramente más grandes que todavía son mucho más pequeños que los más grandes corresponderían a relaciones lineales aproximadas.0 0

(Existe un arte y bastante literatura asociada con la identificación de lo que es una carga "pequeña". Para modelar una variable dependiente, sugeriría incluirla dentro de las variables independientes en el PCA para identificar los componentes, independientemente de sus tamaños, en los que la variable dependiente juega un papel importante. Desde este punto de vista, "pequeño" significa mucho más pequeño que cualquiera de esos componentes).


Veamos algunos ejemplos. (Estos se usan Rpara los cálculos y el trazado). Comience con una función para realizar PCA, busque componentes pequeños, grábelos y devuelva las relaciones lineales entre ellos.

pca <- function(x, threshold, ...) {
  fit <- princomp(x)
  #
  # Compute the relations among "small" components.
  #
  if(missing(threshold)) threshold <- max(fit$sdev) / ncol(x)
  i <- which(fit$sdev < threshold)
  relations <- fit$loadings[, i, drop=FALSE]
  relations <- round(t(t(relations) / apply(relations, 2, max)), digits=2)
  #
  # Plot the loadings, highlighting those for the small components.
  #
  matplot(x, pch=1, cex=.8, col="Gray", xlab="Observation", ylab="Value", ...)
  suppressWarnings(matplot(x %*% relations, pch=19, col="#e0404080", add=TRUE))

  return(t(relations))
}

si,do,re,miUNA

process <- function(z, beta, sd, ...) {
  x <- z %*% beta; colnames(x) <- "A"
  pca(cbind(x, z + rnorm(length(x), sd=sd)), ...)
}

si,...,miUNA=si+do+re+miUNA=si+(do+re)/ /2+misweep

n.obs <- 80 # Number of cases
n.vars <- 4 # Number of independent variables
set.seed(17)
z <- matrix(rnorm(n.obs*(n.vars)), ncol=n.vars)
z.mean <- apply(z, 2, mean)
z <- sweep(z, 2, z.mean)
colnames(z) <- c("B","C","D","E") # Optional; modify to match `n.vars` in length

si,...,miUNA

Resultados

El resultado asociado con el panel superior izquierdo fue

       A  B  C  D  E
Comp.5 1 -1 -1 -1 -1

0 00 0UNA-si-do-re-mi

La salida para el panel central superior fue

       A     B     C     D     E
Comp.5 1 -0.95 -1.03 -0.98 -1.02

(UNA,si,do,re,mi)

       A     B     C     D     E
Comp.5 1 -1.33 -0.77 -0.74 -1.07

UNA=si+do+re+mi

1,1/ /2,1/ /2,1

En la práctica, a menudo no es el caso que una variable se destaque como una combinación obvia de las otras: todos los coeficientes pueden ser de tamaños comparables y de signos variables. Además, cuando hay más de una dimensión de las relaciones, no hay una forma única de especificarlas: se necesita un análisis adicional (como la reducción de filas) para identificar una base útil para esas relaciones. Así es como funciona el mundo: todo lo que puede decir es que estas combinaciones particulares producidas por PCA corresponden a casi ninguna variación en los datos. Para hacer frente a esto, algunas personas usan los componentes más grandes ("principales") directamente como variables independientes en la regresión o el análisis posterior, cualquiera sea la forma que pueda tomar. Si hace esto, ¡no olvide primero eliminar la variable dependiente del conjunto de variables y rehacer la PCA!


Aquí está el código para reproducir esta figura:

par(mfrow=c(2,3))
beta <- c(1,1,1,1) # Also can be a matrix with `n.obs` rows: try it!
process(z, beta, sd=0, main="A=B+C+D+E; No error")
process(z, beta, sd=1/10, main="A=B+C+D+E; Small error")
process(z, beta, sd=1/3, threshold=2/3, main="A=B+C+D+E; Large error")

beta <- c(1,1/2,1/2,1)
process(z, beta, sd=0, main="A=B+(C+D)/2+E; No error")
process(z, beta, sd=1/10, main="A=B+(C+D)/2+E; Small error")
process(z, beta, sd=1/3, threshold=2/3, main="A=B+(C+D)/2+E; Large error")

(Tuve que jugar con el umbral en los casos de error grande para mostrar solo un componente: esa es la razón para suministrar este valor como parámetro process).


El usuario ttnphns ha dirigido amablemente nuestra atención a un hilo estrechamente relacionado. Una de sus respuestas (por JM) sugiere el enfoque descrito aquí.


Wow, heres lo que entiendo de su respuesta ... haga retroceder mis variables contra cualquier otra variable. Use el VIF para luego identificar las variables relacionadas ... esto funciona. ¿Es mejor hacer esto con fragmentos de datos a la vez? ¿También elimina algo si detecta colinealidad utilizando la regresión anterior? Por lo que entiendo acerca de PCA generalmente es que usa las PC más grandes (explicando la mayor varianza) en función de los valores propios, ya que estos explican la mayor varianza, estos se cargan a diferentes grados usando las variables originales. No estoy seguro de qué cargas pequeñas y con qué son colineales
Samuel

Esta respuesta explica cómo interpretar los componentes pequeños: exhiben las colinealidades. Sí, puede usar subgrupos de variables si lo desea. El método de regresión es solo para detectar la presencia de colinealidad, no para identificar las relaciones colineales: eso es lo que hace el PCA.
whuber

"loadings," which are linear combinations of the original variablesUNAUNA-1

Además, ¿puedo pedirle que deje su opinión sobre el posible uso de la operación de barrido ( stats.stackexchange.com/a/16391/3277 ) en la tarea de rastrear subconjuntos de variables linealmente dependientes?
ttnphns 01 de

XX=UWVVprincompXV=UWWUW0 0XVX


3

Me encontré con este problema hace aproximadamente dos semanas y decidí que necesitaba volver a visitarlo porque cuando se trata de conjuntos de datos masivos, es imposible hacer estas cosas manualmente.

Creé un bucle for () que calcula el rango de la matriz columna por columna. Entonces, para la primera iteración, el rango será 1. El segundo, 2. Esto ocurre hasta que el rango sea MENOR que el número de columna que está utilizando.

Muy sencillo:

for (i in 1:47) {

  print(qr(data.frame[1:i])$rank) 
  print(i) 
  print(colnames(data.frame)[i])
  print("###") 
}

para () desglose del bucle

  1. calcula el rango para la i-ésima columna
  2. imprime el número de iteración
  3. imprime el nombre de la columna como referencia
  4. divide la consola con "###" para que pueda desplazarse fácilmente

Estoy seguro de que puede agregar una declaración if, aún no la necesito porque solo estoy tratando con columnas 50ish.

¡Espero que esto ayude!


2
Aunque no hay nada de malo en esto teóricamente, es un algoritmo numéricamente inestable e ineficiente. Especialmente con un gran número de columnas, puede fallar al detectar casi colinealidad y detectar falsamente la colinealidad donde no existe ninguna.
whuber

2

Rango, r de una matriz = número de columnas linealmente independientes (o filas) de una matriz. Para una matriz n por n A , el rango (A) = n => todas las columnas (o filas) son linealmente independientes.


2

No es que la respuesta que dio @Whuber realmente deba ampliarse, pero pensé en proporcionar una breve descripción de las matemáticas.

XXv=0 0v0 0vXXλ=0 0XXXXXλ=0 0XXvλ

κj=λmetrounaXλj

XX=[0.0010 00 00 00.0010 00 00 00.001].
λ1=λ2=λ3=0.001
κ=λmetrounaXλmetroyonorte=1

Citas

Montgomery, D. (2012). Introducción al análisis de regresión lineal, 5ª edición. John Wiley & Sons Inc.


1
X

QRXnorteknorte>>kXR
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.