动态地按多列过滤data.table

sds*_*sds 5 r data.table

假设我有data.table一些列:

a <- data.table(id=1:1000, x=runif(100), y=runif(100), z=runif(100))
Run Code Online (Sandbox Code Playgroud)

我要删除的行,其中x,y或者z是低于中位数:

a <- a[ x > median(x) & y > median(y) & z > median(z) ]
Run Code Online (Sandbox Code Playgroud)

(旁边:上面的电话是median3次还是3000次?)

我做的是

my.cols <- c("x","y","z")
my.meds <- sapply(my.cols, function(n) median(a[[n]]))
a <- a[ Reduce(`&`,Map(function(i) a[[my.cols[i]]] > my.meds[i], 1:length(my.cols))) ]
Run Code Online (Sandbox Code Playgroud)

这是我能做的最好的吗?

Mat*_*wle 5

当您发现自己遍历列时,通常长格式可以更容易.所以只是为了在这种情况下显示该选项,尽管它并不好.

以下内容并不快,因为它使用[on .SD.但无论如何,这是逻辑,因为当我们进行优化时.SD[...].

> a
       id           x         y        z
   1:  60 0.006884017 0.9159115 0.876148
   2: 160 0.006884017 0.9159115 0.876148
   3: 260 0.006884017 0.9159115 0.876148
   4: 360 0.006884017 0.9159115 0.876148
   5: 460 0.006884017 0.9159115 0.876148
  ---                                   
 996: 504 0.990417986 0.7167666 0.751991
 997: 604 0.990417986 0.7167666 0.751991
 998: 704 0.990417986 0.7167666 0.751991
 999: 804 0.990417986 0.7167666 0.751991
1000: 904 0.990417986 0.7167666 0.751991

> require(reshape2)    # but data.table v1.8.11 has a fast melt built-in
> DT = as.data.table(melt(a, "id"))    # copies here => bad
> DT
       id variable       value
   1:  60        x 0.006884017
   2: 160        x 0.006884017
   3: 260        x 0.006884017
   4: 360        x 0.006884017
   5: 460        x 0.006884017
  ---                         
2996: 504        z 0.751991033
2997: 604        z 0.751991033
2998: 704        z 0.751991033
2999: 804        z 0.751991033
3000: 904        z 0.751991033
Run Code Online (Sandbox Code Playgroud)

现在数据是长格式的(首先它可以是长格式吗?),下面的步骤更容易:

> DT[, below:=value<median(value), by=variable]
> DT
       id variable       value below
   1:  60        x 0.006884017  TRUE
   2: 160        x 0.006884017  TRUE
   3: 260        x 0.006884017  TRUE
   4: 360        x 0.006884017  TRUE
   5: 460        x 0.006884017  TRUE
  ---                               
2996: 504        z 0.751991033 FALSE
2997: 604        z 0.751991033 FALSE
2998: 704        z 0.751991033 FALSE
2999: 804        z 0.751991033 FALSE
3000: 904        z 0.751991033 FALSE

> DT[below==TRUE, .SD[.N==3], by=id]
      id variable      value below
  1:  88        x 0.01873885  TRUE
  2:  88        y 0.05834677  TRUE
  3:  88        z 0.08973225  TRUE
  4: 188        x 0.01873885  TRUE
  5: 188        y 0.05834677  TRUE
 ---                              
356: 848        y 0.39433186  TRUE
357: 848        z 0.14152092  TRUE
358: 948        x 0.48932049  TRUE
359: 948        y 0.39433186  TRUE
360: 948        z 0.14152092  TRUE
Run Code Online (Sandbox Code Playgroud)

dcast如果它需要宽,那就回来了.但我试着把事情做好,就像数据库一样.

可能有更直接的方法来做到这一点,也许是一种避免.SD[...]速度的方法.

旁白:我想到setkey(a,x)了上半场.这适用于一列.但是第二个需要y<median(y)median(y)所有的位置a,所以你不能只是在上半部分由y设置,然后再设置一半,并且因为这个原因再次为z设置一半.但是,如果这样的事情是可能的,那将是非常专业的,median这只是我假设的问题中的一个说明性例子.


edd*_*ddi 4

一种选择是构造您想要的字符串eval/parse

EVAL = function(...)eval(parse(text=paste0(...)))   # standard helper function

a[ EVAL(my.cols, ">median(", my.cols, ")", collapse=" & ") ]
Run Code Online (Sandbox Code Playgroud)