在R中获取向量元素频率的最简单方法

4
假设我有一个值的向量v。如何最简单地获取长度等于v的向量f,其中f的第i个元素是v中第i个元素在v中的频率?
我知道的唯一方法似乎过于复杂:
v = sample(1:10,100,replace=TRUE)
D = data.frame( idx=1:length(v), v=v )
E = merge( D, data.frame(table(v)) )
E = E[ with(E,order(idx)), ]
f = E$Freq

当然,有一种更简单的方法可以做到这一点,类似于“frequencies(v)”吗?
4个回答

2
对于一个小正整数向量 v,如问题所述,表达式
tabulate(v)[v]

这个程序特别简单且快速。

对于更一般的数值向量v,您可以使用ecdf来帮助您,例如:

w <- sapply(v, ecdf(v)) * length(v)
tabulate(w)[w]

虽然这种方法可以解决问题,但最好自己编写底层算法的代码,这样可以避免前面解决方案中隐含的浮点舍入误差。

frequencies <- function(x) {
  i <- order(x)
  v <- x[i]
  w <- cumsum(c(TRUE, v[-1] != v[-length(x)]))
  f <- tabulate(w)[w]
  return(f[order(i)])
}

这个算法对数据进行排序,遇到数值时将其分配顺序标识符1、2、3等(通过总和二进制指示器来记录数值的变化),使用之前的tabulate()[]技巧有效地获取频率,然后逐个组件将结果取消排序以使输出与输入匹配。


我相信这种方法是最合适的。此外,在非整数v中,您可以使用w = match(v, unique(v))来提供tabulate - alexis_laz
@alexis 谢谢你的建议。它提供了一个清晰易懂的解决方案。虽然我还没有测试过,但我怀疑使用 matchunique 可能会显著减慢速度。 - whuber
通过使用 match 函数测试你的 frequencies 中的 order[ 部分(即前两行):x = runif(1e5); microbenchmark::microbenchmark(x[order(x)], match(x, unique(x)), times = 30);可以发现,match 函数似乎更快。 - alexis_laz
@alexis 非常有趣!我将来会更加关注match - whuber

2
我认为这里最好的解决方案是:
ave(v,v,FUN=length)

它仅仅是 ave() 的设计,将 FUN() 的返回值复制并映射回每个输入向量的索引,其元素属于该特定调用 FUN() 执行的组。


1
+1,因为我不知道 ave:谢谢! 它在语法上很简单且相当明确。 但如果性能是个问题,那么结果表明 ave 的速度大约是我的答案中“frequency”函数的两倍(对于长度大于100M的非常小的平均频率向量除外:在小平均频率情况下,ave 更好缩放)。 - whuber

1
像这样的东西对我来说是有效的:

sapply(v, function(elmt, vec) sum(vec == elmt), vec=v)

我认为你甚至不需要单独提供“vec”。也就是说,对我来说,简单地使用sapply(v, function(x) { sum(x==v) })也可以。 - Tamas Ferenci
你说得对,我只是不喜欢在我的函数中使用全局变量 ;) - Vincent Guillemot
你说得完全正确,那是更好的编码实践,我也这么认为 :) - Tamas Ferenci

0
我建议您使用table和as.vector:
as.vector(table(dataInVector))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接