¿Cómo maneja R los valores perdidos en lm?

32

Me gustaría hacer una regresión de un vector B contra cada una de las columnas de una matriz A. Esto es trivial si no faltan datos, pero si la matriz A contiene valores faltantes, entonces mi regresión contra A está limitada a incluir solo filas donde todos los valores están presentes (el comportamiento predeterminado na.omit ). Esto produce resultados incorrectos para columnas sin datos faltantes. Puedo hacer una regresión de la matriz de columna B contra columnas individuales de la matriz A, pero tengo miles de regresiones que hacer, y esto es prohibitivamente lento y poco elegante. La función na.exclude parece estar diseñada para este caso, pero no puedo hacer que funcione. ¿Qué estoy haciendo mal aquí? Usando R 2.13 en OSX, si es importante.

A = matrix(1:20, nrow=10, ncol=2)
B = matrix(1:10, nrow=10, ncol=1)
dim(lm(A~B)$residuals)
# [1] 10 2 (the expected 10 residual values)

# Missing value in first column; now we have 9 residuals
A[1,1] = NA  
dim(lm(A~B)$residuals)
#[1]  9 2 (the expected 9 residuals, given na.omit() is the default)

# Call lm with na.exclude; still have 9 residuals
dim(lm(A~B, na.action=na.exclude)$residuals)
#[1]  9 2 (was hoping to get a 10x2 matrix with a missing value here)

A.ex = na.exclude(A)
dim(lm(A.ex~B)$residuals)
# Throws an error because dim(A.ex)==9,2
#Error in model.frame.default(formula = A.ex ~ B, drop.unused.levels = TRUE) : 
#  variable lengths differ (found for 'B')
David Quigley
fuente
1
¿Qué quiere decir con "puedo calcular cada fila individualmente"?
chl
Lo sentimos, significaba "puedo hacer una regresión de la matriz de columnas B contra las columnas en A individualmente", lo que significa llamadas individuales a la vez a lm. Editado para reflejar esto.
David Quigley
1
Las llamadas individuales a lm / regresión no son una excelente manera de hacer la regresión (siguiendo la definición de regresión, que es encontrar el efecto parcial de cada predictor en una respuesta / resultado dado el estado de otro variables)
KarthikS

Respuestas:

23

Editar: no entendí tu pregunta. Hay dos aspectos:

a) na.omity na.excludeambos eliminan en caso de caso tanto los predictores como los criterios. Solo difieren en que el extractor funciona como residuals()o fitted()rellenará su salida con NAs para los casos omitidos na.exclude, por lo que tiene una salida de la misma longitud que las variables de entrada.

> N    <- 20                               # generate some data
> y1   <- rnorm(N, 175, 7)                 # criterion 1
> y2   <- rnorm(N,  30, 8)                 # criterion 2
> x    <- 0.5*y1 - 0.3*y2 + rnorm(N, 0, 3) # predictor
> y1[c(1, 3,  5)] <- NA                    # some NA values
> y2[c(7, 9, 11)] <- NA                    # some other NA values
> Y    <- cbind(y1, y2)                    # matrix for multivariate regression
> fitO <- lm(Y ~ x, na.action=na.omit)     # fit with na.omit
> dim(residuals(fitO))                     # use extractor function
[1] 14  2

> fitE <- lm(Y ~ x, na.action=na.exclude)  # fit with na.exclude
> dim(residuals(fitE))                     # use extractor function -> = N
[1] 20  2

> dim(fitE$residuals)                      # access residuals directly
[1] 14  2

b) El problema real no es con esta diferencia entre na.omity na.exclude, no parece que desee la eliminación en caso de que tenga en cuenta las variables de criterio, lo que ambos hacen.

> X <- model.matrix(fitE)                  # design matrix
> dim(X)                                   # casewise deletion -> only 14 complete cases
[1] 14  2

X+=(XX)1XXβ^=X+YH=XX+Y^=HYXY, por lo que no hay forma de ajustar regresiones separadas para cada criterio. Puede intentar evitar la sobrecarga lm()haciendo algo similar a lo siguiente:

> Xf <- model.matrix(~ x)                    # full design matrix (all cases)
# function: manually calculate coefficients and fitted values for single criterion y
> getFit <- function(y) {
+     idx   <- !is.na(y)                     # throw away NAs
+     Xsvd  <- svd(Xf[idx , ])               # SVD decomposition of X
+     # get X+ but note: there might be better ways
+     Xplus <- tcrossprod(Xsvd$v %*% diag(Xsvd$d^(-2)) %*% t(Xsvd$v), Xf[idx, ])
+     list(coefs=(Xplus %*% y[idx]), yhat=(Xf[idx, ] %*% Xplus %*% y[idx]))
+ }

> res <- apply(Y, 2, getFit)    # get fits for each column of Y
> res$y1$coefs
                   [,1]
(Intercept) 113.9398761
x             0.7601234

> res$y2$coefs
                 [,1]
(Intercept) 91.580505
x           -0.805897

> coefficients(lm(y1 ~ x))      # compare with separate results from lm()
(Intercept)           x 
113.9398761   0.7601234 

> coefficients(lm(y2 ~ x))
(Intercept)           x 
  91.580505   -0.805897

X+HQRYlm()

lince
fuente
Eso tiene sentido dado mi entendimiento de cómo debería funcionar na.exclude. Sin embargo, si llama> X.both = cbind (X1, X2) y luego> dim (lm (X.both ~ Y, na.action = na.exclude) $ residuals) aún obtiene 94 residuales, en lugar de 97 y 97.
David Quigley
Eso es una mejora, pero si observa los residuos (lm (X.both ~ Y, na.action = na.exclude)), verá que cada columna tiene seis valores faltantes, aunque los valores faltantes en la columna 1 de X. ambos provienen de muestras diferentes a las de la columna 2. Por lo tanto, na.exclude conserva la forma de la matriz de residuos, pero bajo el capó R aparentemente solo retrocede con valores presentes en todas las filas de X. Puede haber una buena razón estadística para esto, pero para mi aplicación es un problema.
David Quigley
@David No había entendido bien tu pregunta. Creo que ahora veo su punto y he editado mi respuesta para abordarlo.
caracal
5

Puedo pensar en dos formas. Una es combinar los datos usando na.excludey luego separar los datos nuevamente:

A = matrix(1:20, nrow=10, ncol=2)
colnames(A) <- paste("A",1:ncol(A),sep="")

B = matrix(1:10, nrow=10, ncol=1)
colnames(B) <- paste("B",1:ncol(B),sep="")

C <- cbind(A,B)

C[1,1] <- NA
C.ex <- na.exclude(C)

A.ex <- C[,colnames(A)]
B.ex <- C[,colnames(B)]

lm(A.ex~B.ex)

Otra forma es usar el dataargumento y crear una fórmula.

Cd <- data.frame(C)
fr <- formula(paste("cbind(",paste(colnames(A),collapse=","),")~",paste(colnames(B),collapse="+"),sep=""))

lm(fr,data=Cd)

Cd[1,1] <-NA

lm(fr,data=Cd,na.action=na.exclude)

Si está haciendo mucha regresión, la primera forma debería ser más rápida, ya que se realiza menos magia de fondo. Aunque si solo necesita coeficientes y residuos, sugiero usar lsfit, que es mucho más rápido que lm. La segunda forma es un poco mejor, pero en mi computadora portátil intentar hacer un resumen de la regresión resultante arroja un error. Trataré de ver si esto es un error.

mpiktas
fuente
Gracias, pero lm (A.ex ~ B.ex) en su código se ajusta a 9 puntos contra A1 (correcto) y 9 puntos contra A2 (no deseado). Hay 10 puntos medidos para B1 y A2; Estoy arrojando un punto en la regresión de B1 contra A2 porque falta el punto correspondiente en A1. Si esa es la forma en que funciona, puedo aceptar eso, pero eso no es lo que estoy tratando de hacer que R haga.
David Quigley
@David, parece que he entendido mal tu problema. Publicaré la solución más tarde.
mpiktas
1

El siguiente ejemplo muestra cómo hacer predicciones y residuos que se ajustan al marco de datos original (usando la opción "na.action = na.exclude" en lm () para especificar que los NA deben colocarse en los vectores de residuos y predicción donde el marco de datos original tenía valores perdidos. También muestra cómo especificar si las predicciones deben incluir solo observaciones donde las variables explicativas y dependientes estaban completas (es decir, predicciones estrictamente en la muestra) u observaciones donde las variables explicativas estaban completas, y por lo tanto la predicción Xb es posible ( es decir, incluida la predicción fuera de la muestra para observaciones que tenían variables explicativas completas pero les faltaba la variable dependiente).

Utilizo cbind para agregar las variables predichas y residuales al conjunto de datos original.

## Set up data with a linear model
N <- 10
NXmissing <- 2 
X <- runif(N, 0, 10)
Y <- 6 + 2*X + rnorm(N, 0, 1)
## Put in missing values (missing X, missing Y, missing both)
X[ sample(1:N , NXmissing) ] <- NA
Y[ sample(which(is.na(X)), 1)]  <- NA
Y[ sample(which(!is.na(X)), 1)]  <- NA
(my.df <- data.frame(X,Y))

## Run the regression with na.action specified to na.exclude
## This puts NA's in the residual and prediction vectors
my.lm  <- lm( Y ~ X, na.action=na.exclude, data=my.df)

## Predict outcome for observations with complete both explanatory and
## outcome variables, i.e. observations included in the regression
my.predict.insample  <- predict(my.lm)

## Predict outcome for observations with complete explanatory
## variables.  The newdata= option specifies the dataset on which
## to apply the coefficients
my.predict.inandout  <- predict(my.lm,newdata=my.df)

## Predict residuals 
my.residuals  <- residuals(my.lm)

## Make sure that it binds correctly
(my.new.df  <- cbind(my.df,my.predict.insample,my.predict.inandout,my.residuals))

## or in one fell swoop

(my.new.df  <- cbind(my.df,yhat=predict(my.lm),yhato=predict(my.lm,newdata=my.df),uhat=residuals(my.lm)))
Michael Ash
fuente