R:可行的快速(条件)子集

bum*_*bee 7 r subset data.table

我想对数据行进行子集化

library(data.table); set.seed(333); n <- 100
dat <- data.table(id=1:n, x=runif(n,100,120), y=runif(n,200,220), z=runif(n,300,320))

> head(dat)
   id        x        y        z
1:  1 109.3400 208.6732 308.7595
2:  2 101.6920 201.0989 310.1080
3:  3 119.4697 217.8550 313.9384
4:  4 111.4261 205.2945 317.3651
5:  5 100.4024 212.2826 305.1375
6:  6 114.4711 203.6988 319.4913
Run Code Online (Sandbox Code Playgroud)

分几个阶段。我知道我可以subset(.)顺序申请以实现这一目标。

> s <- subset(dat, x>119)
> s <- subset(s, y>219)
> subset(s, z>315)
   id        x        y        z
1: 55 119.2634 219.0044 315.6556
Run Code Online (Sandbox Code Playgroud)

我的问题是我需要使它自动化,并且可能会发生子集为空的情况。在这种情况下,我想跳过导致空集的步骤。例如,如果我的数据是

dat2 <- dat[1:50]
> s <-subset(dat2,x>119)
> s
   id        x        y        z
1:  3 119.4697 217.8550 313.9384
2: 50 119.2519 214.2517 318.8567
Run Code Online (Sandbox Code Playgroud)

第二步subset(s, y>219)将是空的,但我仍然想应用第三步subset(s,z>315)。是否只有在产生非空集的情况下才应用子命令的方法?我想像这样subset(s, y>219, nonzero=TRUE)。我想避免像

s <- dat
if(nrow(subset(s, x>119))>0){s <- subset(s, x>119)}
if(nrow(subset(s, y>219))>0){s <- subset(s, y>219)}
if(nrow(subset(s, z>318))>0){s <- subset(s, z>319)}
Run Code Online (Sandbox Code Playgroud)

因为我担心if-then丛林会相当慢,尤其是因为我需要将所有这些都应用于列表中的不同data.tables lapply(.)。这就是为什么我希望找到针对速度进行优化的解决方案。

PS。我只是subset(.)为了清楚起见,选择了诸如data.table之类的解决方案(如果不是更多的话)同样受欢迎。

Fra*_*ank 4

我同意康拉德的回答,这应该发出警告或至少报告以某种方式发生的情况。这是一种利用索引的 data.table 方式(有关详细信息,请参阅包小插图):

f = function(x, ..., verbose=FALSE){
  L   = substitute(list(...))[-1]
  mon = data.table(cond = as.character(L))[, skip := FALSE]

  for (i in seq_along(L)){
    d = eval( substitute(x[cond, verbose=v], list(cond = L[[i]], v = verbose)) )
    if (nrow(d)){
      x = d
    } else {
      mon[i, skip := TRUE]
    }    
  }
  print(mon)
  return(x)
}
Run Code Online (Sandbox Code Playgroud)

用法

> f(dat, x > 119, y > 219, y > 1e6)
        cond  skip
1:   x > 119 FALSE
2:   y > 219 FALSE
3: y > 1e+06  TRUE
   id        x        y        z
1: 55 119.2634 219.0044 315.6556
Run Code Online (Sandbox Code Playgroud)

verbose 选项将打印 data.table 包提供的额外信息,以便您可以看到索引何时被使用。例如,f(dat, x == 119, verbose=TRUE)我看到了。

因为我担心 if-then 丛林会相当慢,特别是因为我需要使用 lapply(.) 将所有这些应用到列表中的不同 data.tables。

如果用于非交互式使用,也许更好的是让函数返回list(mon = mon, x = x)以更轻松地跟踪查询是什么以及发生了什么。此外,还可以捕获并返回详细的控制台输出。

  • 如果速度很重要的话,这绝对是一个可行的方法。可以使用`options("datatable.verbose"=TRUE)`轻松检查索引的使用。 (2认同)