Почему R изменяет тип переменной при добавлении значений NA в фрейм данных с факторами?

у меня проблема с тем, как R coerces типы переменных при использовании rbind два data.frames С NA значения. Проиллюстрирую на примере:

x<-factor(sample(1:3,10,T))
y<-rnorm(10)
dat<-data.frame(x,y)
NAs<-data.frame(matrix(NA,ncol=ncol(dat),nrow=nrow(dat)))
colnames(NAs)<-colnames(dat)

теперь цель состоит в том, чтобы добавить dat и NAs сохраняя типы переменных factor и numeric of x и y. Когда я даю:

dat_forward<-rbind(dat,NAs)
is.factor(dat_forward$x)

это прекрасно работает. Однако обратное направление с помощью rbind не удается:

dat_backward<-rbind(NAs,dat)
is.factor(dat_backward$x)
is.character(dat_backward$x)

теперь x приводится к уровню персонажа. Я я смущен - не может ли он оставаться типом фактора, даже если я использую другой порядок привязки? Что было бы прямым изменением моего кода для достижения моей цели?

5 ответов


вот довольно простой способ получить классы столбцов правильно:

x <- rbind(dat[1,], NAs, dat)[-1,]
str(x)
#  $ x: Factor w/ 3 levels "1","2","3": NA NA NA NA NA NA NA NA NA NA ...
#  $ y: num  NA NA NA NA NA NA NA NA NA NA ...

в более общем плане, если вы действительно нуждаясь в этом часто, вы можете создать rbind - like функция, которая принимает дополнительный аргумент, указывающий данные.фрейм, к классам столбцов которого вы хотите принудить все остальные столбцы:

myrbind <- function(x, ..., template=x) {
    do.call(rbind, c(list(template[1,]), list(x), list(...)))[-1,]
}

str(myrbind(NAs, dat,  template=dat))
# 'data.frame': 20 obs. of  2 variables:
#  $ x: Factor w/ 3 levels "1","2","3": NA NA NA NA NA NA NA NA NA NA ...
#  $ y: num  NA NA NA NA NA NA NA NA NA NA ...

## If no 'template' argument is supplied, myrbind acts just like rbind    
str(myrbind(dat, NAs))
# 'data.frame': 20 obs. of  2 variables:
#  $ x: Factor w/ 3 levels "1","2","3": 3 3 3 3 2 3 1 1 3 2 ...
#  $ y: num  0.303 1.77 -1.38 1.731 0.033 ...

аналогично, вы можете просто преобразовать столбец в NAs to factor

NAs$x<-factor(NAs$x)
dat_backward<-rbind(NAs,dat) 
is.factor(dat_backward$x) # TRUE
is.character(dat_backward$x) # FALSE

data.frame делает много вещей неправильно, когда rbind ' ing различных типов вместе, и особенно, когда это включает в себя факторы. Начните использовать data.table (1.8.11+) вместо этого, и у вас не будет этих проблем:

library(data.table)
dt1 = data.table(dat)
dt2 = data.table(NAs)

sapply(rbind(dt1, dt2), class)
#        x         y 
# "factor" "numeric" 
sapply(rbind(dt2, dt1), class)
#        x         y 
# "factor" "numeric" 

С ?rbind.data.frame, мы читаем: "затем он берет классы столбцов из первого фрейма данных...". Вот почему вы видите, что порядок имеет значение в вашем вызове rbind.

чтобы получить классы переменных dat_forward на заказ dat_backward, вы могли бы просто построить dat_forward и изменить порядок строк:

dat_new = rbind(dat, NAs)[c((nrow(dat)+1):(nrow(dat)+nrow(NAs)), 1:nrow(dat)),]
str(dat_new)
# 'data.frame': 20 obs. of  2 variables:
#  $ x: Factor w/ 3 levels "1","2","3": NA NA NA NA NA NA NA NA NA NA ...
#  $ y: num  NA NA NA NA NA NA NA NA NA NA ...

одним из подходов было бы создать NAs с правильными типами данных столбцов. Это можно легко сделать с помощью

NAs <- dat[NA,]

вы также можете сделать столько строк, сколько хотите с

num.rows <- 30
NAs <- dat[NA,][1:num.rows,]