我经常碰到这种情况,以至于我认为必须有一个很好的成语。假设我有一个带一堆属性的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


recodelevels<-解决方案非常好,但是它们也比该解决方案慢得多(一旦您拥有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解决方案还差。)

10-07 16:40