我发现了一个奇怪的 data.table 行为。我想知道是否有办法避免它或解决方法。

在我的数据管理中,我经常使用 lapply.SD 来为列分配新值。要正确分配几列,必须保持 lapply 的输出列的顺序。
我发现情况并非如此。

这里的正常行为

library(data.table)
plouf <- data.table(x = 1, y = 2, z = 3)
cols <- c("y","x")
plouf[,.SD,.SDcols = cols ,by = z]
plouf[,lapply(.SD,function(x){x}),.SDcols = cols ,by = z]
plouf[,lapply(.SD[x == 1],function(x){x}),.SDcols = cols ,by = z]

所有这些行给出:
   z y x
1: 3 2 1

例如,我需要将其重新分配给 c("y","x")。但如果我这样做:
plouf[,lapply(.SD[get("x") == 1],function(x){x}),.SDcols = c("y","x"),by = z]

   z x y
1: 3 1 2

这里 x 和 y 的顺序无缘无故改变了,它应该产生与上一个“工作”示例相同的结果。如果然后将错误的值分配给 c("y","x") 如果我将 lapply 的输出分配给新的列向量。好像是在geti部分使用.SD触发了这个bug。

这对分配的影响示例:
plouf[, c(cols ) := lapply(.SD[get("x") == 1],function(x){x}),
      .SDcols = cols ,by = z][]
#    x y z
# 1: 2 1 3

有没有人有解决方法?我使用的代码看起来更像是:
 plouf[, c(cols ) := lapply(.SD[get("x") >= 1 & get("x") <= 3],function(x){mean}),
          .SDcols = cols ,by = z]

github 上的问题:https://github.com/Rdatatable/data.table/issues/4089

最佳答案

您可以在 lapply 函数中进行子集化,而不是子集化 .SD 。如果用于子集的逻辑向量作为第三个参数传递给 lapply,则不会在每次 lapply 传递时重新评估。

注意:我将函数更改为乘以 10,否则我根本无法判断代码是否在执行任何操作

plouf[, (cols) := lapply(.SD, function(x, i) 10*mean(x[i]),
                         get("x") %between% c(1, 3)),
      .SDcols = cols ,by = z][]

#     x  y z
# 1: 10 20 3

还有其他解决方法可以让您对 .SD 进行子集化,但我认为按组对 .SD 进行子集化比单独对每一列进行子集化要慢。
set.seed(0)
df <- rep(1:50000, sample(500:1000, 50000, T)) %>%
        data.table(a = runif(length(.))
                  ,b = .)

library(microbenchmark)
microbenchmark(
  subSD = df[, lapply(.SD[a < .2], sum), b]
  , in_func = df[, lapply(.SD, function(x, i) sum(x[i]), a < .2), b]
  , times = 10L)

# Unit: milliseconds
#     expr      min         lq      mean     median        uq       max neval cld
#    subSD 19323.19 20398.3666 21289.345 20708.4346 22466.010 23738.467    10   b
#  in_func   972.64   987.7891  1016.252   995.4236  1038.069  1125.709    10  a

编辑:更大的基准
set.seed(0)
rm(df)
df <- rep(1:5e5, sample(50:100, 5e5, T)) %>%
        data.table(a = runif(length(.))
                  ,b = .)

library(microbenchmark)
microbenchmark(
  subSD = df[, lapply(.SD[a < .2], sum), b]
  , in_func = df[, lapply(.SD, function(x, i) sum(x[i]), a < .2), b]
  , times = 2L)

# Unit: seconds
#     expr        min         lq       mean     median        uq       max neval cld
#    subSD 207.111290 207.111290 214.147649 214.147649 221.18401 221.18401     2   b
#  in_func   3.560467   3.560467   3.651359   3.651359   3.74225   3.74225     2  a

关于r - data.table 错误 : lapply on . SD 在使用 get() 时对列重新排序。可能的解决方法?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59176071/

10-13 00:32