model.matrix () с na.action = NULL? - PullRequest
       10

model.matrix () с na.action = NULL?

36 голосов
/ 11 апреля 2011

У меня есть формула и фрейм данных, и я хочу извлечь model.matrix(). Однако мне нужно, чтобы полученная матрица включала NA, найденные в исходном наборе данных. Если бы я использовал model.frame() для этого, я бы просто передал его na.action=NULL. Однако вывод, который мне нужен, имеет формат model.matrix(). В частности, мне нужны только правые переменные, мне нужно, чтобы выходные данные были матрицей (а не фреймом данных), и мне нужно, чтобы факторы были преобразованы в ряд фиктивных переменных.

Я уверен, что мог бы что-то взломать, используя петли или что-то в этом роде, но мне было интересно, кто-нибудь может предложить более чистый и эффективный обходной путь. Большое спасибо за ваше время!

А вот пример:

dat <- data.frame(matrix(rnorm(20),5,4), gl(5,2))
dat[3,5] <- NA
names(dat) <- c(letters[1:4], 'fact')
ff <- a ~ b + fact

# This omits the row with a missing observation on the factor
model.matrix(ff, dat) 

# This keeps the NA, but it gives me a data frame and does not dichotomize the factor
model.frame(ff, dat, na.action=NULL) 

Вот что я хотел бы получить:

   (Intercept)          b fact2 fact3 fact4 fact5
1            1  0.7266086     0     0     0     0
2            1 -0.6088697     0     0     0     0
3            NA 0.4643360     NA    NA    NA    NA
4            1 -1.1666248     1     0     0     0
5            1 -0.7577394     0     1     0     0
6            1  0.7266086     0     1     0     0
7            1 -0.6088697     0     0     1     0
8            1  0.4643360     0     0     1     0
9            1 -1.1666248     0     0     0     1
10           1 -0.7577394     0     0     0     1

Ответы [ 4 ]

43 голосов
/ 23 октября 2013

Предложение Joris работает, но более быстрый и чистый способ сделать это - через глобальную настройку na.action. Опция 'Pass' позволяет достичь цели сохранения NA из исходного набора данных.

Опция 1: Pass

Результирующая матрица будет содержать NA в строках, соответствующих исходному набору данных..

options(na.action='na.pass')
model.matrix(ff, dat) 

Опция 2: пропустить

Результирующая матрица будет пропускать строки, содержащие NA.

options(na.action='na.omit')
model.matrix(ff, dat) 

Опция 3: Fail

Ошибка будетпроисходят, если исходные данные содержат NA.

options(na.action='na.fail')
model.matrix(ff, dat) 

Конечно, всегда будьте осторожны при изменении глобальных опций, потому что они могут изменить поведение других частей вашего кода.Осторожный человек может сохранить исходную настройку, например, current.na.action <- options('na.action'), а затем изменить ее после создания model.matrix.

31 голосов
/ 11 августа 2015

Другой способ - использовать функцию model.frame с аргументом na.action=na.pass в качестве второго аргумента для model.matrix:

> model.matrix(ff, model.frame(~ ., dat, na.action=na.pass))
   (Intercept)          b fact2 fact3 fact4 fact5
1            1 -1.3560754     0     0     0     0
2            1  2.5476965     0     0     0     0
3            1  0.4635628    NA    NA    NA    NA
4            1 -0.2871379     1     0     0     0
5            1  2.2684958     0     1     0     0
6            1 -1.3560754     0     1     0     0
7            1  2.5476965     0     0     1     0
8            1  0.4635628     0     0     1     0
9            1 -0.2871379     0     0     0     1
10           1  2.2684958     0     0     0     1

model.frame позволяет установить соответствующее действие для na.actionкоторый поддерживается, когда вызывается model.matrix.

16 голосов
/ 11 апреля 2011

Вы можете немного возиться с объектом model.matrix, основываясь на именах строк:

MM <- model.matrix(ff,dat)
MM <- MM[match(rownames(dat),rownames(MM)),]
MM[,"b"] <- dat$b
rownames(MM) <- rownames(dat)

, что дает:

> MM
     (Intercept)         b fact2 fact3 fact4 fact5
1              1 0.9583010     0     0     0     0
2              1 0.3266986     0     0     0     0
3             NA 1.4992358    NA    NA    NA    NA
4              1 1.2867461     1     0     0     0
5              1 0.5024700     0     1     0     0
6              1 0.9583010     0     1     0     0
7              1 0.3266986     0     0     1     0
8              1 1.4992358     0     0     1     0
9              1 1.2867461     0     0     0     1
10             1 0.5024700     0     0     0     1

Кроме того, вы можете использовать contrasts(), чтобы сделать работу за вас. Построение матрицы вручную будет:

cont <- contrasts(dat$fact)[as.numeric(dat$fact),]
colnames(cont) <- paste("fact",colnames(cont),sep="")
out <- cbind(1,dat$b,cont)
out[is.na(dat$fact),1] <- NA
colnames(out)[1:2]<- c("Intercept","b")
rownames(out) <- rownames(dat)

, что дает:

> out
     Intercept          b fact2 fact3 fact4 fact5
1            1  0.2534288     0     0     0     0
2            1  0.2697760     0     0     0     0
3           NA -0.8236879    NA    NA    NA    NA
4            1 -0.6053445     1     0     0     0
5            1  0.4608907     0     1     0     0
6            1  0.2534288     0     1     0     0
7            1  0.2697760     0     0     1     0
8            1 -0.8236879     0     0     1     0
9            1 -0.6053445     0     0     0     1
10           1  0.4608907     0     0     0     1

В любом случае оба метода могут быть включены в функцию, которая может работать с более сложными формулами. Я оставляю упражнение читателю (что мне не нравится это предложение, когда я встречаю его в газете ;-))

8 голосов
/ 02 марта 2018

Я наткнулся на более простое решение, посмотрев на mattdevlin и Натана Гулда :

 model.matrix.lm(ff, dat, na.action = "na.pass")

model.matrix.default может не поддерживатьna.action аргумент, но model.matrix.lm делает!

(я обнаружил model.matrix.lm из предложений по автозаполнению Rstudio - похоже, это единственный не-стандартный метод для model.matrix, если вы не загрузилилюбые библиотеки, которые добавляют другие. Тогда я просто догадался, что это может поддерживать аргумент na.action.)

...