r - 用于按索引对向量进行分区并在该分区上执行操作的惯用 R 代码

标签 r functional-programming plyr

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

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

我希望输出创建一个向量(我们称之为 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 等)来做到这一点。

最佳答案

还有一个选择是ave。为了更好地衡量,我收集了上面的答案,尽力使它们的输出等效(向量),并使用示例数据作为输入提供了超过 1000 次运行的计时。首先,我使用 ave 进行回答:ave(df$x, df$index, FUN = function(z) z/sum(z))。我还展示了一个使用 data.table 包的示例,因为它通常非常快,但我知道您正在寻找基本解决方案,因此如果您愿意,可以忽略它。

现在有很多时间安排:

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)}))
b.y <- 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)})
d.t <- function() dt[, x/sum(x), by = index]

library(rbenchmark)
benchmark(plyr(), av(), t.apply(), l.apply(), b.y(), agg(), d.t(), 
           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     b.y()   0.694 13.346154
6     agg()   1.020 19.615385
7     d.t()   2.380 45.769231
1    plyr()   5.119 98.442308

在这种情况下,lapply() 解决方案似乎获胜,而 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     d.t()   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     b.y()  16.329  7.957602
5     agg()  20.541 10.010234

这似乎更符合我的预期。

总之,您有很多不错的选择。找到一两种适合您聚合任务应如何工作的心智模型的方法并掌握该功能。给猫剥皮的方法有很多。

编辑 - 以及 1e7 行的示例

对于 Matt 来说可能不够大,但我的笔记本电脑可以在不崩溃的情况下处理它:

df <- data.frame(x = sample(1:100, 1e7, TRUE), index = gl(10000, 1000))
dt <- data.table(df)
#-----
       test elapsed  relative
6     d.t()    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     b.y()    8.92 14.622951
5     agg()   18.20 29.83606

关于r - 用于按索引对向量进行分区并在该分区上执行操作的惯用 R 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10748253/

相关文章:

java - 如何将函数式编程语言集成到 Java 或 C# 中?

web-services - 用于检查用哪种编程语言编写代码的网站?

与一个级别相比, reshape Actor 阵容

r - dplyr group_by 的带括号或其他标点符号的列名

R Plotly - 按标记的大小分配其颜色

r - 使用 ggplot 绘制方差和置信区间

clojure - 如何在 Clojure 中映射很少使用的状态?

json - 将不完整列表解析为具有两个不同问题的数据帧

r - 如何使用 plyr mdply 并行故障安全执行

r - 是否可以将百分比添加到列联表中