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

У меня есть формула и фрейм данных, и я хочу извлечь 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

person Vincent    schedule 11.04.2011    source источник


Ответы (4)


Вы можете немного повозиться с объектом 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

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

person Joris Meys    schedule 11.04.2011
comment
Я оставляю это упражнение читателю, но оно идеально подходит для конспектов лекций ;) - person csgillespie; 11.04.2011
comment
Звонок match великолепен, вот чего мне не хватало! - person Roman Luštrik; 11.04.2011
comment
match() работает прекрасно. Кстати, это решило еще одну проблему, с которой я некоторое время боролся. Огромное спасибо! - person Vincent; 11.04.2011

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

Вариант 1: пройти

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

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

Вариант 2: пропустить

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

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

Вариант 3: Неудача

Если исходные данные содержат NA, произойдет ошибка.

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

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

person Nathan Gould    schedule 23.10.2013
comment
Узнавайте что-то новое каждый день. - person mgriebe; 24.09.2014
comment
Чтобы было ясно, один из способов сбросить options('na.action') в исходное состояние: current.na.action <- options('na.action', затем options('na.action' = current.na.action$na.action). Обратите внимание, что неиспользование имени na.action для извлечения строковой переменной из current.na.action может привести к ошибкам бесконечной рекурсии и переполнения стека защиты, особенно если вы используете конструкцию внутри функции с on.exit. - person Alex; 19.05.2017

Другой способ — использовать функцию 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.

person mattdevlin    schedule 11.08.2015
comment
Спасибо, но первый абзац моего вопроса объясняет, почему это не работает в приложении, которое я имел в виду. - person Vincent; 16.08.2015
comment
@ Винсент, model.frame - это просто аргумент для model.matrix, поэтому вывод - это формат model.matrix. Разве вывод, который я показал, не то, что вы хотите? - person mattdevlin; 17.08.2015
comment
Вот это да! Мне действительно нужно работать над своими навыками чтения. Это действительно классное решение; не знал, что ты так умеешь. Спасибо! - person Vincent; 17.08.2015
comment
Этот ответ кажется правильным; надеюсь, люди найдут его, хотя старые ответы имеют более высокий рейтинг - person rbatt; 21.11.2015

Я наткнулся на более простое решение после просмотра 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.)

person onestop    schedule 02.03.2018
comment
Спасибо @onestop, я думаю, что это должен быть рекомендуемый ответ. - person Dan Houghton; 20.10.2018
comment
это должен быть рекомендуемый ответ! - person Jinhua Wang; 18.09.2019
comment
Это сработало лучше всего. model.matrix, похоже, не уважает na.action. - person Taylor White; 17.06.2021