And*_*rie 19 r data.table
我正在尝试编写一些包装器函数来减少代码重复data.table.
这是一个使用的例子mtcars.首先,设置一些数据:
library(data.table)
data(mtcars)
mtcars$car <- factor(gsub("(.*?) .*", "\\1", rownames(mtcars)), ordered=TRUE)
mtcars <- data.table(mtcars)
Run Code Online (Sandbox Code Playgroud)
现在,我通常会写这个以获得按组计算的总结.在这种情况下,我分组car:
mtcars[, list(Total=length(mpg)), by="car"][order(car)]
car Total
AMC 1
Cadillac 1
Camaro 1
...
Toyota 2
Valiant 1
Volvo 1
Run Code Online (Sandbox Code Playgroud)
复杂的是,因为参数i和j在框架中进行评估,如果你想传入变量data.table,就必须使用eval(...)它们:
这有效:
group <- "car"
mtcars[, list(Total=length(mpg)), by=eval(group)]
Run Code Online (Sandbox Code Playgroud)
但现在我想通过相同的分组变量来排序结果.我无法得到以下任何变体给我正确的结果.注意我总是得到一行结果,而不是有序集.
mtcars[, list(Total=length(mpg)), by=eval(group)][order(group)]
car Total
Mazda 2
Run Code Online (Sandbox Code Playgroud)
我知道为什么:它是因为group是在而parent.frame不是框架中进行评估data.table.
我怎样才能group在上下文中进行评估data.table?
更一般地说,我如何在函数内部使用它?我需要以下函数来给我所有结果,而不仅仅是第一行数据:
tableOrder <- function(x, group){
x[, list(Total=length(mpg)), by=eval(group)][order(group)]
}
tableOrder(mtcars, "car")
Run Code Online (Sandbox Code Playgroud)
Mat*_*wle 13
Gavin和Josh是对的.这个答案只是为了增加更多背景.这个想法是,你不仅可以通过可变列名到像一个函数,但表达式列名的,使用quote().
group = quote(car)
mtcars[, list(Total=length(mpg)), by=group][order(group)]
group Total
AMC 1
Cadillac 1
...
Toyota 2
Valiant 1
Volvo 1
Run Code Online (Sandbox Code Playgroud)
虽然,开始时更难以开始,但它可以更灵活.无论如何,那是个主意.你需要的内部功能substitute(),如下所示:
tableOrder = function(x,.expr) {
.expr = substitute(.expr)
ans = x[,list(Total=length(mpg)),by=.expr]
setkeyv(ans, head(names(ans),-1)) # see below re feature request #1780
ans
}
tableOrder(mtcars, car)
.expr Total
AMC 1
Cadillac 1
Camaro 1
...
Toyota 2
Valiant 1
Volvo 1
tableOrder(mtcars, substring(car,1,1)) # an expression, not just a column name
.expr Total
[1,] A 1
[2,] C 3
[3,] D 3
...
[8,] P 2
[9,] T 2
[10,] V 2
tableOrder(mtcars, list(cyl,gear%%2)) # by two expressions, so head(,-1) above
cyl gear Total
[1,] 4 0 8
[2,] 4 1 3
[3,] 6 0 4
[4,] 6 1 3
[5,] 8 1 14
Run Code Online (Sandbox Code Playgroud)
keyby在v1.8.0(2012年7月)中添加了一个新参数,使其更简单:
tableOrder = function(x,.expr) {
.expr = substitute(.expr)
x[,list(Total=length(mpg)),keyby=.expr]
}
Run Code Online (Sandbox Code Playgroud)
评论和在该地区的反馈i,j和by变量表达式是最欢迎的.您可以做的另一件事是有一个表,其中一列包含表达式,然后查找要放入的表达式i,j或者by从该表中查找.
Rei*_*son 11
用get(group)指命名的对象group:
> mtcars[, list(Total=length(mpg)), by=eval(group)][order(get(group))]
car Total
AMC 1
Cadillac 1
Camaro 1
Chrysler 1
Datsun 1
Dodge 1
Duster 1
Ferrari 1
Fiat 2
Ford 1
Honda 1
Hornet 2
Lincoln 1
Lotus 1
Maserati 1
Mazda 2
Merc 7
Pontiac 1
Porsche 1
Toyota 2
Valiant 1
Volvo 1
cn car Total
> # vs
> mtcars[, list(Total=length(mpg)), by=eval(group)][order(group)]
car Total
[1,] Mazda 2
Run Code Online (Sandbox Code Playgroud)
原因order(get(group))在于表达式是在框架中进行评估的data.table.在那里,get(group)将寻找一个find变量car.如果你在全球环境中评估它,那就不存在了
> get(group)
Error in get(group) : object 'car' not found
Run Code Online (Sandbox Code Playgroud)
但它确实在评估发生的框架中.group在那里不存在,但按照通常的规则,它会搜索父帧,直到找到匹配的东西,group在这种情况下是全局环境.因此,您需要注意您group在实际函数中使用的对象的名称- 例如,您不希望使用可能在data.table对象中匹配的对象.使用像.group函数arg这样的东西我觉得很安全.
这是你的功能,修改过:
tableOrder <- function(x, .group){
x[, list(Total=length(mpg)), by=eval(.group)][order(get(.group))]
}
> tableOrder(mtcars, "car")
car Total
AMC 1
Cadillac 1
Camaro 1
Chrysler 1
Datsun 1
....
Run Code Online (Sandbox Code Playgroud)
Jos*_*ien 10
对于如何控制划定范围内的一般问题data.table,加文的答案已经得到了你很好覆盖.
但是,要真正充分利用data.table包的优势,您应该为data.table对象设置密钥.密钥会导致数据被预先排序,以便来自分组因子的相同级别(或级别组合)的行存储在连续的内存块中.与"示例中使用的ad hoc类型"相比,这可以反过来大大加快分组操作.(有关详细信息,请在datatable-faq(警告,pdf)中搜索'ad hoc' ).
在许多情况下(包括您的示例)使用键也具有简化操作data.table所需的代码的愉快副作用.此外,它会按照键指定的顺序自动输出结果,这通常也是您想要的.
首先,如果您只需要按'car'列进行子集,则可以执行以下操作:
## Create data.table with a key
group <- "car"
mtcars <- data.table(mtcars, key = group)
## Outputs results in correct order
mtcars[, list(Total=length(mpg)), by = key(mtcars)]
car Total
AMC 1
Cadillac 1
Camaro 1
Chrysler 1
Datsun 1
Run Code Online (Sandbox Code Playgroud)
即使您的密钥包含多个列,使用密钥仍然可以实现更简单的代码(并且您可以获得加速,这可能是您首先使用data.table的真正原因!):
group <- "car"
mtcars <- data.table(mtcars, key = c("car", "gear"))
mtcars[, list(Total=length(mpg)), by = eval(group)]
Run Code Online (Sandbox Code Playgroud)
编辑:谨慎的谨慎
如果by参数用于基于作为键的一部分但不是键的第一个元素的列执行分组,则结果的顺序可能仍然需要后处理.所以,在上面的第二个例子中,if key = c("gear", "car"),然后"Dodge"排序"Datsun".在这样的情况下,我可能仍然希望事先重新排序密钥,而不是在事后重新排序结果.也许Matthew Dowle将权衡这两者中的哪一个更优先/更快.
| 归档时间: |
|
| 查看次数: |
6442 次 |
| 最近记录: |