Como R lida com valores ausentes em lm?

32

Eu gostaria de regredir um vetor B contra cada uma das colunas da matriz A. Isso é trivial se não houver dados ausentes, mas se a matriz A contiver valores ausentes, minha regressão contra A é restrita a incluir apenas linhas em que todas valores estão presentes (o comportamento padrão na.omit ). Isso produz resultados incorretos para colunas sem dados ausentes. Posso regredir a matriz da coluna B contra colunas individuais da matriz A, mas tenho milhares de regressões a fazer, e isso é proibitivamente lento e deselegante. A função na.exclude parece ter sido projetada para este caso, mas não posso fazê-lo funcionar. O que eu estou fazendo errado aqui? Usando R 2.13 no OSX, se isso for 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
fonte
1
O que você quer dizer com "Eu posso calcular cada linha individualmente"?
chl
Desculpe, pretendia dizer "Eu posso regredir a matriz da coluna B contra as colunas de A individualmente", significando chamadas únicas de uma vez para lm. Editado para refletir isso.
David Quigley
1
Chamadas únicas de uma vez para lm / regressão não é uma ótima maneira de fazer a regressão (seguindo a definição de regressão, que é encontrar o efeito parcial de cada preditor em uma resposta / resultado, dado o estado de outra variáveis)
KarthikS

Respostas:

23

Edit: Eu entendi mal a sua pergunta. Existem dois aspectos:

a) na.omite na.excludeambos fazem exclusão casualmente em relação a preditores e critérios. Eles diferem apenas no fato de o extrator funcionar como residuals()ou fitted()irá preencher sua saída com NAs para os casos omitidos na.exclude, tendo assim uma saída do mesmo comprimento que as variáveis ​​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) O problema real não é com essa diferença entre na.omite na.exclude, você não parece querer uma exclusão casualmente que leve em consideração as variáveis ​​de critério, o que ambas fazem.

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

Os resultados da regressão dependem do matrizes (pseudoinverse de montagem de matriz X , coeficientes β = X + Y ) e a matriz de chapéu H = X X + , valores ajustados Y = H Y ) Se você não deseja exclusão casewise, precisará de uma matriz de design X diferente para cada coluna de YX+=(XX)-1XXβ^=X+YH=XX+Y^=HYXY, portanto, não há como ajustar regressões separadas para cada critério. Você pode tentar evitar a sobrecarga lm()fazendo algo ao longo das linhas a seguir:

> 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

Note que não pode ser numericamente melhores maneiras de caculate e H , você pode verificar a Q R -decomposition vez. A abordagem SVD é explicada aqui no SE . Não cronometrei a abordagem acima com grandes matrizes Y contra o uso real .X+HQRYlm()

caracal
fonte
Isso faz sentido, considerando meu entendimento de como o n.exclude deve funcionar. No entanto, se você chamar> X.both = cbind (X1, X2) e depois> dim (lm (X.oth ~ Y, na.action = na.exclude) $ resíduos), você ainda terá 94 resíduos, em vez de 97 e 97.
David Quigley
Isso é uma melhoria, mas se você observar os resíduos (lm (X. tanto Y, na.ação = na.excluir)), verá que cada coluna possui seis valores ausentes, mesmo que os valores ausentes na coluna 1 de X. ambos são de amostras diferentes das da coluna 2. Portanto, na.exclude está preservando a forma da matriz de resíduos, mas sob o capô R aparentemente está apenas regredindo com valores presentes em todas as linhas de X. ambos. Pode haver uma boa razão estatística para isso, mas para o meu aplicativo é um problema.
David Quigley 20/05
@ David Eu tinha entendido mal a sua pergunta. Acho que agora entendi o seu ponto e editei minha resposta para abordá-lo.
caracal 20/05
5

Eu posso pensar em duas maneiras. Uma é combinar os dados, usar os dados na.excludee depois separá-los novamente:

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)

Outra maneira é usar o dataargumento e criar uma 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)

Se você estiver fazendo muita regressão, a primeira maneira deve ser mais rápida, pois é executada menos mágica em segundo plano. Embora se você precisar apenas de coeficientes e resíduos, sugiro usar lsfit, o que é muito mais rápido que lm. A segunda maneira é um pouco melhor, mas no meu laptop, tentar fazer um resumo da regressão resultante gera um erro. Vou tentar ver se isso é um bug.

mpiktas
fonte
Obrigado, mas lm (A.ex ~ B.ex) no seu código se encaixa 9 pontos contra A1 (correto) e 9 pontos contra A2 (indesejados). Existem 10 pontos medidos para B1 e A2; Estou jogando fora um ponto na regressão de B1 contra A2 porque o ponto correspondente está ausente em A1. Se é apenas a maneira como funciona, posso aceitar isso, mas não é isso que estou tentando convencer o R ​​a fazer.
David Quigley
@ David, oh, parece que eu entendi mal o seu problema. Vou postar a correção mais tarde.
mpiktas 20/05
1

O exemplo a seguir mostra como fazer previsões e resíduos que estejam em conformidade com o dataframe original (usando a opção "na.action = na.exclude" em lm () para especificar que as NA devem ser colocadas nos vetores residuais e de previsão nos quais o dataframe original também mostra como especificar se as previsões devem incluir apenas observações onde as variáveis ​​explicativas e dependentes estavam completas (ou seja, previsões estritamente dentro da amostra) ou observações onde as variáveis ​​explicativas estavam completas e, portanto, a previsão de Xb é possível ( isto é, incluindo previsão fora da amostra para observações que apresentavam variáveis ​​explicativas completas, mas estavam ausentes a variável dependente).

Eu uso cbind para adicionar as variáveis ​​preditas e residuais ao conjunto de dados 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
fonte