【问题标题】:Fast post hoc computation using R使用 R 的快速事后计算
【发布时间】:2019-01-26 23:24:06
【问题描述】:

我有一个大型数据集,我想执行事后计算:

dat = as.data.frame(matrix(runif(10000*300), ncol = 10000, nrow = 300))

dat$group = rep(letters[1:3], 100)

这是我的代码:

start <- Sys.time()

vars <- names(dat)[-ncol(dat)] 

aov.out <- lapply(vars, function(x) {
        lm(substitute(i ~ group, list(i = as.name(x))), data = dat)})

TukeyHSD.out <- lapply(aov.out, function(x) TukeyHSD(aov(x)))

Sys.time() - start

时差4.033335分钟

大约需要 4 分钟,是否有更高效、更优雅的方式来使用 R 执行事后处理?

非常感谢

【问题讨论】:

    标签: r processing-efficiency posthoc


    【解决方案1】:

    你的例子太大了。为了说明这个想法,我使用了一个小的。

    set.seed(0)
    dat = as.data.frame(matrix(runif(2*300), ncol = 2, nrow = 300))
    dat$group = rep(letters[1:3], 100)
    

    你为什么在一个合适的“lm”模型上调用aov?这基本上改装了相同的模型。

    首先阅读Fitting a linear model with multiple LHSlmaov 的主力,因此您可以将多个 LHS 公式传递给 aov。该模型具有类c("maov", "aov", "mlm", "lm")

    response_names <- names(dat)[-ncol(dat)]
    form <- as.formula(sprintf("cbind(%s) ~ group", toString(response_names)))
    fit <- do.call("aov", list(formula = form, data = quote(dat)))
    

    现在的问题是:TuckyHSD 没有“maov”方法。所以我们需要黑客攻击。

    TuckyHSD 依赖于拟合模型的残差。在c("aov", "lm") 的情况下,残差是一个向量,但在c("maov", "aov", "mlm", "lm") 的情况下,它是一个矩阵。以下演示了黑客攻击。

    aov_hack <- fit
    aov_hack[c("coefficients", "fitted.values")] <- NULL  ## don't need them
    aov_hack[c("contrasts", "xlevels")] <- NULL  ## don't need them either
    attr(aov_hack$model, "terms") <- NULL  ## don't need it
    class(aov_hack) <- c("aov", "lm")  ## drop "maov" and "mlm"
    ## the following elements are mandatory for `TukeyHSD`
    ## names(aov_hack)
    #[1] "residuals"   "effects"     "rank"        "assign"      "qr"         
    #[6] "df.residual" "call"        "terms"       "model" 
    
    N <- length(response_names)  ## number of response variables
    result <- vector("list", N)
    for (i in 1:N) {
      ## change response variable in the formula
      aov_hack$call[[2]][[2]] <- as.name(response_names[i])
      ## change residuals
      aov_hack$residuals <- fit$residuals[, i]
      ## change effects
      aov_hack$effects <- fit$effects[, i]
      ## change "terms" object and attribute
      old_tm <- terms(fit)  ## old "terms" object in the model
      old_tm[[2]] <- as.name(response_names[i])  ## change response name in terms
      new_tm <- terms.formula(formula(old_tm))  ## new "terms" object
      aov_hack$terms <- new_tm  ## replace `aov_hack$terms`
      ## replace data in the model frame
      aov_hack$model[1] <- data.frame(fit$model[[1]][, i])
      names(aov_hack$model)[1] <- response_names[i]
      ## run `TukeyHSD` on `aov_hack`
      result[[i]] <- TukeyHSD(aov_hack)
      }
    

    result[[1]]  ## for example
    #  Tukey multiple comparisons of means
    #    95% family-wise confidence level
    #
    #Fit: aov(formula = V1 ~ group, data = dat)
    #
    #$group
    #            diff        lwr        upr     p adj
    #b-a -0.012743870 -0.1043869 0.07889915 0.9425847
    #c-a -0.022470482 -0.1141135 0.06917254 0.8322109
    #c-b -0.009726611 -0.1013696 0.08191641 0.9661356
    

    我使用了“for”循环。如果需要,请将其替换为 lapply

    【讨论】:

    • @Dong 该错误现已修复。如果您使用我的方法,模型估计可以快几倍,但与您的原始代码相比,事后并没有得到加速。所以整体加速是有限的。正如我测试的那样,问题不是“for”循环,而是TukeyHSDqtukeyptukey 函数的缓慢性。这两个函数占 post-hoc 执行时间的 60%~70%。对于TukeyHSD,我的黑客攻击并不是一个很好的“maov”方法,因为它不会重复计算qtukey。事实上,这个分位数只需要为所有模型计算一次。
    • @Dong 编写正确的TukeyHSD.maov 涉及更多,尽管我的答案中的代码提供了一个不错的开始。是的,通常 R 核心中对“mlm”和“maov”的支持较差。希望这在未来会变得更好。
    猜你喜欢
    • 2016-04-22
    • 2018-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    • 2012-09-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多