which() 咩?

谢益辉 2017-04-19

R 里面有个 which() 函数,我感觉很多人一旦学了就忘不了,总是想用。它可以把逻辑向量转化为整数向量,这些整数表示哪些位置上的值是 TRUE,如 which(c(F, T, F)) 就会返回 2。我见过好些人在用下标索引的时候硬生生把逻辑向量用 which() 转一下,比如 x[which(y)]y 是逻辑向量),这个 which() 的调用完全没必要,因为 R 有三种索引方式:整数值、逻辑值、字符(名字)。可能一般人还是习惯整数索引吧。

作为强迫症患者,我尽量不做无谓的强迫,尽管这个有点难。比如要不要提出来 which() 是不必要的,其实不能完全根据自己的喜好来判断,而是要客观测量它究竟有多糟糕。糟不糟糕,主要准则应该是速度,比如用了 which() 会导致代码显著变慢吗?理论上多调用一个函数肯定会更慢,但我用三秒钟的时间想了一下,万一 which() 慢,但整数索引比逻辑索引更快,那时间有没有可能追回来?

空谈误国,放码过来。前方测速区:

n = 100000
x = runif(n)
i = logical(n)
i[sample(n, n/4)] = TRUE

microbenchmark::microbenchmark(x[i], x[which(i)], times = 1000)

跑了一下,貌似没那么糟糕。

Unit: microseconds
        expr   min    lq  mean median    uq   max neval
        x[i] 362.4 488.6 719.6  577.2 669.7  6723  1000
 x[which(i)] 438.4 581.3 900.7  670.2 795.8 82968  1000

我还有个类似的洁癖,就是不愿意看到 if (x == TRUE) 这种写法,if (x) 就足够。去年在芝加哥参加 JSM 的时候我已经吐槽过一次。不过呢,把 TRUE 写出来倒也不是完全没有好处:它的潜在好处是快速扫描代码的时候很容易知道这里有个逻辑值,因为这四个大写字母比较耀眼。