【问题标题】:Evaluate code within a function call in R (Use ICC::ICCbare within a loop)在 R 中的函数调用中评估代码(在循环中使用 ICC::ICCbare)
【发布时间】:2014-11-05 08:21:02
【问题描述】:

我想在循环中使用ICC::ICCbare 函数。但是,ICCbare 使用具体的变量名称作为输入,例如:

ICCbare(x = group, y = variable1, data = dat)

其中“group”和“variable1”都是data.frame“dat”的列(即dat$variable1); ICCbare不能与y = dat[, i]一起使用。

为了编写一个循环,我需要在 ICCbare 的函数调用中评估一些 R 代码。我的想法如下:

for(i in 1:10){
  ICCbare(group, names(dat)[i], data = dat)  
}

但是,这不起作用。打印以下错误:

Error in '[.data.frame`(data, yc) : undefined columns selected'

有没有办法在将语句names(dat)[i]) 传递给函数调用之前先对其进行评估?

这是我的问题的最小工作示例:

# Create data set
dat <- data.frame(group=c(rep("A",5), 
                  rep("B",5)), 
                  variable1=1:10, 
                  variable2=rnorm(10))

# Loop
for (i in names(dat)[2:3]){
  ICCbare("group", i, data = dat)
} 

【问题讨论】:

  • 我怀疑你想多了。如果我正确理解了ICCbare 的文档(没有安装软件包),您应该将characters 传递给列名。不是这样吗?您在循环外使用的示例似乎表明使用了非标准评估。真的是这样吗?ICCbare(x = "group", y = "variable1", data = dat) 不起作用?
  • 是的 ICCbare(x = "group", y = "variable1", data = dat) 确实有效。但是,我不太确定这对我的“循环问题”有何帮助?
  • 那么,你试过for(i in 1:10) ICCbare("group", names(dat)[i], data = dat)吗?
  • 如果@Roland 的解决方案有效,for (i in names(dat)) ICCbare("group", i, data = dat) 也应该有效。
  • 两种解决方案——不幸的是——也不适合我。他们给了我和上面一样的错误信息。

标签: r


【解决方案1】:

我同意@agstudy。这是非标准评估的一个坏例子。您可以将其用作解决方法:

v <- "variable1"
ICCbare("group",  v, data = dat)
#Error in `[.data.frame`(data, yc) : undefined columns selected

eval(bquote(ICCbare("group",  .(v), data = dat)))
#$ICC
#[1] 0.8275862

【讨论】:

    【解决方案2】:

    这是ICCbare 中的一个错误,它试图以错误的方式将参数管理为name

    function (x, y, data) 
    {
      ICCcall <- Call <- match.call()
      xc <- as.character(ICCcall[[2L]])  ## this is ugly!
      yc <- as.character(ICCcall[[3L]])  
      inds <- unique(data[xc])[[1]]
      tdata <- data.frame(data[yc], data[xc])
    

    我个人会删除第一行并假设参数只是列名。

    ICCbare_simple <- 
    function (xc, yc, data) 
    {
      ## remove lines before this one 
      inds <- unique(data[xc])[[1]]
      ## the rest of the code 
      .....
    }
    

    【讨论】:

    • 这两个答案都是一个很好的解决方案。但是,我接受了 Roland 的建议,因为它是一个很好的解决方法,不需要在 ICCbare 中进行任何更改。不过还是谢谢!
    • @phx 您应该将此报告给包维护者。他们应该解决这个问题。
    • 我也已经邮寄了包维护者。他将来可能会改变他的职能中的参数管理。
    【解决方案3】:

    我是ICC 的维护者,我要感谢您的精彩讨论。我知道这是一个很晚的回复,但我刚刚更新了软件包,新版本(v2.3.0)应该修复了“丑陋”的代码和 OP 遇到的问题。请参阅this gist 中的示例。

    我只是想在此处发布此内容,以防有人搜索类似问题。再次感谢,抱歉耽搁了。

    这里是要点的内容:


    ICC非标准评估示例

    RICC 包通过单向方差分析计算组内相关系数 (ICC)。最近,该包已更新,以更好地在每个函数(2.3.0 及更高版本)内执行 R 的非标准评估。包函数现在应该能够处理调用函数的一系列可能场景,我希望这是一种不那么怪诞和更标准的编写 R 函数的方式。为了演示,下面是其中一些场景。请注意,示例使用ICCbare 函数,但提供函数参数的方式将适用于ICC 中的所有函数。

    首先,加载包(并确保版本>2.3.0)

    library(ICC)
    packageVersion("ICC")
    

    data.frame 的列

    在这里,我们提供列名和包含用于计算 ICC 的数据的 data.frame。我们将使用ChickWeight 数据名。

    data(ChickWeight)
    ICCbare(x = Chick, y = weight, data = ChickWeight)
    #$ICC
    #[1] 0.1077609
    

    遍历data.frame 的列

    在这种情况下,我们可能有一个 data.frame,我们希望在其中估计许多不同类型测量的 ICC,每个测量具有相同的分组或因子变量(例如,x)。极端情况可能是在模拟或引导场景中,甚至是一些花哨的高通量表型/数据收集。关键是,我们希望自动计算每一列的 ICC。

    首先,我们将在示例中使用 3 个特征来模拟我们自己的数据集:

    set.seed(101)
    n <- 15                                    # number of individuals/groups/categories/factors
    k <- 3                                     # number of measures per 'n'
    va <- 1                                    # variance among
    icc <- 0.6                                 # expected ICC
    vw <- (va * (1 - icc)) / icc               # solve for variance within
    simdf <- data.frame(ind = rep(LETTERS[1:n], each = k),
       t1 = rep(rnorm(n, 10, sqrt(va)), each = k) + rnorm(n*k, 0, sqrt(vw)),
       t2 = rep(rnorm(n, 10, sqrt(va)), each = k) + rnorm(n*k, 0, sqrt(vw)),
       t3 = rep(rnorm(n, 10, sqrt(va)), each = k) + rnorm(n*k, 0, sqrt(vw)))
    

    想到了两种遍历列的方法:迭代传递每列的名称或迭代传递列索引。我将在下面演示两者。我在for 循环中执行这些操作,因此更容易看到,但一个简单的扩展是通过使用apply 系列函数中的某些东西来对其进行矢量化。首先,传递名称:

    for(i in names(simdf)[-1]){
       cat(i, ":")
       tmp.icc <- ICCbare(x = ind, y = i, data = simdf)
       cat(tmp.icc, "\n")
    }
    #t1 : 0.60446 
    #t2 : 0.6381197 
    #t3 : 0.591065 
    

    甚至像这样:

    for(i in 1:3){
       cat(paste0("t", i), ": ")
       tmp.icc <- ICCbare(x = ind, y = paste0("t", i), data = simdf)
       cat(tmp.icc, "\n")
    }
    #t1 : 0.60446 
    #t2 : 0.6381197 
    #t3 : 0.591065 
    

    或者,传递列索引:

    for(i in 2:ncol(simdf)){
       cat(names(simdf)[i], ": ")
       tmp.icc <- ICCbare(x = ind, y = simdf[, i], data = simdf)
       cat(tmp.icc, "\n")
    }
    #t1 : 0.60446 
    #t2 : 0.6381197 
    #t3 : 0.591065 
    

    不推荐将字符作为参数传递

    请注意,如果直接传递一个字符(例如,"t1"),该函数仍然可以工作,尽管使用了warning。该警告仅意味着这可能不再适用于该软件包的未来版本。例如:

    ICCbare(x = ind, y = "t1", data = simdf)
    #[1] 0.60446
    #Warning message:
    #In ICCbare(x = ind, y = "t1", data = simdf) :
    #  passing a character string to 'y' is deprecated since ICC version
    #  2.3.0 and will not be supported in future versions. The argument 
    #  to 'y' should either be an unquoted column name of 'data' or an object
    

    但是请注意,对字符求值的表达式(例如,paste0("t", 1))不会抛出 warning,这很好!

    【讨论】:

    • 这并没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方发表评论 - 您可以随时评论自己的帖子,一旦您有足够的reputation,您就可以comment on any post
    • 我想发表评论,但因为我刚刚注册,所以无法评论其他帖子(如@Pino 所说)。我完全意识到这不是一个明确的答案,但希望得到可用的信息,以防以后有人发现这篇文章。对不起,如果我没有遵守协议,但在我的情况下,写一个“答案”是我能做任何事情的唯一方法!
    • 我已经粘贴并格式化了要点内容,以使其成为更完整的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 2020-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多