89_Simple
89_Simple

Reputation: 3815

taking weighted average of a list

I have a vector of weight and a list of the same length as weight. I want to calculate the weighted average of each element of the list using the weight as shown below:

wt <- c(0.3, 0.5, 0.2)      
temp_ls <- list(x = c(1, 2, 3, 4), y = c(5, 6, 7, 8), z = c(9, 10, 11, 12))

result_vec <- rep(length(temp_ls$x))
   
for(i in seq_along(temp_ls$x)){
  
   temp_vec <- rep(NA, length(temp_ls))
    
   for(j in seq_along(temp_ls)){
      
     temp_vec[j] <- temp_ls[[j]][i]
      
   }
    
  result_vec[i] <- weighted.mean(temp_vec, wt)
}


result_vec
# 4.6 5.6 6.6 7.6
  

This approach seems way too complicated and long and wondered if any shorter method is there using lapply family.

Upvotes: 2

Views: 235

Answers (1)

akrun
akrun

Reputation: 887971

An option is to transpose the list, loop over the list and apply the weighted.mean

library(dplyr)
library(purrr)
temp_ls %>%
     transpose %>%
     map_dbl(~ weighted.mean(flatten_dbl(.x), w = wt))
#[1] 4.6 5.6 6.6 7.6

Or another approach would be to enframe the list to a two column tibble, then unnest the list element and do a group by weighted.mean

library(tibble)
library(tidyr)
library(data.table)
enframe(temp_ls) %>% 
  mutate(wt = wt) %>%
  unnest(c(value)) %>% 
  group_by(grp = rowid(name)) %>% 
  summarise(out = weighted.mean(value, w = wt), .groups = 'drop') %>% 
  pull(out)
#[1] 4.6 5.6 6.6 7.6

Or using aggregate after stacking into two column data.frame in base R

aggregate(values ~ grp, transform(stack(temp_ls), 
   grp = ave(seq_along(ind), ind, FUN = seq_along)),
      FUN = weighted.mean, w = wt)
# grp values
#1   1    4.6
#2   2    5.6
#3   3    6.6
#4   4    7.6

Or using mapply

do.call(mapply, c(FUN = function(...) weighted.mean(c(...), w = wt), 
     unname(temp_ls)))
#[1] 4.6 5.6 6.6 7.6

Or with Map

unlist(do.call(Map, c(f = function(...) weighted.mean(c(...), 
            w = wt), unname(temp_ls))))

Or more compactly

apply(do.call(cbind, temp_ls), 1, weighted.mean, w = wt)
#[1] 4.6 5.6 6.6 7.6

Upvotes: 4

Related Questions