【问题标题】:Using a list of symbols in data.table's 'by'在 data.table 的“by”中使用符号列表
【发布时间】:2021-02-03 14:57:28
【问题描述】:

我想写一个函数outer_fun(),它做一些事情,还调用另一个函数inner_fun()。来自outer_fun() 的所有参数都传递给inner_fun()

inner_fun()data.table 进行一些计算(这是两个函数的参数)。另一个要通过函数传递的参数是by

这是我所拥有的草图:

library(data.table)

data("CO2")
setDT(CO2)

outer_fun <- function(DT, by) {
    # some other stuff
    by <- substitute(by)
    inner_fun(DT, by)
}

inner_fun <- function(DT, by) {
    DT[, .(mean = mean(uptake)),
    by = list(Plant, by)]
}

outer_fun(CO2, by = Type)

这会引发错误:

Error in `[.data.table`(DT, , .(mean = mean(uptake)), by = list(Plant,  : 
  column or expression 2 of 'by' or 'keyby' is type language. Do not quote column names. Usage: DT[,sum(colC),by=list(colA,month(colB))] 

据我了解这个问题,我必须将by 中的两个列表正确组合到inner_fun() 中。另一种尝试是这样的:

outer_fun <- function(DT, by) {
    # some other stuff
    by <- substitute(by)
    inner_fun(DT, by)
}
inner_fun <- function(DT, by) {
    .by <- append(by, substitute(Plant), after = 0L)
    DT[, .(mean = mean(uptake)),
       by = eval(as.expression(list(.by)))]
}

outer_fun(CO2, by = Type)

这会引发类似的错误:

 Error in `[.data.table`(DT, , .(mean = mean(uptake)), by = eval(as.expression(list(.by)))) : 
  column or expression 1 of 'by' or 'keyby' is type symbol. Do not quote column names. Usage: DT[,sum(colC),by=list(colA,month(colB))] 

我在这里挣扎了两天,非常感谢您的帮助!

编辑: 看来,我还不清楚所需的解决方案应该具备什么能力。结果应该适用于data.table中允许的各种by,例如:

outer_fun(CO2, by = Type)
outer_fun(CO2, by = "Type")
outer_fun(CO2, by = .(Type, Treatment))
outer_fun(CO2, by = c("Type", "Treatment"))
outer_fun(CO2, by = "Type,Treatment")
outer_fun(CO2, by = Treatment == "chilled")
outer_fun(CO2, by = cut(conc, breaks = quantile(conc)))
...

【问题讨论】:

  • 您不想使用像"Type" 这样的字符串吗?对于by 中的两列或更多列,您希望使用哪种语法?
  • 好吧,两者都是(字符串和表达式)。我想保留在 data.table 中工作的所有内容。所以对于两列,c("col1", "col2"), "col1,col2", .(col1, col2)。
  • 我应该补充一点,我需要保持对 col1 > 5 等表达式的支持。
  • 我认为@jangorecki 有一个处理此类用例的 PR

标签: r data.table evaluation


【解决方案1】:

我认为让[.data.table 函数自己处理by 参数会更好。这将使inner_funouter_fun 更容易。缺点是Plant 等其他固定分组变量应在outer_funby 参数中提供。

outer_fun <- function(DT, ...) {
    inner_fun(DT, ...)
}
inner_fun <- function(DT, ...) {
    DT[, .(mean = mean(uptake)), ...]
}

那么,下面所有的例子都是可能的:

outer_fun(CO2, by = .(Plant, Type, Treatment))
outer_fun(CO2, by = c("Plant", "Type", "Treatment"))
outer_fun(CO2, by = "Plant,Type,Treatment")
outer_fun(CO2, by = .(Plant, Treatment == "chilled"))
outer_fun(CO2, by = .(Plant, cut(conc, breaks = quantile(conc), include.lowest = T)))

【讨论】:

  • 感谢您的建议。这确实简化了很多。但我真的需要那个固定的by
【解决方案2】:

我会通过最终将 by 信息作为字符提供来解决这个问题

library(data.table)

data("CO2")
setDT(CO2)

outer_fun <- function(DT, by) {
  # some other stuff
  by <- substitute(by)
  inner_fun(DT, by)
}

inner_fun <- function(DT, by) {

  # add Plant to the by Information
  byFun <- c("Plant", as.character(by))

  # remove list oder c()- function names
  byFun <- byFun[!byFun %in% c(".", "list", "c")] 

  DT[, .(mean = mean(uptake)),
     by = byFun]
 }

# single column name unquoted
outer_fun(CO2, by = Type)[1:3]
#>    Plant   Type     mean
#> 1:   Qn1 Quebec 33.22857
#> 2:   Qn2 Quebec 35.15714
#> 3:   Qn3 Quebec 37.61429


#list of column names unquoted
outer_fun(CO2, by = .(Type, Treatment))[1:3]
#>    Plant   Type  Treatment     mean
#> 1:   Qn1 Quebec nonchilled 33.22857
#> 2:   Qn2 Quebec nonchilled 35.15714
#> 3:   Qn3 Quebec nonchilled 37.61429

outer_fun(CO2, by = list(Type, Treatment))[1:3]
#>    Plant   Type  Treatment     mean
#> 1:   Qn1 Quebec nonchilled 33.22857
#> 2:   Qn2 Quebec nonchilled 35.15714
#> 3:   Qn3 Quebec nonchilled 37.61429


# single column name as string
outer_fun(CO2, by = "Type")[1:3]
#>    Plant   Type     mean
#> 1:   Qn1 Quebec 33.22857
#> 2:   Qn2 Quebec 35.15714
#> 3:   Qn3 Quebec 37.61429


# multiple column names as string
outer_fun(CO2, by = c("Type", "Treatment"))[1:3]
#>    Plant   Type  Treatment     mean
#> 1:   Qn1 Quebec nonchilled 33.22857
#> 2:   Qn2 Quebec nonchilled 35.15714
#> 3:   Qn3 Quebec nonchilled 37.61429

outer_fun(CO2, by = list("Type", "Treatment"))[1:3]
#>    Plant   Type  Treatment     mean
#> 1:   Qn1 Quebec nonchilled 33.22857
#> 2:   Qn2 Quebec nonchilled 35.15714
#> 3:   Qn3 Quebec nonchilled 37.61429

【讨论】:

  • 抱歉,我不清楚我到底要搜索什么(在简化我的用例过程中,这条信息丢失了)。我寻找一种解决方案,我可以在其中投入任何东西,就像我会直接处理 data.table 一样。请查看我的编辑。
【解决方案3】:

这是一个使用Chapter 6: Computing on the language的选项:

outer_fun <- function(DT, outby) {
    inner_fun(DT, substitute(outby))
}

inner_fun <- function(DT, inby) {
    l <- as.list(inby)

    if (l[[1L]] == as.symbol("list") || l[[1L]] == as.symbol(".")) 
        l <- l[-1L]

    if (is.character(l[[length(l)]])) {
        if (length(l) == 1L)
            l <- strsplit(l[[1L]],",")[[1L]]
        l <- lapply(l, as.name)

        if (l[[1L]] == as.symbol("c"))
            l <- l[-1L]
    }

    BY <- as.call(c(as.symbol("list"), as.name("Plant"), l))
    eval(bquote(DT[, mean(uptake), .(BY)]))
}


outer_fun(CO2, outby = list(Type, Treatment))
outer_fun(CO2, outby = list(Type))
outer_fun(CO2, outby = .(Type, Treatment))
outer_fun(CO2, outby = list(Treatment == "chilled"))
outer_fun(CO2, outby = list(cut(conc, breaks = quantile(conc))))
outer_fun(CO2, outby = Type)

outer_fun(CO2, outby = "Type")
outer_fun(CO2, outby = c("Type", "Treatment"))
outer_fun(CO2, outby = "Type,Treatment")

【讨论】:

  • 这不适用于outer_fun(CO2, outby = .(Type, Treatment))。 ``` 替代(DT[, .(mean = mean(uptake)), .(Plant, BY)], list(BY = inby) ) ``` 在inner_fun() 内产生DT[, .(mean = mean(uptake)), .(Plant, .(Type, Treatment))],其中嵌套列表在byeval() 期间导致错误。我也去过那里。 ://
猜你喜欢
  • 2014-04-27
  • 1970-01-01
  • 1970-01-01
  • 2019-01-10
  • 1970-01-01
  • 2019-10-27
  • 2014-01-21
  • 2021-06-12
  • 1970-01-01
相关资源
最近更新 更多