我有一个包含NA
值的类因子的长向量。
# simple example
x <- factor(c(NA,'A','B','C',NA), levels=c('A','B','C'))
出于建模目的,我希望将这些NA
值替换为新的因子水平(例如,"未知"),并将该水平设置为参考水平。
由于替换级别不是现有级别,因此简单替换不起作用:
# this won't work, since the replacement value is not an existing level of the factor
x[is.na(x)] <- '?'
x # returns: [1] <NA> A B C <NA> -- the NAs remain
# this doesn't work either:
replace(x, NA,'?')
我想出了几个解决方案,但两者都有点丑陋,而且速度惊人。
f1 <- function(x, uRep='?'){
# convert to character, replace NAs with Unknown, and convert back to factor
stopifnot(is.factor(x))
newLevels <- c(uRep,levels(x))
x <- as.character(x)
x[is.na(x)] <- uRep
factor(x, levels=newLevels)
}
f2 <- function(x, uRep='?'){
# add new level for Unknown, replace NAs with Unknown, and make Unknown first level
stopifnot(is.factor(x))
levels(x) <- c(levels(x),uRep)
x[is.na(x)] <- uRep
relevel(x, ref=uRep)
}
f3 <- function(x, uRep='?'){ # thanks to @HongOoi
y <- addNA(x)
levels(y)[length(levels(y))]<-uRep
relevel(y, ref=uRep)
}
#test
f1(x) # works
f2(x) # works
f3(x) # works
解决方案 #2 是仅编辑(相对较小的)关卡集,外加一个算术运算以重新调平。 我本来希望这比#1更快,#1是投射到角色并回到因素。
但是,#2 在具有 10 个级别和 10% NA 的 10K 元素的基准向量上慢两倍。
x <- sample(factor(c(LETTERS[1:10],NA),levels=LETTERS[1:10]),10000,replace=TRUE)
library(microbenchmark)
microbenchmark(f1(x),f2(x),f3(x),times=500L)
# Unit: microseconds
# expr min lq mean median uq max neval
# f1(x) 271.981 278.1825 322.4701 313.0360 360.7175 609.393 500
# f2(x) 651.728 703.2595 768.6756 747.9480 825.7800 1517.707 500
# f3(x) 808.246 883.2980 966.2374 927.5585 1061.1975 1779.424 500
解决方案#3,我的内置addNA
包装器(在下面的答案中提到)比任何一个都慢。addNA
对NA
值进行了一些额外的检查,并将新级别设置为最后一个级别(需要我重新分级)并命名为 NA(然后在重新分级之前需要按索引重命名,因为 NA 很难访问 -relevel(addNA(x), ref=NA_character_))
不起作用)。
有没有更有效的方法来写这个,或者我只是被灌了?
如果需要预制件解决方案,可以使用fct_explicit_na
后跟forcats
包中的fct_relevel
。它比你的f1
函数慢,但它仍然可以在长度为 100,000 的向量上运行几分之一秒:
library(forcats)
x <- factor(c(NA,'A','B','C',NA), levels=c('A','B','C'))
[1] <NA> A B C <NA> Levels: A B C
x = fct_relevel(fct_explicit_na(x, "Unknown"), "Unknown")
长度
为 100,000 的向量上的
[1] Unknown A B C Unknown
Levels: Unknown A B C
时序:
x <- sample(factor(c(LETTERS[1:10],NA), levels=LETTERS[1:10]), 1e5, replace=TRUE)
microbenchmark(forcats = fct_relevel(fct_explicit_na(x, "Unknown"), "Unknown"),
f1 = f1(x),
unit="ms", times=100L)
Unit: milliseconds expr min lq mean median uq max neval cld forcats 7.624158 10.634761 15.303339 12.162105 15.513846 250.0516 100 b f1 3.568801 4.226087 8.085532 5.321338 5.995522 235.2449 100 a
有一个内置函数addNA
用于此。
从 ?因素:
addNA(x, ifany = FALSE)
addNA modifies a factor by turning NA into an extra level (so that NA values are counted in tables, for instance).