我的数据框有两列用作分组键,17列需要在每个组中求和,而另一列应该是平均值。让我在diamonds
的{{1}}的不同数据框中说明这一点。
我知道我可以这样做:
ggplot2
但是虽然3列是合理的,但其中有17列是不可接受的。
在研究这个时,我找到了ddply(diamonds, ~cut, summarise, x=sum(x), y=sum(y), z=sum(z), price=mean(price))
函数,但我想出的最好的是:
colwise
是否有可能进一步改善这一点?我想以更简单的方式做到这一点,比如(虚构的命令):
cbind(ddply(diamonds, ~cut, colwise(sum, 7:9)), price=ddply(diamonds, ~cut, summarise, mean(price))[,2])
或:
ddply(diamonds, ~cut, colwise(sum, 7:9), price=mean(price))
总结一下:
ddply(diamonds, ~cut, colwise(sum, 7:9), colwise(mean, ~price))
,x
和y
一样。z
来执行此操作,而无需使用ddply
(或类似函数),如第二个示例所示。作为参考,我期望的结果是5行和5列:
cbind
答案 0 :(得分:10)
我想为此建议data.table
解决方案。您可以通过位置或名称轻松预定义要操作的列,然后重复使用相同的代码,无论您要操作多少列。
预定列名称
Sums <- 7:9
Means <- "price"
运行代码
library(data.table)
data.table(diamonds)[, c(lapply(.SD[, Sums, with = FALSE], sum),
lapply(.SD[, Means, with = FALSE], mean))
, by = cut]
# cut x y z price
# 1: Ideal 118691.07 118963.24 73304.61 3457.542
# 2: Premium 82385.88 81985.82 50297.49 4584.258
# 3: Good 28645.08 28703.75 17855.42 3928.864
# 4: Very Good 69359.09 69713.45 43009.52 3981.760
# 5: Fair 10057.50 9954.07 6412.26 4358.758
对于您的具体示例,这可以简化为
data.table(diamonds)[, c(lapply(.SD[, 7:9, with = FALSE], sum), pe = mean(price)), by = cut]
# cut x y z pe
# 1: Ideal 118691.07 118963.24 73304.61 3457.542
# 2: Premium 82385.88 81985.82 50297.49 4584.258
# 3: Good 28645.08 28703.75 17855.42 3928.864
# 4: Very Good 69359.09 69713.45 43009.52 3981.760
# 5: Fair 10057.50 9954.07 6412.26 4358.758
答案 1 :(得分:5)
针对您的特定情况的另一种方法(在我看来更容易阅读)(mean = sum/n
!)
nCut <- ddply(diamonds, ~cut, nrow)
res <- ddply(diamonds, ~cut, colwise(sum, 6:9))
res$price <- res$price/nCut$V1
或更通用,
do.call(merge,
lapply(c(colwise(sum, 7:9), colwise(mean, 6)),
function(cw) ddply(diamonds, ~cut, cw)))
答案 2 :(得分:5)
使用dplyr
的Antoher解决方案。首先,在要聚合的每个变量上应用两个聚合函数。在结果变量中,您只选择所需的函数/变量组合。
library(dplyr)
library(ggplot2)
diamonds %>%
group_by(cut) %>%
summarise_each(funs(sum, mean), x:z, price) %>%
select(cut, matches("[xyz]_sum"), price_mean)
答案 3 :(得分:2)
只是提出另一种解决方案:
library(plyr)
library(ggplot2)
trans <- list(mean = 8:10, sum = 7)
makeList <- function(inL, mdat = diamonds, by = ~cut) {
colN <- names(mdat)
args <- unlist(llply(names(inL), function(n) {
llply(inL[[n]], function(x) {
ret <- list(call(n, as.symbol(colN[[x]])))
names(ret) <- paste(n, colN[[x]], sep = ".")
ret
})
}))
args$.data <- as.symbol(deparse(substitute(mdat)))
args$.variables <- by
args$.fun <- as.symbol("summarise")
args
}
do.call(ddply, makeList(trans))
# cut mean.x mean.y mean.z sum.price
# 1 Fair 6.246894 6.182652 3.982770 7017600
# 2 Good 5.838785 5.850744 3.639507 19275009
# 3 Very Good 5.740696 5.770026 3.559801 48107623
# 4 Premium 5.973887 5.944879 3.647124 63221498
# 5 Ideal 5.507451 5.520080 3.401448 74513487
这个想法是函数makeList
为ddply
创建一个参数列表。通过这种方式,您可以非常轻松地将术语添加到列表中(如function.name = column.indices
),ddply
将按预期工作:
trans <- c(trans, sd = list(9:10))
do.call(ddply, makeList(trans))
# cut mean.x mean.y mean.z sum.price sd.y sd.z
# 1 Fair 6.246894 6.182652 3.982770 7017600 0.9563804 0.6516384
# 2 Good 5.838785 5.850744 3.639507 19275009 1.0515353 0.6548925
# 3 Very Good 5.740696 5.770026 3.559801 48107623 1.1029236 0.7302281
# 4 Premium 5.973887 5.944879 3.647124 63221498 1.2597511 0.7311610
# 5 Ideal 5.507451 5.520080 3.401448 74513487 1.0744953 0.6576481
答案 4 :(得分:2)
它使用dplyr
,但我相信这将完全以合理易读的语法完成指定的目标:
diamonds %>%
group_by(cut) %>%
select(x:z) %>%
summarize_each(funs(sum)) %>%
merge(diamonds %>%
group_by(cut) %>%
summarize(price = mean(price))
,by = "cut")
唯一的&#34;技巧&#34;是合并中有一个管道表达式,它与总和的计算分开处理平均价格的计算。
我将此解决方案与@David Arenburg(使用data.table
)和@thothal(使用问题请求使用plyr
)提供的解决方案进行基准测试,并进行了5000次重复。此处data.table
的出现速度低于plyr
和dplyr
。 dplyr
比plyr
快。可以想象,基准测试结果可能会根据列数,分组因子中的级别数以及应用的特定函数而发生变化。例如,MarkusN在我完成初始基准测试后提交了一个答案,该测试基本上比先前提交的样本数据答案快得多。他通过计算许多不需要的汇总统计数据来完成这一点,然后将它们扔掉......当然,必须有一个点,使这种方法的成本超过优势。
test replications elapsed relative user.self sys.self user.child sys.child
2 dataTable 5000 119.686 2.008 119.611 0.127 0 0
1 dplyr 5000 59.614 1.000 59.676 0.004 0 0
3 plyr 5000 68.505 1.149 68.493 0.064 0 0
? MarkusN 5000 23.172 ????? 23.926 0 0 0
当然,速度不是唯一的考虑因素。特别是,dplyr和plyr对于它们的加载顺序(dplyr之前的plyr)是挑剔的,并且有几个相互掩盖的函数。
答案 5 :(得分:1)
不是100%你正在寻找的东西,但它可能会给你另一个想法如何做到这一点。使用data.table
您可以执行以下操作:
diamonds2[, .(c = sum(c), p = sum(p), ce = sum(ce), pe = mean(pe)), by = cut]
要缩短代码(你尝试用colwise做的),你可能需要编写一些函数来实现你想要的。
答案 6 :(得分:0)
为了完整起见,我们提供了基于dplyr
的解决方案以及Veerendra Gadekar in another question和here by MarkusN发布的答案。
在这种特殊情况下,可以先将sum
应用于某些列,然后mean
应用于所有感兴趣的列:
diamonds %>%
group_by(cut) %>%
mutate_each('sum', 8:10) %>%
summarise_each('mean', 8:10, price)
这是可能的,因为mean
不会更改计算的列8:10
总和,并会计算所需的价格均值。但是如果我们想要计算价格的标准差而不是平均值,那么这种方法就不会起作用,因为8:10
列都是0。
更通用的方法可能是:
diamonds %>%
group_by(cut) %>%
mutate_each('sum', 8:10) %>%
mutate_each('mean', price) %>%
summarise_each('first', 8:10, price)
有人可能不会对summarise_each
重复之前命名的列规范感到高兴,但这似乎是一个优雅的解决方案。
它优于MarkusN的解决方案,它不需要匹配新创建的列,也不会更改其名称。
Veerendra Gadekar的解决方案应以select(cut, 8:10, price) %>% arrange(cut)
结束,以产生预期结果(列的子集,以及按分组键排序的行)。 Hong Ooi的建议与此处的第一个类似,但假设没有其他专栏。
最后,它似乎比data.table
解决方案更易读,更易于理解,例如the one proposed by David Arenburg。