我经常碰到这种情况,以至于我认为必须有一个很好的成语。假设我有一个带一堆属性的data.frame,包括“产品”。我还有一把将产品转换成品牌+尺寸的钥匙。产品代码1-3是Tylenol,4-6是Advil,7-9是Bayer,10-12是Generic。
什么是最快的方式(就人类时间而言)?
如果有3个或更少的类别,我倾向于使用嵌套的ifelse
,如果有3个以上的类别,则键入数据表并将其合并。还有更好的主意吗? Stata的recode
command对于这种事情非常漂亮,尽管我相信它会促进数据代码混合太多。
dat <- structure(list(product = c(11L, 11L, 9L, 9L, 6L, 1L, 11L, 5L,
7L, 11L, 5L, 11L, 4L, 3L, 10L, 7L, 10L, 5L, 9L, 8L)), .Names = "product", row.names = c(NA,
-20L), class = "data.frame")
最佳答案
可以使用列表作为关联数组来定义brand -> product code
映射,即:
brands <- list(Tylenol=1:3, Advil=4:6, Bayer=7:9, Generic=10:12)
一旦有了它,就可以将其反转以创建
product code -> brand
列表(可能占用大量内存),或者仅使用搜索功能:find.key <- function(x, li, default=NA) {
ret <- rep.int(default, length(x))
for (key in names(li)) {
ret[x %in% li[[key]]] <- key
}
return(ret)
}
我敢肯定有更好的方法来编写此函数(
for
循环让我很烦!),但是至少它是矢量化的,因此只需要遍历列表即可。使用它就像:
> dat$brand <- find.key(dat$product, brands)
> dat
product brand
1 11 Generic
2 11 Generic
3 9 Bayer
4 9 Bayer
5 6 Advil
6 1 Tylenol
7 11 Generic
8 5 Advil
9 7 Bayer
10 11 Generic
11 5 Advil
12 11 Generic
13 4 Advil
14 3 Tylenol
15 10 Generic
16 7 Bayer
17 10 Generic
18 5 Advil
19 9 Bayer
20 8 Bayer
recode
和levels<-
解决方案非常好,但是它们也比该解决方案慢得多(一旦您拥有find.key
,它对人类来说比recode
更容易,并且与levels<-
相当) :> microbenchmark(
recode=recode(dat$product,recodes="1:3='Tylenol';4:6='Advil';7:9='Bayer';10:12='Generic'"),
find.key=find.key(dat$product, brands),
levels=`levels<-`(factor(dat$product),brands))
Unit: microseconds
expr min lq median uq max
1 find.key 64.325 69.9815 76.8950 83.8445 221.748
2 levels 240.535 248.1470 274.7565 306.8490 1477.707
3 recode 1636.039 1683.4275 1730.8170 1855.8320 3095.938
(我无法正确地对
switch
版本进行基准测试,但它似乎比上述所有版本都要快,尽管它对人类而言甚至比recode
解决方案还差。)