R 组按聚合

R group by aggregate

在 R(我比较陌生)中,我有一个数据框,其中包含许多列和一个数字列,我需要根据另一列确定的组进行聚合。

 SessionID   Price
 '1',       '624.99'
 '1',       '697.99'
 '1',       '649.00'
 '7',       '779.00'
 '7',       '710.00'
 '7',       '2679.50'

我需要按 SessionID 和 return 每个 ONTO 原始数据帧的最大值和最小值进行分组,例如:

 SessionID   Price     Min     Max
 '1',       '624.99'   624.99  697.99
 '1',       '697.99'   624.99  697.99
 '1',       '649.00'   624.99  697.99
 '7',       '779.00'   710.00  2679.50
 '7',       '710.00'   710.00  2679.50
 '7',       '2679.50'  710.00  2679.50

有什么想法可以有效地做到这一点吗?

使用基数 R:

df <- transform(df, Min = ave(Price, SessionID, FUN = min),
                    Max = ave(Price, SessionID, FUN = max))
df
#  SessionID   Price    Min     Max
#1         1  624.99 624.99  697.99
#2         1  697.99 624.99  697.99
#3         1  649.00 624.99  697.99
#4         7  779.00 710.00 2679.50
#5         7  710.00 710.00 2679.50
#6         7 2679.50 710.00 2679.50

由于您想要的结果不是聚合的,而只是带有两个额外列的原始数据,因此您希望在 base R 中使用 ave 而不是 aggregate,如果需要,您通常会使用它按 SessionID 到 aggregate 数据。 (注意:AEBilgrau 显示您也可以使用带有一些额外匹配的聚合来做到这一点。)

同样,对于 dplyr,您想使用 mutate 而不是 summarise,因为您不想 aggregate/summarise 数据。

使用 dplyr:

library(dplyr)
df <- df %>% group_by(SessionID) %>% mutate(Min = min(Price), Max = max(Price))

使用 data.table 包:

library(data.table)

dt = data.table(SessionID=c(1,1,1,7,7,7), Price=c(624,697,649,779,710,2679))

dt[, c("Min", "Max"):=list(min(Price),max(Price)), by=SessionID]
dt
#   SessionId Price Min  Max
#1:         1   624 624  697
#2:         1   697 624  697
#3:         1   649 624  697
#4:         7   779 710 2679
#5:         7   710 710 2679
#6:         7  2679 710 2679

在您的情况下,如果您有 data.frame df,只需执行 dt=as.data.table(df) 并使用上面的代码。

我很好奇解决方案的平均基准 data.frame:

df = data.frame(SessionID=rep(1:1000, each=100), Price=runif(100000, 1, 2000))
dt = as.data.table(df)

algo1 <- function() 
{
    df %>% group_by(SessionID) %>% mutate(Min = min(Price), Max = max(Price))
}

algo2 <- function()
{
    dt[, c("Min", "Max"):=list(min(Price),max(Price)), by=SessionID]
}

algo3 <- function()
{
    tmp <- aggregate(Price ~ SessionID, df, function(x) c(Min = min(x), Max = max(x)))
    cbind(df, tmp[match(df$SessionID, tmp$SessionID), 2])
}

algo4 <- function()
{
    transform(df, Min = ave(Price, SessionID, FUN = min), Max = ave(Price, SessionID, FUN = max))
}   



#> system.time(algo1())
#   user  system elapsed 
#   0.03    0.00    0.19 

#> system.time(algo2())
#   user  system elapsed 
#   0.01    0.00    0.01 

#> system.time(algo3())
#   user  system elapsed 
#   0.77    0.01    0.78 

#> system.time(algo4())
#   user  system elapsed 
#   0.02    0.01    0.03 

这是我使用 aggregate 的解决方案。

首先加载数据:

df <- read.table(text = 
"SessionID   Price
'1'       '624.99'
'1'       '697.99'
'1'       '649.00'
'7'       '779.00'
'7'       '710.00'
'7'       '2679.50'", header = TRUE) 

然后aggregatematch又回到原来的data.frame:

tmp <- aggregate(Price ~ SessionID, df, function(x) c(Min = min(x), Max = max(x)))
df <- cbind(df, tmp[match(df$SessionID, tmp$SessionID), 2])
print(df)
#  SessionID   Price    Min     Max
#1         1  624.99 624.99  697.99
#2         1  697.99 624.99  697.99
#3         1  649.00 624.99  697.99
#4         7  779.00 710.00 2679.50
#5         7  710.00 710.00 2679.50
#6         7 2679.50 710.00 2679.50

编辑:根据下面的评论,您可能想知道为什么会这样。确实有些奇怪。但请记住,data.frame 只是花哨的 list。尝试调用 str(tmp),您会看到 Price 列本身是 2 x 2 数字矩阵。它变得混乱,因为 print.data.frame 知道如何处理这个,所以 print(tmp) 看起来有 3 列。无论如何,tmp[2] 只需访问 data.frame/list 的第二个 column/entry 和第 1 列 data.frame 的 return而 tmp[,2] 访问第二列和 return 存储的数据类型。