How to aggregate data in data.frame in R

前端 未结 5 1851
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-10 22:24

I have a large data.frame. The data.frame include a lot of values.

For example:

df <- data.frame(Company = c(\'A\', \'A\', \'B\', \'C\', \'A\', \'         


        
相关标签:
5条回答
  • 2020-12-10 22:48

    The concise data.table solution already posted is using column numbers instead of column names. This is considered bad practice according to Frequently Asked Questions about data.table, section 1.1:

    If your colleague comes along and reads your code later they may have to hunt around to find out which column is number 5. If you or they change the column ordering higher up in your R program, you may produce wrong results with no warning or error if you forget to change all the places in your code which refer to column number 5.

    So, I would like to propose alternative approaches which use column names.

    Variant 1

    library(data.table)
    setDT(df)[, .(average.Age = mean(Age), 
                  average.Wages = mean(Wages), 
                  sum.Education.University = sum(Education.University),
                  average.Productivity = mean(Productivity)), 
              by = Company]
    
       Company average.Age average.Wages sum.Education.University average.Productivity
    1:       A    27.00000      56666.67                        2             102.6667
    2:       B    28.66667      68333.33                        3             111.6667
    3:       C    29.00000      53333.33                        1             101.6667
    

    Here, every column is aggregated separately. Although it requires more of typing, it has several benefits:

    1. It is easy to understand what the code snippet is intended to do.
    2. The column names in the result can be amended as required.
    3. The order of columns in the result can be controlled if required.

    Variant 2

    If there are many columns which require the same operations, the data.table FAQ recommends to use .SDcols. So, we can do

    m_cols <- c("Age", "Wages", "Productivity")
    s_cols <- c("Education.University")
    by_cols <- c("Company") 
    setDT(df)[, c(.SD[, lapply(.SD, mean), .SDcols = m_cols],
                  .SD[, lapply(.SD, sum ), .SDcols = s_cols]),
              by = by_cols]
    
       Company      Age    Wages Productivity Education.University
    1:       A 27.00000 56666.67     102.6667                    2
    2:       B 28.66667 68333.33     111.6667                    3
    3:       C 29.00000 53333.33     101.6667                    1
    

    This is similar to Akrun's answer but uses column names instead of column numbers. In addition, the column names are stored in a variable which is handy for programming.

    Note that by_cols may contain additional columns for aggregation, .e.g,

    by_cols <- c("Company", "Name")
    

    If column order matters, we can use setcolorder():

    result <- setDT(df)[, c(.SD[, lapply(.SD, mean), .SDcols = m_cols],
                            .SD[, lapply(.SD, sum ), .SDcols = s_cols]),
                        by = by_cols]
    setcolorder(result, intersect(names(df), names(result)))
    result
    
       Company      Age    Wages Education.University Productivity
    1:       A 27.00000 56666.67                    2     102.6667
    2:       B 28.66667 68333.33                    3     111.6667
    3:       C 29.00000 53333.33                    1     101.6667
    

    Likewise, the column names of the result can be amended to meet OP's requirements:

    setnames(result, m_cols, paste0("average.", m_cols))
    setnames(result, s_cols, paste0("sum.", s_cols))
    result
    
       Company average.Age average.Wages sum.Education.University average.Productivity
    1:       A    27.00000      56666.67                        2             102.6667
    2:       B    28.66667      68333.33                        3             111.6667
    3:       C    29.00000      53333.33                        1             101.6667
    

    Note that the data.table functions setcolorder() and setnames() work in place, i.e., without copying the data.table object. This saves memory and time which is of particular importantance when dealing with large tables.

    0 讨论(0)
  • 2020-12-10 22:49

    Base R

    cbind(aggregate(.~Company, df[,-c(2, 5)], mean),
          aggregate(Education.University~Company, df, sum)[-1])
    #  Company      Age    Wages Productivity Education.University
    #1       A 27.00000 56666.67     102.6667                    2
    #2       B 28.66667 68333.33     111.6667                    3
    #3       C 29.00000 53333.33     101.6667                    1
    

    Here is the longer version that may be easier to understand

    merge(x = aggregate(x = list(Age_av = df$Age,
                                 Wages_av = df$Wages,
                                 Productivity_av = df$Productivity),
                    by = list(Company = df$Company),
                    FUN = mean),
          y = aggregate(x = list(Education.University_sum = df$Education.University),
                    by = list(Company = df$Company),
                    FUN = sum),
          by = "Company")
    #  Company   Age_av Wages_av Productivity_av Education.University_sum
    #1       A 27.00000 56666.67        102.6667                        2
    #2       B 28.66667 68333.33        111.6667                        3
    #3       C 29.00000 53333.33        101.6667                        1
    
    0 讨论(0)
  • 2020-12-10 23:06

    One option is using data.table

    library(data.table)
    setDT(df)[, c(lapply(.SD[, c(2:3, 5), with = FALSE], mean), 
        .(Education.University = sum(Education.University))), by = Company]
    #   Company      Age    Wages Productivity Education.University
    #1:       A 27.00000 56666.67     102.6667                    2
    #2:       B 28.66667 68333.33     111.6667                    3
    #3:       C 29.00000 53333.33     101.6667                    1
    

    Or with dplyr

    library(dplyr)
    df %>%
       group_by(Company) %>% 
       mutate(Education.University = sum(Education.University)) %>%
       summarise_if(is.numeric, mean)
    # A tibble: 3 x 5
    #  Company      Age    Wages Education.University Productivity
    #   <fctr>    <dbl>    <dbl>                <dbl>        <dbl>
    #1       A 27.00000 56666.67                    2     102.6667
    #2       B 28.66667 68333.33                    3     111.6667
    #3       C 29.00000 53333.33                    1     101.6667
    
    0 讨论(0)
  • 2020-12-10 23:06

    Just use the "aggregate" function

    aggregate(x = df[c("Age","Wages","Education.University","Productivity")], by = df[c("Company")], FUN = mean)
    
    #  Company      Age    Wages Education.University Productivity
    #1       A 27.00000 56666.67            0.6666667     102.6667
    #2       B 28.66667 68333.33            1.0000000     111.6667
    #3       C 29.00000 53333.33            0.3333333     101.6667
    
    0 讨论(0)
  • 2020-12-10 23:08

    You can easily do it by using dplyr library.

    library(dplyr)
    df %>% group_by(Company) %>% summarise(Age = mean(Age), Wages = mean(Wages), Education.University = sum(Education.University), Productivity = mean(Productivity))
    
    0 讨论(0)
提交回复
热议问题