簡體   English   中英

R自定義data.table函數,具有多個變量輸入

[英]R custom data.table function with multiple variable inputs

我正在使用data.table(v 1.9.6)編寫自定義聚合函數,並且很難將函數參數傳遞給它。 對此有類似的問題,但沒有一個涉及多個(可變)輸入,似乎沒有一個確定的答案,而是“小黑客”。

  1. 將變量和名稱傳遞給data.table函數
  2. eval和data.table中的引用
  3. 如何在R中的data.table中完全使用變量中的列名

我想獲取數據表總和並命令定義變量並在頂部創建新變量(2個步驟)。 關鍵的想法是一切都應該參數化,即變量總和,變量分組,變量排序。 它們都可以是一個或多個變量。 一個小例子。

dt <- data.table(a=rep(letters[1:4], 5), 
                 b=rep(letters[5:8], 5),
                 c=rep(letters[3:6], 5),
                 x=sample(1:100, 20),
                 y=sample(1:100, 20),
                 z=sample(1:100, 20))

temp <- 
  dt[, .(x_sum = sum(x, na.rm = T),
         y_sum = sum(y, na.rm = T)),
     by = .(a, b)][order(a, b)]

temp2 <- 
  temp[, `:=` (x_sum_del = (x_sum - shift(x = x_sum, n = 1, type = "lag")),
               y_sum_del = (y_sum - shift(x = y_sum, n = 1, type = "lag")),
               x_sum_del_rel = ((x_sum - shift(x = x_sum, n = 1, type = "lag")) /
                                  (shift(x = x_sum, n = 1, type = "lag"))),
               y_sum_del_rel = ((y_sum - shift(x = y_sum, n = 1, type = "lag")) /
                                  (shift(x = y_sum, n = 1, type = "lag")))
               )
       ]

如何以編程方式傳遞以下函數參數(即不是單個輸入,而是向量/輸入列表):

  • x和y - > var_list
  • x和y的新名稱(例如x_sum,y_sum) - > var_name_list
  • 按參數分組a,b - > by_var_list
  • 按參數a,b - > order_var_list排序
  • temp 2應該適用於所有預定義的參數,我也在考慮使用apply函數,但又一次努力傳遞一個變量列表。

我玩過get(),as.name(),eval(),quote()的變體,但是當我傳遞多個變量時,它們就不再起作用了。 我希望問題很清楚,否則我很樂意在你認為必要的地方進行調整。 函數調用如下所示:

fn_agg(dt, var_list, var_name_list, by_var_list, order_var_list)

看起來像是一個問題:)
我更喜歡用get / mget計算語言。

fn_agg = function(dt, var_list, var_name_list, by_var_list, order_var_list) {
    j_call = as.call(c(
        as.name("."),
        sapply(setNames(var_list, var_name_list), function(var) as.call(list(as.name("sum"), as.name(var), na.rm=TRUE)), simplify=FALSE)
    ))
    order_call = as.call(c(
        as.name("order"),
        lapply(order_var_list, as.name)
    ))
    j2_call = as.call(c(
        as.name(":="),
        c(
            sapply(setNames(var_name_list, paste0(var_name_list,"_del")), function(var) {
                substitute(.var - shift(x = .var, n = 1, type = "lag"), list(.var=as.name(var)))
            }, simplify=FALSE),
            sapply(setNames(var_name_list, paste0(var_name_list,"_del_rel")), function(var) {
                substitute((.var - shift(x = .var, n = 1, type = "lag")) / (shift(x = .var, n = 1, type = "lag")), list(.var=as.name(var)))
            }, simplify=FALSE)
        )
    ))
    dt[eval(order_call), eval(j_call), by=by_var_list
       ][, eval(j2_call)
         ][]
}

ans = fn_agg(dt, var_list=c("x","y"), var_name_list=c("x_sum","y_sum"), by_var_list=c("a","b"), order_var_list=c("a","b"))
all.equal(temp2, ans)
#[1] TRUE

一些額外的說明:

  1. 進行嚴格的輸入驗證,因為調試問題對元編程更加困難。
  2. 步驟2的優化是可能的,因為移位被多次計算,簡單的方法是在步驟2中計算_del在步驟_del_rel中計算_del
  3. 如果order變量始終是一樣by變量,你可以把它們放到keyby說法。

這是一個使用mget的選項,如注釋:

fn_agg <- function(DT, var_list, var_name_list, by_var_list, order_var_list) {

  temp <- DT[, setNames(lapply(.SD, sum, na.rm = TRUE), var_name_list), 
             by = by_var_list, .SDcols = var_list]

  setorderv(temp, order_var_list)

  cols1 <- paste0(var_name_list, "_del")
  cols2 <- paste0(cols1, "_rel")

  temp[, (cols1) := lapply(mget(var_name_list), function(x) {
    x - shift(x, n = 1, type = "lag")
  })]

  temp[, (cols2) := lapply(mget(var_name_list), function(x) {
    xshift <- shift(x, n = 1, type = "lag")
    (x - xshift) / xshift
  })]

  temp[]
}

fn_agg(dt, 
       var_list = c("x", "y"), 
       var_name_list = c("x_sum", "y_sum"), 
       by_var_list = c("a", "b"), 
       order_var_list = c("a", "b"))

#   a b x_sum y_sum x_sum_del y_sum_del x_sum_del_rel y_sum_del_rel
#1: a e   254   358        NA        NA            NA            NA
#2: b f   246   116        -8      -242  -0.031496063    -0.6759777
#3: c g   272   242        26       126   0.105691057     1.0862069
#4: d h   273   194         1       -48   0.003676471    -0.1983471

您也可以使用data.table.SDcols參數而不是mget

temp[, (cols1) := lapply(.SD, function(x) {
    x - shift(x, n = 1, type = "lag")
  }), .SDcols = var_name_list]

此外,有可能通過避免重復計算shift(x, n = 1, type = "lag")來改進函數,但我只想演示在函數中使用data.table的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM