用于通过索引分割vector并在该分区上执行操作的语法R代码

我试图在R中find惯用的方法来通过一些索引向量对数值向量进行分割,find该分区中所有数字的总和,然后用该分割总和除除每个单独的条目。 换句话说,如果我从这开始:

df <- data.frame(x = c(1,2,3,4,5,6), index = c('a', 'a', 'b', 'b', 'c', 'c')) 

我想输出创build一个向量(让我们称之为z):

 c(1/(1+2), 2/(1+2), 3/(3+4), 3/(3+4), 5/(5+6), 6/(5+6)) 

如果我这样做是SQL和可以使用窗口函数,我会这样做:

 select x / sum(x) over (partition by index) as z from df 

如果我使用plyr,我会做这样的事情:

 ddply(df, .(index), transform, z = x / sum(x)) 

但我想知道如何使用标准的R函数编程工具,如mapply / aggregate等

另一个select是ave 。 为了更好的衡量,我已经收集了上面的答案,尽我所能使他们的输出等效(一个向量),并且使用您的示例数据作为input提供超过1000次运行的计时。 首先,我的答案使用aveave(df$x, df$index, FUN = function(z) z/sum(z)) 。 我也展示了一个使用data.table包的例子,因为它通常很快,但是我知道你正在寻找基本的解决scheme,所以你可以忽略它。

现在有一些时机:

 library(data.table) library(plyr) dt <- data.table(df) plyr <- function() ddply(df, .(index), transform, z = x / sum(x)) av <- function() ave(df$x, df$index, FUN = function(z) z/sum(z)) t.apply <- function() unlist(tapply(df$x, df$index, function(x) x/sum(x))) l.apply <- function() unlist(lapply(split(df$x, df$index), function(x){x/sum(x)})) by <- function() unlist(by(df$x, df$index, function(x){x/sum(x)})) agg <- function() aggregate(df$x, list(df$index), function(x){x/sum(x)}) dt <- function() dt[, x/sum(x), by = index] library(rbenchmark) benchmark(plyr(), av(), t.apply(), l.apply(), by(), agg(), dt(), replications = 1000, columns = c("test", "elapsed", "relative"), order = "elapsed") #----- test elapsed relative 4 l.apply() 0.052 1.000000 2 av() 0.168 3.230769 3 t.apply() 0.257 4.942308 5 by() 0.694 13.346154 6 agg() 1.020 19.615385 7 dt() 2.380 45.769231 1 plyr() 5.119 98.442308 

在这种情况下, lapply()解决scheme似乎赢了, data.table()的速度惊人地慢。 让我们来看看这是如何扩展到一个更大的聚合问题:

 df <- data.frame(x = sample(1:100, 1e5, TRUE), index = gl(1000, 100)) dt <- data.table(df) #Replication code omitted for brevity, used 100 replications and dropped plyr() since I know it #will be slow by comparison: test elapsed relative 6 dt() 2.052 1.000000 1 av() 2.401 1.170078 3 l.apply() 4.660 2.270955 2 t.apply() 9.500 4.629630 4 by() 16.329 7.957602 5 agg() 20.541 10.010234 

这似乎更符合我的预期。

总之,你有很多好的select。 find一个或两个方法,与您的思维模型一起工作,如何聚合任务应该工作和掌握该function。 许多方法去皮肤猫。

编辑 – 和1e7行的例子

可能对于Matt来说不够大,但是和我的笔记本电脑一样大,不会崩溃:

 df <- data.frame(x = sample(1:100, 1e7, TRUE), index = gl(10000, 1000)) dt <- data.table(df) #----- test elapsed relative 6 dt() 0.61 1.000000 1 av() 1.45 2.377049 3 l.apply() 4.61 7.557377 2 t.apply() 8.80 14.426230 4 by() 8.92 14.622951 5 agg() 18.20 29.83606 

如果你只使用一个vector,只需要一个索引vector,那么tapply就相当快

 dat <- 1:6 lev <- rep(1:3, each = 2) tapply(dat, lev, function(x){x/sum(x)}) #$`1` #[1] 0.3333333 0.6666667 # #$`2` #[1] 0.4285714 0.5714286 # #$`3` #[1] 0.4545455 0.5454545 # unlist(tapply(dat, lev, function(x){x/sum(x)})) # 11 12 21 22 31 32 #0.3333333 0.6666667 0.4285714 0.5714286 0.4545455 0.5454545 

另外三种方法:

 dat <- 1:6 lev <- rep(1:3, each = 2) lapply(split(dat, lev), function(x){x/sum(x)}) by(dat, lev, function(x){x/sum(x)}) aggregate(dat, list(lev), function(x){x/sum(x)})