【问题标题】:Efficiently compute mean and standard deviation from a frequency table从频率表中高效计算均值和标准差
【发布时间】:2012-05-10 23:20:35
【问题描述】:

假设我有以下频率表。

> print(dat)
V1    V2
1  1 11613
2  2  6517
3  3  2442
4  4   687
5  5   159
6  6    29

# V1 = Score
# V2 = Frequency

如何有效地计算平均值和标准差? 产率:SD=0.87 MEAN=1.66。 按频率复制分数的计算时间太长。

【问题讨论】:

    标签: r statistics mean


    【解决方案1】:

    我可能遗漏了一些东西,但这似乎工作得很快,甚至在频率列中替换了数百万:

    dset <- data.frame(V1=1:6,V2=c(11613,6517,2442,687,159,29))
    mean(rep(dset$V1,dset$V2))
    #[1] 1.664102
    sd(rep(dset$V1,dset$V2))
    #[1] 0.8712242
    

    【讨论】:

      【解决方案2】:

      平均很容易。 SD 有点棘手(不能再次使用 fastmean(),因为分母中有一个 n-1。

      > dat <- data.frame(freq=seq(6),value=runif(6)*100)
      > fastmean <- function(dat) {
      +   with(dat, sum(freq*value)/sum(freq) )
      + }
      > fastmean(dat)
      [1] 55.78302
      > 
      > fastRMSE <- function(dat) {
      +   mu <- fastmean(dat)
      +   with(dat, sqrt(sum(freq*(value-mu)^2)/(sum(freq)-1) ) )
      + }
      > fastRMSE(dat)
      [1] 34.9316
      > 
      > # To test
      > expanded <- with(dat, rep(value,freq) )
      > mean(expanded)
      [1] 55.78302
      > sd(expanded)
      [1] 34.9316
      

      请注意,fastRMSE 会计算两次 sum(freq)。消除这一点可能会导致另一次轻微的速度提升。

      基准测试

      > microbenchmark(
      +   fastmean(dat),
      +   mean( with(dat, rep(value,freq) ) )
      +   )
      Unit: microseconds
                                     expr    min      lq median     uq    max
      1                     fastmean(dat) 12.433 13.5335 14.776 15.398 23.921
      2 mean(with(dat, rep(value, freq))) 21.225 22.3990 22.714 23.406 86.434
      > dat <- data.frame(freq=seq(60),value=runif(60)*100)
      > 
      > dat <- data.frame(freq=seq(60),value=runif(60)*100)
      > microbenchmark(
      +   fastmean(dat),
      +   mean( with(dat, rep(value,freq) ) )
      +   )
      Unit: microseconds
                                     expr    min     lq  median      uq     max
      1                     fastmean(dat) 13.177 14.544 15.8860 17.2905  54.983
      2 mean(with(dat, rep(value, freq))) 42.610 48.659 49.8615 50.6385 151.053
      > dat <- data.frame(freq=seq(600),value=runif(600)*100)
      > microbenchmark(
      +   fastmean(dat),
      +   mean( with(dat, rep(value,freq) ) )
      +   )
      Unit: microseconds
                                     expr      min       lq    median       uq       max
      1                     fastmean(dat)   15.706   17.489   25.8825   29.615    79.113
      2 mean(with(dat, rep(value, freq))) 1827.146 2283.551 2534.7210 2884.933 26196.923
      

      复制的解决方案似乎是 O( N^2 ) 在条目数上。

      fastmean 解决方案似乎有 12 毫秒左右的固定成本,之后它可以很好地扩展。

      更多基准测试

      Comparison with dot product.
      
      dat <- data.frame(freq=seq(600),value=runif(600)*100)
      dbaupp <- function(dat) {
        total.count <- sum(dat$freq)
        as.vector(dat$freq %*% dat$value) / total.count
      }
      microbenchmark(
        fastmean(dat),
        mean( with(dat, rep(value,freq) ) ),
        dbaupp(dat)
      )
      
      Unit: microseconds
                                     expr     min       lq   median       uq       max
      1                       dbaupp(dat)  20.162  21.6875  25.6010  31.3475   104.054
      2                     fastmean(dat)  14.680  16.7885  20.7490  25.1765    94.423
      3 mean(with(dat, rep(value, freq))) 489.434 503.6310 514.3525 583.2790 30130.302
      

      【讨论】:

        【解决方案3】:

        怎么样:

        > m = sum(dat$V1 * dat$V2) / sum(dat$V2)
        > m
        [1] 1.664102
        > sigma = sqrt(sum((dat$V1 - m)**2 * dat$V2) / (sum(dat$V2)-1))
        > sigma
        [1] 0.8712242
        

        这里没有复制。

        【讨论】:

          【解决方案4】:

          以下代码不使用复制,它尽可能使用 R 内置函数(尤其是对于点积),因此它可能比使用 sum(V1 * V2) 的解决方案更有效。 (编辑:这可能是错误的:@gsk3 的解决方案似乎比我的测试快 1.5 - 2 倍。)

          平均值

          均值(或期望)的定义是sum(n * freq(n)) / total.count,其中n 是“分数”,freq(n)n 的频率(total.count 就是sum(freq(n)))。分子中的总和恰好是频率分数的dot product

          R 中的点积是%*%(它返回一个矩阵,但在大多数情况下,这基本上可以在向量中处理):

          > total.count <- sum(dat$V2)
          > mean <- dat$V1 %*% dat$V2 / total.count
          > mean
                   [,1]
          [1,] 1.664102
          

          标清

          the end of this section of the Wikipedia article有一个公式,翻译成如下代码

          > sqrt(dat$V1^2 %*% dat$V2 / total.count - mean^2)
                    [,1]
          [1,] 0.8712039
          

          【讨论】:

          • 另外,我不确定它是否更快(请参阅我的基准测试)。我不同意内置总是更快的暗示(参见mean()!)。但这是一个不错的解决方案,而且可以很好地扩展。
          • @gsk3,不,total.count 是事物/样本的总数,所以 dat$V2。是的,我自己刚刚做了一些基准测试并得出了相同的结论(我只是在编辑我对这种影响的答案)。给 R 提供更多信息的操作(即%*%)比天真的操作慢,这有点奇怪!
          • 哦,这很有趣。当我编写示例时,我一直在相反的前提下工作,但最初的问题清楚地表明 V2 是频率。没关系,因为我还是重命名了它们:-)。不幸的是,R 内置函数并不总是(通常?)针对速度进行优化。我希望有人花一些时间为 Summer of Code 之类的东西优化一些东西。
          • 看起来Julia 可能很快就会成为合适的替代品(他们从一开始就专注于性能!)。此外,取消as.vector 调用会使它们更快一些(但没有我想象的那么快,如果有人看到我的编辑,哈哈)。
          • @Tommy ,不,您不知道所有 Julia 代码至少比 Julia 的创建者编写的用于比较它的 R 代码快 60 倍吗?这是他们的版本:Rmean &lt;- function(dat) { Sys.sleep(10); fastmean(dat) }
          猜你喜欢
          • 1970-01-01
          • 2014-03-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-15
          • 2016-04-17
          相关资源
          最近更新 更多