聚合 - 处理FUN中两列的数据

时间:2016-09-24 12:59:01

标签: r

有人提出了许多类似的问题,但我无法在其他地方找到这个概念。对不起,如果这是重复。

我想要聚合数据框的一列,并创建一个基于两个旧列的新列。

这是(非工作)示例代码,用于显示我尝试的内容:

x <- c('a', 'a', 'b', 'b')
y <- c(1,2,3,4)
z <- c(0.3, 0.4, 0.5, 0.6)
df <- data.frame(x=x, y=y, z=z)

myfun <- function (vec) {
  sum(vec['y']*vec['z']) / sum(vec['y'])
}

df_agg <- aggregate(cbind(df$y, df$z), list(df$x), myfun)

我期待的结果是:

Group.1   V1
a         (1*0.3+2*0.4)/3
b         (3*0.5+4*0.6)/7

这可以用聚合来实现吗?或者我应该使用其他功能吗?

4 个答案:

答案 0 :(得分:1)

对于按组分列多个列的计算,我们可以使用data.table。转换&#39; data.frame&#39;到&#39; data.table&#39; (setDT(df)),按&#39; x&#39;分组获得&#39; y&#39;产品的sum和&#39; z&#39;除以&#39; y&#39;的sum

library(data.table)
setDT(df)[ , .(Out = sum(y*z)/sum(y)) , by = x]

或者可以使用dplyr

完成
library(dplyr)
df %>%
   group_by(x) %>%
   summarise(Out = sum(y*z)/sum(y))

by

中的base R
by(df[2:3], df[1], FUN = function(x) sum(x[1]*x[2])/sum(x[1]))

答案 1 :(得分:0)

试试这个:

library(data.table)
DT <- as.data.table(df)
DT[, sum(y*z)/sum(y), by=x]

它给出了:

   x        V1
1: a 0.3666667
2: b 0.5571429

您希望的输出是什么。

(1*0.3 + 2*0.4)/3 = 1.1/3 = 0.37
(3*0.5 + 4*0.6)/7 = 3.9/7 = 0.56

答案 2 :(得分:0)

以下是几个不使用包的单行程序。 (1)和(1a)使用aggregate

1)聚合聚合行索引,如下所示:

aggregate(list(V1 = 1:nrow(df)), df["x"], function(i) with(df[i, ], sum(y * z) / sum(y)))

,并提供:

  x        V1
1 a 0.3666667
2 b 0.5571429

可以根据问题中定义的myfun编写,如下所示:

aggregate(list(V1 = 1:nrow(df)), df["x"], function(i) myfun(df[i, ]))

1a)聚合(...,总和)使用聚合来计算y和y * z的总和,然后分成第二步:

with(aggregate(. ~ x, transform(df, yz = y*z), sum), data.frame(x, V1 = yz / y))

,并提供:

  x        V1
1 a 0.3666667
2 b 0.5571429

2)或使用by

do.call("rbind", by(df, df$x, with, data.frame(x = x[1], V1 = sum(y * z) / sum(y))))

,并提供:

  x        V1
a a 0.3666667
b b 0.5571429

3)tapply 虽然输出形式不同,但也可以使用tapply调用的比率:

with(df, tapply(y * z, x, sum) / tapply(y, x, sum))

,并提供:

        a         b 
0.3666667 0.5571429 

按摩要求形式:

setNames(as.data.frame.table(with(df, tapply(y * z, x, sum) / tapply(y, x, sum))), 
  c("x", "V1"))

4)rowsum rowsum可以类似于tapply的方式使用。它还提供了一种不同形式的输出作为一个列矩阵,其中x值成为行名:

m <- with(df, rowsum(y * z, x) / rowsum(y, x))

,并提供:

> m
       [,1]
a 0.3666667
b 0.5571429

我们可以将其置于如下所需的格式:

data.frame(x = rownames(m), V1 = m)

<强> sqldf

这是一个sqldf解决方案。它确实使用了sqldf包,但看起来特别简单:

library(sqldf)
sqldf("select x, sum(y * z) / sum(y) V1 from df group by x")

,并提供:

  x        V1
1 a 0.3666667
2 b 0.5571429

答案 3 :(得分:0)

这可以在基础R中完成,但需要重新思考一些类似R的东西。请注意,在聚合帮助中,它使用聚合中的cbind,但查看接收新的mini-data.frames的函数。他们只采取一个向量。帮助示例向您显示聚合并不适用于您希望的方式。此外,您应该尽可能利用矢量化数学函数。以下是有效的基础R方式,类似于@G的答案1a。格罗滕迪克。

# make your data.frame
x <- c('a', 'a', 'b', 'b')
y <- c(1,2,3,4)
z <- c(0.3, 0.4, 0.5, 0.6)
df <- data.frame(x=x, y=y, z=z)

df$yz <- df$y * df$z #vectorize this step

# at this point you can use a cbind in aggregate if you wish but 
# it won't do the whole thing for you. This only leaves one highly 
# optimized mathematical function that isn't vectorized (sum)
tdf <- aggregate(cbind(y, yz) ~ x, data = df, sum)

tdf$myFunValue <- tdf$yz / tdf$y # final step vectorized

尝试养成以这种方式思考R命令的习惯,最低限度需要聚合或应用族命令,而不是执行传统的编程操作,例如将所有内容放在一个循环中。