按组和列加权平均数

5
我希望能够按组获得加权平均数,对于每个(实际上大约60个)列。这个问题与以下问题非常相似:在数据框中重复应用ave以计算组均值
到目前为止,我已经想出了两种方法来获得加权平均数:
1. 对于每个列,使用单独的sapply语句。 2. 在for-loop中放置一个sapply语句。
然而,我感觉必须有一种方法可以在sapply语句中插入apply语句或反之亦然,从而消除for-loop。我尝试了许多变化但没有成功。我还查看了sweep函数。
以下是我目前拥有的代码。
df <- read.table(text= "
          region    state  county  weights y1980  y1990  y2000
             1        1       1       10     100    200     50
             1        1       2        5      50    100    200
             1        1       3      120    1000    500    250
             1        1       4        2      25    100    400
             1        1       4       15     125    150    200

             2        2       1        1      10     50    150
             2        2       2       10      10     10    200
             2        2       2       40      40    100     30
             2        2       3       20     100    100     10
", header=TRUE, na.strings=NA)

# add a group variable to the data set

group <- paste(df$region, '_', df$state, '_', df$county, sep = "")
df    <- data.frame(group, df)

# obtain weighted averages for y1980, y1990 and y2000 
# one column at a time using one sapply per column

sapply(split(df, df$group), function(x) weighted.mean(x$y1980, w = x$weights))
sapply(split(df, df$group), function(x) weighted.mean(x$y1990, w = x$weights))
sapply(split(df, df$group), function(x) weighted.mean(x$y2000, w = x$weights))

# obtain weighted average for y1980, y1990 and y2000
# one column at a time using a for-loop

y <- matrix(NA, nrow=7, ncol=3)
group.b <- df[!duplicated(df$group), 1]

for(i in 6:8) { 

    y[,(i-5)] <- sapply(split(df[,c(1:5,i)], df$group), function(x) weighted.mean(x[,6], w = x$weights))

}

# add weighted averages to the original data set

y2 <- data.frame(group.b, y)
colnames(y2) <- c('group','ave1980','ave1990','ave2000')
y2

y3 <- merge(df, y2, by=c('group'), all = TRUE)
y3

最近我提了很多问题,对此感到抱歉,也感谢您的任何建议。

编辑后显示y3

  group region state county weights y1980 y1990 y2000   ave1980  ave1990  ave2000
1 1_1_1      1     1      1      10   100   200    50  100.0000 200.0000  50.0000
2 1_1_2      1     1      2       5    50   100   200   50.0000 100.0000 200.0000
3 1_1_3      1     1      3     120  1000   500   250 1000.0000 500.0000 250.0000
4 1_1_4      1     1      4       2    25   100   400  113.2353 144.1176 223.5294
5 1_1_4      1     1      4      15   125   150   200  113.2353 144.1176 223.5294
6 2_2_1      2     2      1       1    10    50   150   10.0000  50.0000 150.0000
7 2_2_2      2     2      2      10    10    10   200   34.0000  82.0000  64.0000
8 2_2_2      2     2      2      40    40   100    30   34.0000  82.0000  64.0000
9 2_2_3      2     2      3      20   100   100    10  100.0000 100.0000  10.0000

请继续提出问题,但如果您能提供一些虚拟数据并演示所需结果的外观,我(我们?)将不胜感激。 - Roman Luštrik
1
抱歉。我已将数据集“y3”的内容添加到我的帖子中。虚拟数据位于数据集“df”中,代码似乎在我的电脑上运行。 - Mark Miller
2个回答

6

我建议使用data.table软件包:

library(data.table)
dt <- as.data.table(df)
dt2 <- dt[,lapply(.SD,weighted.mean,w=weights),by=list(region,state,county)]
print(dt2)

   region state county   weights     y1980    y1990    y2000
1:      1     1      1  10.00000  100.0000 200.0000  50.0000
2:      1     1      2   5.00000   50.0000 100.0000 200.0000
3:      1     1      3 120.00000 1000.0000 500.0000 250.0000
4:      1     1      4  13.47059  113.2353 144.1176 223.5294
5:      2     2      1   1.00000   10.0000  50.0000 150.0000
6:      2     2      2  34.00000   34.0000  82.0000  64.0000
7:      2     2      3  20.00000  100.0000 100.0000  10.0000

如果您想的话,可以随后与原始数据表进行合并
merge(dt,dt2,by=c("region","state","county"))

   region state county weights.x y1980.x y1990.x y2000.x weights.y   y1980.y  y1990.y  y2000.y
1:      1     1      1        10     100     200      50  10.00000  100.0000 200.0000  50.0000
2:      1     1      2         5      50     100     200   5.00000   50.0000 100.0000 200.0000
3:      1     1      3       120    1000     500     250 120.00000 1000.0000 500.0000 250.0000
4:      1     1      4         2      25     100     400  13.47059  113.2353 144.1176 223.5294
5:      1     1      4        15     125     150     200  13.47059  113.2353 144.1176 223.5294
6:      2     2      1         1      10      50     150   1.00000   10.0000  50.0000 150.0000
7:      2     2      2        10      10      10     200  34.00000   34.0000  82.0000  64.0000
8:      2     2      2        40      40     100      30  34.00000   34.0000  82.0000  64.0000
9:      2     2      3        20     100     100      10  20.00000  100.0000 100.0000  10.0000

如何限制在此处仅对子集(例如list(y1980, y1990))执行weighed.mean操作? - FooBar
2
@FooBar 使用.SDcols。请参阅help("data.table") - Roland

1
我找到了如何将sapply嵌套在apply中,以获得按组和列加权平均值而不使用显式的for-loop。下面我提供数据集、apply语句以及对apply语句的解释。
以下是原始帖子中的数据集:
df <- read.table(text= "
          region    state  county  weights y1980  y1990  y2000
             1        1       1       10     100    200     50
             1        1       2        5      50    100    200
             1        1       3      120    1000    500    250
             1        1       4        2      25    100    400
             1        1       4       15     125    150    200

             2        2       1        1      10     50    150
             2        2       2       10      10     10    200
             2        2       2       40      40    100     30
             2        2       3       20     100    100     10
", header=TRUE, na.strings=NA)

# add a group variable to the data set

group <- paste(df$region, '_', df$state, '_', df$county, sep = "")
df    <- data.frame(group, df)

这里是获取所需加权平均值的apply / sapply代码。
apply(df[,6:ncol(df)], 2, function(x) {sapply(split(data.frame(df[,1:5], x), df$group), function(y) weighted.mean(y[,6], w = y$weights))})

以下是对上述apply/sapply语句的解释:

  1. 请注意,apply语句逐个选择df的第6到8列。

  2. 对于这三列中的每一列,我都创建了一个新的数据框,将该单独列与df的前五列组合在一起。

  3. 然后,我将这些新的6列数据框按照分组变量df$group拆分成块。

  4. 一旦一个包含六列的数据框被拆分成其各自的块,我就计算每个块的最后一列(第6列)的加权平均值。

以下是结果:

          y1980    y1990    y2000
1_1_1  100.0000 200.0000  50.0000
1_1_2   50.0000 100.0000 200.0000
1_1_3 1000.0000 500.0000 250.0000
1_1_4  113.2353 144.1176 223.5294
2_2_1   10.0000  50.0000 150.0000
2_2_2   34.0000  82.0000  64.0000
2_2_3  100.0000 100.0000  10.0000

使用data.table包很好,但在我更熟悉其语法及其与data.frame语法的区别之前,我认为了解如何使用applysapply来实现相同的功能是很有益的。现在我可以使用这两种方法,以及原始帖子中的方法,来相互检查并了解它们的所有信息。


这个解决方案有点复杂,其性能可能不太好(但我没有进行基准测试)。你基本上是在试图重新发明轮子。如果你想要漂亮的语法,应该看看plyr包。我演示了data.table的解决方案,因为它在处理大数据集时更快。所有的分割-应用-组合函数都允许你指定多个分组变量。 - Roland
我刚刚对两种解决方案进行了基准测试。你的速度大约快了2.5倍。感谢你的建议。 - Mark Miller

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接