计算除当前组之外的所有组的平均值
Posted
技术标签:
【中文标题】计算除当前组之外的所有组的平均值【英文标题】:Calculate mean of all groups except the current group 【发布时间】:2021-06-24 12:16:34 【问题描述】:我有一个包含两个分组变量“mkt”和“mdl”以及一些值“pr”的数据框:
df <- data.frame(mkt = c(1,1,1,1,2,2,2,2,2),
mdl = c('a','a','b','b','b','a','b','a','b'),
pr = c(120,120,110,110,145,130,145,130, 145))
df
mkt mdl pr
1 1 a 120
2 1 a 120
3 1 b 110
4 1 b 110
5 2 b 145
6 2 a 130
7 2 b 145
8 2 a 130
9 2 b 145
在每个 'mkt' 中,每个 'mdl' 的平均 'pr' 应该计算为同一 'mkt' 中所有其他 'mdl' 的 'pr' 的平均值,除了当前的“mdl”。
例如,对于由mkt == 1
和mdl == a
定义的组,'avgother' 计算为mkt == 1
(相同'mkt')和mdl == b
(所有其他' mdl' 比当前组a
)。
想要的结果:
# mkt mdl pr avgother
# 1 1 a 120 110
# 2 1 a 120 110
# 3 1 b 110 120
# 4 1 b 110 120
# 5 2 b 145 130
# 6 2 a 130 145
# 7 2 b 145 130
# 8 2 a 130 145
# 9 2 b 145 130
【问题讨论】:
【参考方案1】:首先获取每个mkt
和mdl
值的平均值,然后为每个mkt
排除当前值并获取剩余值的平均值。
library(dplyr)
library(purrr)
df %>%
group_by(mkt, mdl) %>%
summarise(avgother = mean(pr)) %>%
mutate(avgother = map_dbl(row_number(), ~mean(avgother[-.x]))) %>%
ungroup %>%
inner_join(df, by = c('mkt', 'mdl'))
# mkt mdl avgother pr
# <dbl> <chr> <dbl> <dbl>
#1 1 a 110 120
#2 1 a 110 120
#3 1 b 120 110
#4 1 b 120 110
#5 2 a 145 130
#6 2 a 145 130
#7 2 b 130 145
#8 2 b 130 145
#9 2 b 130 145
【讨论】:
【参考方案2】:使用data.table
,通过“mkt”计算总和和长度。然后,在每个 mkt-mdl 组内,计算平均值为(mkt sum - group sum) / (mkt length - group length)
library(data.table)
setDT(df)[ , `:=`(s = sum(pr), n = .N), by = mkt]
df[ , avgother := (s - sum(pr)) / (n - .N), by = .(mkt, mdl)]
df[ , `:=`(s = NULL, n = NULL)]
# mkt mdl pr avgother
# 1: 1 a 120 110
# 2: 1 a 120 110
# 3: 1 b 110 120
# 4: 1 b 110 120
# 5: 2 b 145 130
# 6: 2 a 130 145
# 7: 2 b 145 130
# 8: 2 a 130 145
# 9: 2 b 145 130
【讨论】:
【参考方案3】:考虑具有多个ave
的基R 调用,使用sum / count
的均值分解版本进行不同级别的分组计算:
df <- within(df,
avgoth <- (ave(pr, mkt, FUN=sum) - ave(pr, mkt, mdl, FUN=sum)) /
(ave(pr, mkt, FUN=length) - ave(pr, mkt, mdl, FUN=length))
)
df
# mkt mdl pr avgoth
# 1 1 a 120 110
# 2 1 a 120 110
# 3 1 b 110 120
# 4 1 b 110 120
# 5 2 b 145 130
# 6 2 a 130 145
# 7 2 b 145 130
# 8 2 a 130 145
# 9 2 b 145 130
【讨论】:
【参考方案4】:为了完整起见,这里是另一个data.table
方法,它使用按每个 i 分组,即同时加入和聚合。
为了演示,我们使用了一个增强的样本数据集,该数据集具有包含 3 种产品的第三市场:
df <- data.frame(mkt = c(1,1,1,1,2,2,2,2,2,3,3,3),
mdl = c('a','a','b','b','b','a','b','a','b', letters[1:3]),
pr = c(120,120,110,110,145,130,145,130, 145, 1:3))
library(data.table)
mdt <- setDT(df)[, .(mdl, s = sum(pr), .N), by = .(mkt)]
df[mdt, on = .(mkt, mdl), avgother := (sum(pr) - s) / (.N - N), by = .EACHI][]
mkt mdl pr avgother 1: 1 a 120 110.0 2: 1 a 120 110.0 3: 1 b 110 120.0 4: 1 b 110 120.0 5: 2 b 145 130.0 6: 2 a 130 145.0 7: 2 b 145 130.0 8: 2 a 130 145.0 9: 2 b 145 130.0 10: 3 a 1 2.5 11: 3 b 2 2.0 12: 3 c 3 1.5
临时表mdt
包含每个mkt
内的价格总和和计数,但在市场内为每个产品mdl
复制:
mdt
mkt mdl s N 1: 1 a 460 4 2: 1 a 460 4 3: 1 b 460 4 4: 1 b 460 4 5: 2 b 695 5 6: 2 a 695 5 7: 2 b 695 5 8: 2 a 695 5 9: 2 b 695 5 10: 3 a 6 3 11: 3 b 6 3 12: 3 c 6 3
在mdt
中有mkt
和 mdl
允许按每个 i 分组 (by = .EACHI
)
【讨论】:
【参考方案5】:这是一种在计算平均值之前通过子集pr
值直接计算avgother
的方法,这些值不属于mdl
的实际值。
这与迄今为止发布的其他答案完全不同,这有理由将其作为单独的答案发布,恕我直言。
# enhanced sample dataset covering more corner cases
df <- data.frame(mkt = c(1,1,1,1,2,2,2,2,2,3,3,3,4),
mdl = c('a','a','b','b','b','a','b','a','b', letters[1:3],'d'),
pr = c(120,120,110,110,145,130,145,130, 145, 1:3, 9))
library(data.table)
setDT(df)[, avgother := sapply(mdl, function(m) mean(pr[m != mdl])), by = mkt][]
mkt mdl pr avgother 1: 1 a 120 110.0 2: 1 a 120 110.0 3: 1 b 110 120.0 4: 1 b 110 120.0 5: 2 b 145 130.0 6: 2 a 130 145.0 7: 2 b 145 130.0 8: 2 a 130 145.0 9: 2 b 145 130.0 10: 3 a 1 2.5 11: 3 b 2 2.0 12: 3 c 3 1.5 13: 4 d 9 NaN
方法之间的区别
其他答案或多或少共享相同的方法(尽管以不同的方式实现)
-
为每个
mkt
计算pr
的总和和计数
为每个mkt
和mdl
计算pr
的总和和计数
从 mkt 总和和计数中减去 mkt/mdl 总和和计数
计算avgother
这种方法
按mkt
分组
在每个mkt
内循环通过mdl
,
子集pr
删除不属于mdl
实际值的值
在直接计算mean()
之前。
关于性能的警告:虽然代码本质上是单行,但这并不意味着它是最快的。
【讨论】:
以上是关于计算除当前组之外的所有组的平均值的主要内容,如果未能解决你的问题,请参考以下文章