有效地引入一个因子向量的新水平
问题描述:
我有一个包含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
(以下答复中提到)比任慢。 addNA
对NA
值进行了一些额外检查,并将新等级设置为最后一个等级(要求我重新命名)并将其命名为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).
答
如果你想预晶圆厂的解决方案可以使用fct_explicit_na
其次fct_relevel
从forcats
包。它比你的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
简单地做'addNA'不设置NA电平作为基准电平,也不会与所希望的水平名称替换。这不起作用:'再次(addNA(x),ref = NA_character_)'' – C8H10N4O2