通过data.table中的引用更好地替换?

时间:2017-03-10 22:48:43

标签: r function pointers data.table pass-by-reference

How to use data.table within functions and loops?相关,是否有更好的方法来执行下面显示的功能,特别是使用data.table
注意:以下所有代码都有效,但是......很慢 (我使用简单的“清理”步骤来证明问题)。

目标是在data.table中编写一个1)有效 2)替换 3)某些值的函数,然后它可以在循环中用于清理大量的数据集 在C ++中,这将使用指针完成,并按引用调用,如下所示:

   void cleanDT(* dataTable dt); cleanDT(&dt222)

然而,在R中,我们每次调用函数时都会来回复制整个数据集(data.tables)。

cleanDT <- function (dt) {
  strNames <- names(dt);   nCols <- 1:length(strNames)
  for (i in nCols) {
    strCol <- strNames[i]
    if ( class(dt[[strCol]]) == "numeric"  ) 
      dt[[strCol]] <- floor(dt[[strCol]])
    else 
      dt[[strCol]] <- gsub("I", "i", dt[[strCol]])
  }
  return(dt)
}
cleanDTByReference <- function (dt) {
  dtCleaned <- dt
  strNames <- names(dt);   nCols <- 1:length(strNames)
  for (i in nCols) {
    strCol = strNames[i]
    if ( class(dt[[strCol]]) == "numeric"  ) 
      dtCleaned[[strCol]] <- floor(dt[[strCol]])
    else 
      dtCleaned[[strCol]] <-  gsub("I", "i", dt[[strCol]]) 
  }
  eval.parent(substitute(dt <- dtCleaned))
}

dt222 <- data.table(ggplot2::diamonds); dt222[1:2]
dt222 <- cleanDT(dt222); dt222[1:2]

dt222 <- data.table(diamonds); dt222[1:2]
#   carat     cut color clarity depth table price    x    y    z
#1:  0.23   Ideal     E     SI2  61.5    55   326 3.95 3.98 2.43
#2:  0.21 Premium     E     SI1  59.8    61   326 3.89 3.84 2.31

cleanDTByReference(dt222); dt222[1:2]
#   carat     cut color clarity depth table price x y z
#1:     0   ideal     E     Si2    61    55   326 3 3 2
#2:     0 Premium     E     Si1    59    61   326 3 3 2

然后我们将使用此函数来清理循环中的数据表列表,如下所示:

dt333 <- data.table(datasets::mtcars)
listDt <- list(dt222, dt333)

for(dt in listDt) {
  print(dt[1:2])
  cleanDTByReference(dt); print(dt[1:2])
}

理想情况下,我希望使用一个函数让我的所有数据表“清理”这种方式。但是目前没有使用引用,上面的代码实际上并没有改变listDt,也没有改变dt222dt333。 你能告诉我如何实现这个目标吗?

2 个答案:

答案 0 :(得分:3)

如果您遵循data.table语法,则可以使用函数修改data.table 引用。 (我强烈建议研究data.table vignettes and FAQ。)

功能定义:

change_DT_in_place <- function(DT){
  cat(address(DT), "\n")
  numcols <- DT[, which(sapply(.SD, is.numeric))]
  cat("num: ", numcols, "- ")
  if (length(numcols) > 0) {
    DT[, (numcols) := lapply(.SD, floor), .SDcols = numcols]
  }
  othcols <-  DT[, which(!sapply(.SD, is.numeric))]
  cat("other: ", othcols, "\n")
  if (length(othcols) > 0) {
    DT[, (othcols) := lapply(.SD, gsub, pattern = "I", replacement = "i"), 
       .SDcols = othcols]
  }
}

请注意,我已添加了一些cat()语句来演示内部工作原理。 address()返回变量RAM中的地址。

要验证函数是否有效,必须证明data.tables

  1. 已更改
  2. 但是没有被复制。
  3. 创建data.tables:

    library(data.table)
    dt1 <- as.data.table(ggplot2::diamonds)
    dt2 <- as.data.table(mtcars)
    

    修改第一个data.table:

    head(dt1)
    #   carat       cut color clarity depth table price    x    y    z
    #1:  0.23     Ideal     E     SI2  61.5    55   326 3.95 3.98 2.43
    #2:  0.21   Premium     E     SI1  59.8    61   326 3.89 3.84 2.31
    #3:  0.23      Good     E     VS1  56.9    65   327 4.05 4.07 2.31
    #4:  0.29   Premium     I     VS2  62.4    58   334 4.20 4.23 2.63
    #5:  0.31      Good     J     SI2  63.3    58   335 4.34 4.35 2.75
    #6:  0.24 Very Good     J    VVS2  62.8    57   336 3.94 3.96 2.48
    address(dt1)
    #[1] "0000000015660EE0"
    change_DT_in_place(dt1)
    #0000000015660EE0 
    #num:  1 5 6 7 8 9 10 - other:  2 3 4 
    address(dt1)
    #[1] "0000000015660EE0"
    head(dt1)
    #   carat       cut color clarity depth table price x y z
    #1:     0     ideal     E     Si2    61    55   326 3 3 2
    #2:     0   Premium     E     Si1    59    61   326 3 3 2
    #3:     0      Good     E     VS1    56    65   327 4 4 2
    #4:     0   Premium     i     VS2    62    58   334 4 4 2
    #5:     0      Good     J     Si2    63    58   335 4 4 2
    #6:     0 Very Good     J    VVS2    62    57   336 3 3 2
    

    修改第二个data.table:

    head(dt2)
    #    mpg cyl disp  hp drat    wt  qsec vs am gear carb
    #1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
    #2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
    #3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
    #4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
    #5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
    #6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1
    address(dt2)
    #[1] "0000000018C42E78"
    change_DT_in_place(dt2)
    #0000000018C42E78 
    #num:  1 2 3 4 5 6 7 8 9 10 11 - other:   
    address(dt2)
    #[1] "0000000018C42E78"
    head(dt2)
    #   mpg cyl disp  hp drat wt qsec vs am gear carb
    #1:  21   6  160 110    3  2   16  0  1    4    4
    #2:  21   6  160 110    3  2   17  0  1    4    4
    #3:  22   4  108  93    3  2   18  1  1    4    1
    #4:  21   6  258 110    3  3   19  1  0    3    1
    #5:  18   8  360 175    3  3   17  0  0    3    2
    #6:  18   6  225 105    2  3   20  1  0    3    1
    

    结论

    在这两种情况下,函数都已经更改了data.tables,从未更改的指针地址可以看出。

答案 1 :(得分:-2)

以下是使用data.table的更好方法:

dt <- as.data.table(ggplot2::diamonds)
dt1 <- as.data.table(mtcars)

changeDT <- function(dt){
  cols <- names(dt)
  dt[, c(cols) := lapply(.SD, function(x) ifelse(sapply(x, is.numeric), 
                                                 floor(x), 
                                                 gsub("I", "i", x))),
     .SDcols = cols]
    }

    list1 <- list(dt, dt1)

    x <- lapply(list1, changeDT)