Как R обрабатывает пропущенные значения в lm?

32

Я хотел бы регрессировать вектор B против каждого из столбцов в матрице A. Это тривиально, если нет пропущенных данных, но если матрица A содержит пропущенные значения, тогда моя регрессия против A ограничена включением только тех строк, где все значения присутствуют ( поведение na.omit по умолчанию ). Это приводит к неправильным результатам для столбцов без пропущенных данных. Я могу регрессировать матрицу столбцов B относительно отдельных столбцов матрицы A, но у меня есть тысячи регрессий, и это непомерно медленно и не элегантно. Функция na.exclude, похоже, предназначена для этого случая, но я не могу заставить ее работать. Что я здесь не так делаю? Использование R 2.13 на OSX, если это имеет значение.

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')
Дэвид Куигли
источник
1
Что вы подразумеваете под «Я могу рассчитать каждую строку индивидуально»?
ЧЛ
Извините, хотел сказать «я могу регрессировать матрицу столбцов B по отношению к столбцам в A по отдельности», что означает одноразовые вызовы lm. Отредактировано, чтобы отразить это.
Дэвид Куигли
1
Один за другим вызовы lm / регрессии - не лучший способ сделать регрессию (исходя из определения регрессии, которое заключается в том, чтобы найти частичное влияние каждого предиктора на ответ / результат, учитывая состояние других переменные)
KarthikS

Ответы:

23

Изменить: я неправильно понял ваш вопрос. Есть два аспекта:

а) na.omitи na.excludeоба делают регистром удаления как по предикторам, так и по критериям. Они отличаются только тем, что функции экстрактора, такие как residuals()или fitted()будут дополнять свой вывод символами NAs для пропущенных случаев, и na.exclude, следовательно, будут иметь ту же длину, что и входные переменные.

> 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

б) Реальная проблема не в этой разнице между, na.omitи na.exclude, похоже, вы не хотите удалять регистр, который принимает во внимание переменные критерия, как это делают оба.

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

Результаты регрессии зависят от матриц (псевдообратная матрица дизайна , коэффициенты ) и шляпа матрица , подогнанные значения ). Если вы не хотите удалять регистр, вам нужна отдельная матрица для каждого столбца , поэтому нет способа обойти отдельные регрессии для каждого критерия. Вы можете попытаться избежать накладных расходов , выполнив что-то вроде следующего:X+=(XX)1XXβ^=X+YH=XX+Y^=HYXYlm()

> 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

Обратите внимание, что могут быть численно лучшие способы вычисления и , вместо этого вы можете проверить разложение. SVD-подход объясняется здесь на SE . Я не рассчитал вышеупомянутый подход с большими матрицами против фактического использования .X+HQRYlm()

каракал
источник
Это имеет смысл, учитывая мое понимание того, как должен работать na.exclude. Однако, если вы вызываете> X.both = cbind (X1, X2) и затем> dim (lm (X.both ~ Y, na.action = na.exclude) $ residuals), вы все равно получите 94 остатка вместо 97 и 97.
Дэвид Куигли
Это улучшение, но если вы посмотрите на остатки (lm (X.both ~ Y, na.action = na.exclude)), вы увидите, что в каждом столбце есть шесть пропущенных значений, несмотря на то, что в столбце 1 X есть пропущенные значения. оба взяты из образцов, отличных от тех, что указаны в столбце 2. Поэтому na.exclude сохраняет форму матрицы невязок, но под капотом R, по-видимому, регрессирует только со значениями, присутствующими во всех строках X.both. Для этого может быть веская статистическая причина, но для моего приложения это проблема.
Дэвид Куигли
@ Дэвид, я неправильно понял твой вопрос. Я думаю, что теперь я понимаю вашу точку зрения, и отредактировал мой ответ, чтобы устранить ее
Каракал
5

Я могу придумать два пути. Один из них - объединить данные, na.excludeа затем снова разделить данные:

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)

Другой способ - использовать dataаргумент и создать формулу.

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)

Если вы делаете много регрессии, первый путь должен быть быстрее, так как выполняется меньше фоновой магии. Хотя, если вам нужны только коэффициенты и остатки, я предлагаю использовать lsfit, что гораздо быстрее, чем lm. Второй способ немного приятнее, но на моем ноутбуке при попытке сделать резюме по полученной регрессии выдается ошибка. Я попытаюсь увидеть, является ли это ошибкой.

mpiktas
источник
Спасибо, но lm (A.ex ~ B.ex) в вашем коде соответствует 9 баллам против A1 (правильно) и 9 баллов против A2 (нежелательно). Есть 10 измеренных точек для B1 и A2; Я выбрасываю одну точку в регрессии B1 против A2, потому что соответствующая точка отсутствует в A1. Если это просто «Как это работает», я могу принять это, но это не то, что я пытаюсь заставить R сделать.
Дэвид Куигли
@ Дэвид, похоже, я неправильно понял твою проблему. Я выложу исправление позже.
mpiktas
1

В следующем примере показано, как делать прогнозы и остатки, которые соответствуют исходному кадру данных (используя опцию «na.action = na.exclude» в lm (), чтобы указать, что NA должны быть помещены в векторы остатков и предсказания, где исходный кадр данных имел пропущенные значения. Он также показывает, как указать, должны ли предсказания включать только наблюдения, в которых были выполнены как объясняющие, так и зависимые переменные (т. е. строго в выборочных предсказаниях), или наблюдения, в которых объясняющие переменные были полными, и, следовательно, возможно предсказание Xb ( то есть, включая прогнозирование вне выборки для наблюдений, которые имели полные объясняющие переменные, но в которых отсутствовала зависимая переменная).

Я использую cbind для добавления прогнозируемых и остаточных переменных к исходному набору данных.

## 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)))
Майкл Эш
источник