有效地引入一个因子向量的新水平

问题描述:

我有一个包含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更快,这是铸造人物并回归原因。

然而,#2是10K元素的基准向量的两倍,10个元素具有10个等级和10%的NA。

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_))不起作用) 。

有没有更有效的方法来写这个,或者我刚刚被洗净?

这里有一个内建函数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). 
+0

简单地做'addNA'不设置NA电平作为基准电平,也不会与所希望的水平名称替换。这不起作用:'再次(addNA(x),ref = NA_character_)'' – C8H10N4O2

如果你想预晶圆厂的解决方案可以使用fct_explicit_na其次fct_relevelforcats包。它比你的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") 
[1] Unknown A  B  C  Unknown 
Levels: Unknown A B C 

时序上长度为100,000的矢量:

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