Vinterwoo
Vinterwoo

Reputation: 3941

Calculate means from different data frames

My goal is calculate a final data frame, which would contain the means from several different data frames. Given data like this:

A <- c(1,2,3,4,5,6,7,8,9)
B <- c(2,2,2,3,4,5,6,7,8)
C <- c(1,1,1,1,1,1,2,2,1)
D <- c(5,5,5,5,6,6,6,7,7)
E <- c(4,4,3,5,6,7,8,9,7)

DF1 <- data.frame(A,B,C)
DF2 <- data.frame(E,D,C)
DF3 <- data.frame(A,C,E)
DF4 <- data.frame(A,D,E)

I'd like to calculate means for all three columns (per row) in each data frame. To do this I put together a for loop:

All <- data.frame(matrix(ncol = 3, nrow = 9))
for(i in seq(1:ncol(DF1))){
    All[,i] <- mean(c(DF1[,i], DF2[,i], DF3[,i], DF4[,i]))
}

        X1       X2       X3
1 5.222222 4.277778 3.555556
2 5.222222 4.277778 3.555556
3 5.222222 4.277778 3.555556
4 5.222222 4.277778 3.555556
5 5.222222 4.277778 3.555556
6 5.222222 4.277778 3.555556
7 5.222222 4.277778 3.555556
8 5.222222 4.277778 3.555556
9 5.222222 4.277778 3.555556

But the end result was that I calculated entire column means (as opposed to a mean for each individual row).

For example, the first row and first column for each of the 4 data frames is 1,4,1,1. So I would expect the first col and row of the final data frame to be 1.75 (mean(c(1,4,1,1))

Upvotes: 1

Views: 2816

Answers (3)

Abdou
Abdou

Reputation: 13274

A combination of tidyverse and base:

#install.packages('tidyverse')

library(tidyverse)


transpose(list(DF1, DF2, DF3, DF4)) %>%
  map(function(x) 
    rowMeans(do.call(rbind.data.frame, 
                     transpose(x)))) %>%
  bind_cols()

Should yield:

#       A     B     C
#   <dbl> <dbl> <dbl>
# 1  1.75  3.25   2.5
# 2  2.50  3.25   2.5
# 3  3.00  3.25   2.0
# 4  4.25  3.50   3.0
# 5  5.25  4.25   3.5
# 6  6.25  4.50   4.0
# 7  7.25  5.00   5.0
# 8  8.25  5.75   5.5
# 9  8.50  5.75   4.0

Upvotes: 2

akrun
akrun

Reputation: 887118

We place the datasets in a list, get the sum (+) of corresponding elements using Reduce and divide it by the number of datasets

Reduce(`+`, mget(paste0("DF", 1:4)))/4
#     A    B   C
#1 1.75 3.25 2.5
#2 2.50 3.25 2.5
#3 3.00 3.25 2.0
#4 4.25 3.50 3.0
#5 5.25 4.25 3.5
#6 6.25 4.50 4.0
#7 7.25 5.00 5.0
#8 8.25 5.75 5.5
#9 8.50 5.75 4.0

NOTE: It should be faster than any apply based solutions and the output is a data.frame as that of the original dataset


If we want the tidyverse, then another option is

library(dplyr)
library(tidyr)
library(purrr)
library(tibble)
mget(paste0("DF", 1:4)) %>%
        map(rownames_to_column, "rn") %>% 
        map(setNames, c("rn", LETTERS[1:3])) %>%
        bind_rows() %>% 
        group_by(rn) %>%
        summarise_each(funs(mean))
# A tibble: 9 × 4
#     rn     A     B     C
#  <chr> <dbl> <dbl> <dbl>
#1     1  1.75  3.25   2.5
#2     2  2.50  3.25   2.5
#3     3  3.00  3.25   2.0
#4     4  4.25  3.50   3.0
#5     5  5.25  4.25   3.5
#6     6  6.25  4.50   4.0
#7     7  7.25  5.00   5.0
#8     8  8.25  5.75   5.5
#9     9  8.50  5.75   4.0

Upvotes: 4

alistaire
alistaire

Reputation: 43334

Since what you're describing is effectively an array, you can actually make it one with abind::abind, which makes the operation pretty simple:

apply(abind::abind(DF1, DF2, DF3, DF4, along = 3), 1:2, mean)
##          A    D   E
##  [1,] 1.75 3.25 2.5
##  [2,] 2.50 3.25 2.5
##  [3,] 3.00 3.25 2.0
##  [4,] 4.25 3.50 3.0
##  [5,] 5.25 4.25 3.5
##  [6,] 6.25 4.50 4.0
##  [7,] 7.25 5.00 5.0
##  [8,] 8.25 5.75 5.5
##  [9,] 8.50 5.75 4.0

The column names are meaningless, and the result is a matrix, not a data.frame, but even if you wrap it in data.frame, it's still very fast.

Upvotes: 3

Related Questions