Q:

Calculate mean for column grouped by values of two other columns

I have a dataframe with 5 columns. I know how to calculate the mean for one column grouped by another column. However, i need to group it by two columns. For example, I want to calculate the mean for column 5 grouped by column 1 and column 2.
df <- structure(list(Country = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 
3L, 3L, 3L), .Label = c("AT", "CH", "DE"), class = "factor"), 
    Occupation = c(1L, 3L, 5L, 3L, 1L, 2L, 5L, 3L, 5L, 3L, 1L, 
    2L, 1L, 5L, 3L, 3L, 1L, 3L, 2L, 5L, 5L, 1L, 2L, 1L, 3L), 
    Age = c(20L, 46L, 30L, 12L, 73L, 53L, 19L, 43L, 65L, 53L, 
    19L, 34L, 76L, 25L, 45L, 39L, 18L, 59L, 37L, 24L, 19L, 60L, 
    51L, 32L, 29L), Gender = structure(c(1L, 1L, 2L, 2L, 2L, 
    1L, 2L, 2L, 2L, 1L, 2L, 2L, 1L, 1L, 2L, 2L, 1L, 2L, 1L, 1L, 
    2L, 2L, 1L, 1L, 2L), .Label = c("female", "male"), class = "factor"), 
    Income = c(100L, 80L, 78L, 29L, 156L, 56L, 95L, 104L, 87L, 
    56L, 203L, 45L, 112L, 78L, 56L, 140L, 99L, 67L, 89L, 109L, 
    43L, 145L, 30L, 101L, 77L)), class = "data.frame", row.names = c(NA, 
-25L))

head(df)

  Country Occupation Age Gender Income
1      AT          1  20 female    100
2      AT          3  46 female     80
3      AT          5  30   male     78
4      AT          3  12   male     29
5      AT          1  73   male    156
6      AT          2  53 female     56

So what I want to to is calculate the mean for column ‘income’, grouped by country and occupation. E.g., I want to calculate the mean of ‘income’ for all those people living in country ‘AT’ with occupation ‘3’, the mean of ‘income’ for all those living in country ‘CH’ with occupation ‘1’ and so on.

A:

(1) base method (aggregate)
mean.df <- aggregate(Income ~ Country + Occupation, df, mean)
names(mean.df)[3] <- "Income_Mean"
merge(df, mean.df)

(2) base method (tapply)
mean.df1 <- tapply(df$Income, list(df$Country, df$Occupation), mean)
mean.df2 <- as.data.frame(as.table(mean.df1))
names(mean.df2) <- c("Country", "Occupation", "Income_Mean")
merge(df, mean.df2)

(3) stats method (ave)
df2 <- df
df2$Income_Mean <- ave(df$Income, df$Country, df$Occupation)

(4) dplyr method
df %>% group_by(Country, Occupation) %>%
       mutate(Income_Mean = mean(Income))

Output :
   Country Occupation   Age Gender Income Income_Mean
   <fct>        <int> <int> <fct>   <int>       <dbl>
 1 AT               1    20 female    100       128  
 2 AT               3    46 female     80        71  
 3 AT               5    30 male       78        86.5
 4 AT               3    12 male       29        71  
 5 AT               1    73 male      156       128  
 6 AT               2    53 female     56        56  
 7 AT               5    19 male       95        86.5
 8 AT               3    43 male      104        71  
 9 CH               5    65 male       87        82.5
10 CH               3    53 female     56        84
# ... with 15 more rows

