假设我有data.table
列,其中包含几列:
a <- data.table(id=1:1000, x=runif(100), y=runif(100), z=runif(100))
我想删除x
,y
或z
低于中位数的行:
a <- a[ x > median(x) & y > median(y) & z > median(z) ]
(除此之外:上面的呼叫是median
3次还是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))) ]
这是我能做的最好的吗?
答案 0 :(得分:5)
当您发现自己在列中进行迭代时,通常可以更轻松地使用长格式。所以只是为了在这种情况下显示该选项,虽然它不是很好。
以下内容并不快,因为它在[
上使用.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
现在数据是长格式的(首先是长格式吗?),以下步骤更容易:
> 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
然后dcast
如果它需要宽,则返回。但我试着把事情做好,就像数据库一样。
可能有更直接的方法来实现上述目标,也许是一种避免速度.SD[...]
的方法。
除此之外:我考虑过setkey(a,x)
然后只是上半场。这适用于一列。但是第二个需要y<median(y)
,其中median(y)
在a
的所有位置,所以你不能只在上半部分将y设置为y然后再设置一半,再为z设置一半,因为那个。但是,如果这样的事情是可能的,那对median
来说就是非常专业的,这只是我假设的问题中的一个说明性例子。
答案 1 :(得分:4)
一个选项是构造所需的字符串并eval/parse
:
EVAL = function(...)eval(parse(text=paste0(...))) # standard helper function
a[ EVAL(my.cols, ">median(", my.cols, ")", collapse=" & ") ]
答案 2 :(得分:1)
我首选的方法是准备语句,因为我将直接运行它,所以:
library(data.table)
a = data.table(id=1:1000, x=runif(100), y=runif(100), z=runif(100))
upper = c("x","y","z")
l = lapply(upper, function(col) call(">", as.name(col), call("median", as.name(col))))
ii = Reduce(function(c1, c2) substitute(.c1 & .c2, list(.c1=c1, .c2=c2)), l)
ii
#x > median(x) & y > median(y) & z > median(z)
a[eval(ii)]
# id x y z
# 1: 7 0.4750376 0.8936338 0.6158251
# ...
你基本上在准备好的陈述中使用eval
参数中的i
。