Home > OS >  calculate a weighted mean by group with dplyr (and replicate other approaches)
calculate a weighted mean by group with dplyr (and replicate other approaches)

Time:02-20

I'm confused about the syntax of dplyr when attempting to compute a weighted mean.

I am following David's suggestion here. The syntax is very transparent and therefore attractive, but it does not appear to work as I expected: Below the weigthed mean is computed for the whole data, rather than grouping by the B variable.

head(df)
# A tibble: 4 × 3
      A     B     P
  <dbl> <dbl> <dbl>
1     1    10   0.4
2     2    10   0.6
3     1    20   0.2
4     2    20   0.8

library(dplyr)
df %>% group_by(B) %>%
    summarise(wm = weighted.mean(A, P))
# wm
# 1 1.7

I can achieve the desired result in several other ways. How can I use dplyr to replicate the calculations below?

# with a slit/apply routine:
sapply(split(df, df$B), function(x) weighted.mean(x$A, x$P))
#  10  20 
# 1.6 1.8 

# with data.table
library(data.table)
setDT(df)[, .(wm = weighted.mean(A, P)), B]
#     B  wm
# 1: 10 1.6
# 2: 20 1.8

# with plyr:
library(plyr)
ddply(df, .(B), summarise, wm = weighted.mean(A, P))
#    B  wm
# 1 10 1.6
# 2 20 1.8

# with aggregate | the formula approach is mysterious
df$wm <- 1:nrow(df)
aggregate(wm ~ B, data=df, function(x) weighted.mean(df$A[x], df$P[x]))
#    B  wm
# 1 10 1.6
# 2 20 1.8
df$wm <- NULL  # no longer needed

Here is the toy data (a tibble, rather than a standard dataframe):

library(tidyverse)
df = structure(list(A = c(1, 2, 1, 2), B = c(10, 10, 20, 20), P = c(0.4, 0.6, 0.2, 0.8)), 
    row.names = c(NA, -4L), class = c("tbl_df", "tbl", "data.frame"))

Here is one and another post about calculating a mean by group with dplyr, but I did not see how they could shed light on my problem.

CodePudding user response:

This is very common thing that happens when package plyr is loaded because plyr::summarise can override dplyr::summarise function. Just use dplyr::summarise. It's the first thing to check if summarise outputs unexpected results.

Another way is to detach the plyr package before using dplyr:

detach("package:plyr")
library("dplyr")
df %>% group_by(B) %>%
    summarise(wm = weighted.mean(A, P))
#       B    wm
#    <dbl> <dbl>
# 1    10   1.6
# 2    20   1.8

  • Related