【问题标题】:Loop through columns in a data.table and transform those columns遍历 data.table 中的列并转换这些列
【发布时间】:2012-01-12 13:46:09
【问题描述】:

我有一个 data.table DT,其中有一个名为 RF 的列和许多带有下划线 _ 的列。我想用一个循环遍历所有这些列 下划线并从中减去RF 列。但是,我被困住了。似乎 RHS 上的所有内容 data.table 中的 := 运算符不适用于动态变量。

这是我的DT 和所需的输出(硬编码):

library(data.table)
DT <- data.table(RF  = 1:10,
                 S_1 = 11:20,
                 S_2 = 21:30)
#Desired output
DT[ , S_1 := S_1 - RF]
DT[ , S_2 := S_2 - RF]
DT
      RF S_1 S_2
 [1,]  1  10  20
 [2,]  2  10  20
 [3,]  3  10  20
...

但是,我希望它更灵活,即遍历名称中带有“_”的每一列并减去 RF

#1. try: Does not work; Interestingly, the i on the LHS of := is interpreted as the column i, but on the RHS of
#:= it is interpreted as 2 and 3, respectively
for (i in grep("_", names(DT))){
  DT[ , i:= i - 1, with=FALSE]
}
DT
          RF  S_1 S_2
 [1,]  1   1   2
 [2,]  2   1   2
 [3,]  3   1   2
...

#2. try: Work with parse and eval
for (i in grep("_", names(DT), value=TRUE)){
  DT[ , eval(parse(text=i)):= eval(parse(text=i)) - RF]
}
#Error in eval(expr, envir, enclos) : object 'S_1' not found

任何提示如何做到这一点都会很棒。

编辑:我一发布这个问题,我就想:你为什么首先使用:= 运算符,果然,我刚刚意识到我不必这样做。这确实有效,不需要循环:

DT[, grep("_", names(DT)), with=FALSE] - DT[, RF]

对此感到抱歉。但是,我没有回答这个问题,因为我仍然对为什么我使用 := 运算符的方法不起作用感兴趣。所以也许有人可以帮助我。

【问题讨论】:

  • 另外一个用于自行查找基本答案——并让我们知道。 :-)
  • 两个想法:1)我会将此作为答案发布并接受它,以及 2)使用 grep() 而不是 grepl() 有什么好处/坏处?
  • @Chase 好的,我会这样做的。但是,我无法让示例与 grepl 一起使用,无论是 with=FALSE 还是没有它。感谢你们俩的帮助!编辑:我会在两天内接受答案,显然我不能更早。
  • +1 表示有趣的问题和可重复的示例,尤其是在找到解决方案后将其搁置。

标签: r data.table


【解决方案1】:

我在发布问题后不幸发现的解决方法如下:

DT[, .SD, .SDcols = patterns('_')] - DT[, RF]

这也适用于更复杂的设置,在该设置中您想要保留额外的列,但需要付出一些额外的努力:

library(data.table)
DT <- data.table(RF  = 1:10,
                 S_1 = 11:20,
                 S_2 = 21:30,
                 addCol = rnorm(10)) #Column that should not be subtracted by RF, but still kept in DT

DT <- cbind(DT[, .SD, .SDcols = patterns("_")] - DT[, RF], addCol = DT[, addCol])

【讨论】:

    【解决方案2】:

    您的第二次尝试走在了正确的轨道上。这是一种使用substitute 构建表达式的方法,该表达式作为'j' 参数传入DT[ , j ]

    for (i in grep("_", names(DT), value=TRUE)){
        e <- substitute(X := X - RF, list(X = as.symbol(i)))
        DT[ , eval(e)]
    }
    DT
    #     RF S_1 S_2
    # [1,]  1  10  20
    # [2,]  2  10  20
    # [3,]  3  10  20
    # [4,]  4  10  20
    # [5,]  5  10  20
    

    您也可以使用 LHS 表达式而不是符号:

    for (i in grep("_", names(DT), value=TRUE))
        DT[, (i) := get(i)-RF]
    

    【讨论】:

    • 谢谢@Josh O'Brien,这正是我想要的(虽然我认为循环遍历首先是一个坏主意,请参阅我的编辑[显然,这是我的问题问题,而不是你的答案;-)])。无论如何,现在我将尝试围绕substituteevalquotedeparse 等等...我偶然发现它们几次,我认为它们在制作方面非常强大很多东西都是动态的(就像你的回答一样),但我仍然无法理解它们的全部美……不过,再次感谢你给我指路!
    • 不客气,@Christoph_J。另外,有几点想法。首先,我认为在这种情况下,循环 可能 会更好,因为它允许您利用 data.table 的 modify-by-reference := 运算符。 (我稍后可能会运行一些基准来检查这一点,如果我这样做,我会将结果添加到我的答案中)。其次,要探索substitute 等人,您可能需要在j 中调用browser(),如下所示:for(i in ......) { DT[ , browser()]}。然后你可以在 DT 的“内部”四处寻找,尝试不同的东西(如quote(i)eval(quote(i)as.symbol(eval(quote(i))eval(parse(text=i)) 等)
    • 再次感谢@Josh O'Brien。我知道debug(),但从未直接使用过browser()。这是一个很好的提示!
    • 对于人们如何与parseeval 等作斗争。 Hadley Wickham 有一篇非常有用的文章:Controlling Evaluation
    • 另外,当然,不要在DT[ ] 调用中开始您的调查,它有自己的精心选择,但还有额外的范围规则/行为层! (有关这方面的信息,请参阅例如项目 1.6 和 2.8 vignette("datatable-faq"))。
    【解决方案3】:

    更新为使用set()+..set 功能强大(请参阅早期尝试的编辑)。

    varnames <- grep("_", names(DT), value=TRUE)
    set(DT, j = varnames, value = DT[, ..varnames] - DT[, RF])
    

    【讨论】:

    • 呃。第 3 行和第 5 行使用 [&lt;- 并将复制整个 DT。使用set() 函数可以更轻松地完成此类任务。使用set 似乎还没有答案,你想试试吗?
    • 感谢@MatthewDowle 的提示!
    • NP。此外 DT[[varnames]] 比 with=FALSE 更快更容易。
    • 糟糕,varnames 在这里不是向量。
    • 是的,varnames 是一个向量。
    猜你喜欢
    • 2012-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    相关资源
    最近更新 更多