我想按“行数”分解我的数据。也就是说,我想向我的函数发送固定数量的行,当我到达数据帧的末尾(最后一个块)时,我只需要发送块,无论它是否具有固定数量的行数或更少。像这样的东西:
ddply(df, .(8 rows), .fun=somefunction)
如果要使用plyr
,可以添加类别列:
df <- data.frame(x=rnorm(100), y=rnorm(100))
somefunction <- function(df) {
data.frame(mean(df$x), mean(df$y))
}
df$category <- rep(letters[1:10], each=10)
ddply(df, .(category), somefunction)
但是,在这种情况下,apply 系列可能是更好的选择:
somefunction <- function(n, x, y) {
data.frame(mean(x[n:(n+9)]), mean(y[n:n+9]))
}
lapply(seq(1, nrow(df), by=10), somefunction, x=df$x, y=df$y)
如果对速度和简洁感兴趣,那么为了完整性(并使用 4 而不是 8 的块大小来保持示例简短):
require(data.table)
set.seed(0)
DT = data.table(a=rnorm(10))
DT
a
[1,] 1.262954285
[2,] -0.326233361
[3,] 1.329799263
[4,] 1.272429321
[5,] 0.414641434
[6,] -1.539950042
[7,] -0.928567035
[8,] -0.294720447
[9,] -0.005767173
[10,] 2.404653389
DT[,list(sum=sum(a),groupsize=.N),by=list(chunk=(0:(nrow(DT)-1))%/%4)]
chunk sum groupsize
[1,] 0 3.538950 4
[2,] 1 -2.348596 4
[3,] 2 2.398886 2
诚然,这是一个相当长的声明。它命名列并返回组大小,以显示最后一个块确实只包含所需的 2 行。
一旦感到舒服它正在做正确的事情,它就可以缩短为:
DT[,sum(a),by=list(chunk=(0:(nrow(DT)-1))%/%4)]
chunk V1
[1,] 0 3.538950
[2,] 1 -2.348596
[3,] 2 2.398886
请注意,您可以像这样动态地进行聚合;它们不需要先添加到数据中。如果您在生产脚本中有很多不同的聚合,或者只是想与命令行中的数据进行交互,那么像这样非常小的生产力差异有时会有所帮助,具体取决于您的工作流程。
注意:我选择了sum
,但可以用somefunction(.SD)
或(更有可能)替换为list(exp1,exp2,...)
每个exp
将列名视为变量名的任何 R 表达式。
您可以在对 的调用中定义 8 行 ID ddply
。
不是特别优雅,但使用ddply
(以及head
示例函数)
df <- data.frame(x = rnorm(100), y = rnorm(100))
ddply(df, .(row_id = rep(seq(ceiling(nrow(df) / 8)), each = 8)[1:nrow(df)]),
head, n = 1)