计算除当前组之外的所有组的平均值

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 == 1mdl == 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】:

首先获取每个mktmdl 值的平均值,然后为每个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 的总和和计数 为每个mktmdl 计算pr 的总和和计数 从 mkt 总和和计数中减去 mkt/mdl 总和和计数 计算avgother

这种方法

mkt分组 在每个mkt 内循环通过mdl, 子集pr 删除属于mdl 实际值的值 在直接计算mean()之前。

关于性能的警告:虽然代码本质上是单行,但这并不意味着它是最快的。

【讨论】:

以上是关于计算除当前组之外的所有组的平均值的主要内容,如果未能解决你的问题,请参考以下文章

所有值和单独组的平均值

计算 Octave 中行组的列的平均值

计算组内子组的平均值

计算组的平均值,同时保留对象类型的列

加快附近组的计算?

元组列表和平均计算[重复]