r - 在因子向量上有效地引入新水平



我有一个包含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包装器(在下面的答案中提到)比任何一个都慢。addNANA值进行了一些额外的检查,并将新级别设置为最后一个级别(需要我重新分级)并命名为 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).

相关内容

最新更新