【问题标题】:Mean per group in a data.frame [duplicate]data.frame 中每组的平均值
【发布时间】:2021-11-08 23:32:28
【问题描述】:

我有一个data.frame,我需要计算每组的平均值(即下面的每个Month)。

Name     Month  Rate1     Rate2
Aira       1      12        23
Aira       2      18        73
Aira       3      19        45
Ben        1      53        19
Ben        2      22        87
Ben        3      19        45
Cat        1      22        87
Cat        2      67        43
Cat        3      45        32

我想要的输出如下所示,其中Rate1Rate2 的值是组均值。值请忽略,示例我已经补上。

Name       Rate1       Rate2
Aira        23.21       12.2
Ben         45.23       43.9
Cat         33.22       32.2

【问题讨论】:

  • 这可能有用R Grouping functions
  • 如果有更多的列,如 FirstName、LastName 和 Address 而不仅仅是 Name 分组依据?

标签: r aggregate


【解决方案1】:

这种类型的操作正是 aggregate 的设计目的:

d <- read.table(text=
'Name     Month  Rate1     Rate2
Aira       1      12        23
Aira       2      18        73
Aira       3      19        45
Ben        1      53        19
Ben        2      22        87
Ben        3      19        45
Cat        1      22        87
Cat        2      67        43
Cat        3      45        32', header=TRUE)

aggregate(d[, 3:4], list(d$Name), mean)

  Group.1    Rate1    Rate2
1    Aira 16.33333 47.00000
2     Ben 31.33333 50.33333
3     Cat 44.66667 54.00000

这里我们聚合 data.frame d 的第 3 列和第 4 列,按 d$Name 分组,并应用 mean 函数。


或者,使用公式接口:

aggregate(. ~ Name, d[-2], mean)

【讨论】:

  • 是的,将其更改为aggregate(d[, 3:4], list(Name=d$Name), mean)
  • 当使用 aggregate(as.numeric(matrix$value), list(matrix$hour), mean) 时,出于某种原因,我得到了一些 NaN-values。检查我的 data.table 中的 is.nan()is.na() 但是没有显示任何结果。有什么想法吗?
  • @jdepypere - 不知道为什么,但仔细查看split(matrix$value, matrix$hour) 的元素可能是值得的987654335@)
  • 很好的答案。 aggregate(d[, 3:4], list(d[,1]), mean)aggregate(d[, c('Rate1', 'Rate2')], list(d[, c('Name')]), mean) 更“一致”,恕我直言。
  • @FabianHabersack 你会?merge 得到原始数据的结果,但你需要为聚合对象使用不同的名称。使用 dplyr 可以更轻松地完成您的工作:d %&gt;% group_by(Name) %&gt;% mutate(mean1=mean(Rate1), mean2=mean(Rate2))
【解决方案2】:

或者使用dplyr包中的group_bysummarise_at

library(dplyr)

d %>%
  group_by(Name) %>%
  summarise_at(vars(-Month), funs(mean(., na.rm=TRUE)))

# A tibble: 3 x 3
  Name  Rate1 Rate2
  <fct> <dbl> <dbl>
1 Aira   16.3  47.0
2 Ben    31.3  50.3
3 Cat    44.7  54.0

请参阅?summarise_at,了解指定要作用的变量的多种方法。这里,vars(-Month) 表示所有变量除了Month


在 tidyverse/dplyr 的更新版本中,使用 summarise(across(...)) 优于 summarise_at

d %>% 
  group_by(Name) %>%
  summarise(across(-Month, mean, na.rm = TRUE))

【讨论】:

  • summarise _each 在新版本中已弃用。我应该使用什么?我试过 summarise_at 或 _if or_all。但没用。
  • 试试d %&gt;% group_by(Name) %&gt;% summarise_at(.vars = names(.)[3:4],.funs = c(mean="mean"))
  • 为什么要summarize_at 而不是summary?
  • summarise_at 允许您指定要汇总的特定变量范围
【解决方案3】:

您也可以使用包plyr,它在某种程度上更加通用:

library(plyr)

ddply(d, .(Name), summarize,  Rate1=mean(Rate1), Rate2=mean(Rate2))

  Name    Rate1    Rate2
1 Aira 16.33333 47.00000
2  Ben 31.33333 50.33333
3  Cat 44.66667 54.00000

【讨论】:

    【解决方案4】:

    第三个不错的选择是使用包 data.table,它也有类 data.frame,但是像您要查找的操作计算得更快。

    library(data.table)
    mydt <- structure(list(Name = c("Aira", "Aira", "Aira", "Ben", "Ben", "Ben", "Cat", "Cat", "Cat"), Month = c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L), Rate1 = c(15.6396600443877, 2.15649279424609, 6.24692918928743, 2.37658797276116, 34.7500663272292, 3.28750138697048, 29.3265553981065, 17.9821839334431, 10.8639802575958), Rate2 = c(17.1680489538369, 5.84231656330206, 8.54330866437461, 5.88415184986176, 3.02064294862551, 17.2053351400752, 16.9552950199166, 2.56058000170089, 15.7496228048122)), .Names = c("Name", "Month", "Rate1", "Rate2"), row.names = c(NA, -9L), class = c("data.table", "data.frame"))
    

    现在为每个人(姓名)取所有 3 个月的 Rate1 和 Rate2 的平均值: 首先,决定要对哪些列取平均值

    colstoavg <- names(mydt)[3:4]
    

    现在我们使用 lapply 对我们想要平均的列取平均值 (colstoavg)

    mydt.mean <- mydt[,lapply(.SD,mean,na.rm=TRUE),by=Name,.SDcols=colstoavg]
    
     mydt.mean
       Name     Rate1     Rate2
    1: Aira  8.014361 10.517891
    2:  Ben 13.471385  8.703377
    3:  Cat 19.390907 11.755166
    

    【讨论】:

    • 在data.tables中也要使用lapply吗?
    • @HermanToothrot 我会这么说,至少对我来说。特别是在这种情况下,通过使用.SDcols,他们准确地指定了要将函数应用于哪些列。如果您可以想象一个包含大量列的 data.table 的情况,您可以首先获取所有数字列的名称,然后使用.SDcols = numericVars 设置它们,而不必尝试全部命名在 j 列中。另一个实例是使用:= 就地赋值.SD := lapply(.SD, f)
    【解决方案5】:

    以下是在基本 R 中执行此操作的多种方法,包括替代 aggregate 方法。下面的示例返回意味着每月,我认为这是您所要求的。虽然,可以使用相同的方法来返回每个人的平均值:

    使用ave

    my.data <- read.table(text = '
         Name     Month  Rate1     Rate2
         Aira       1      12        23
         Aira       2      18        73
         Aira       3      19        45
         Ben        1      53        19
         Ben        2      22        87
         Ben        3      19        45
         Cat        1      22        87
         Cat        2      67        43
         Cat        3      45        32
    ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA')
    
    Rate1.mean <- with(my.data, ave(Rate1, Month, FUN = function(x) mean(x, na.rm = TRUE)))
    Rate2.mean <- with(my.data, ave(Rate2, Month, FUN = function(x) mean(x, na.rm = TRUE)))
    
    my.data <- data.frame(my.data, Rate1.mean, Rate2.mean)
    my.data
    

    使用by

    my.data <- read.table(text = '
         Name     Month  Rate1     Rate2
         Aira       1      12        23
         Aira       2      18        73
         Aira       3      19        45
         Ben        1      53        19
         Ben        2      22        87
         Ben        3      19        45
         Cat        1      22        87
         Cat        2      67        43
         Cat        3      45        32
    ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA')
    
    by.month <- as.data.frame(do.call("rbind", by(my.data, my.data$Month, FUN = function(x) colMeans(x[,3:4]))))
    colnames(by.month) <- c('Rate1.mean', 'Rate2.mean')
    by.month <- cbind(Month = rownames(by.month), by.month)
    
    my.data <- merge(my.data, by.month, by = 'Month')
    my.data
    

    使用lapplysplit

    my.data <- read.table(text = '
         Name     Month  Rate1     Rate2
         Aira       1      12        23
         Aira       2      18        73
         Aira       3      19        45
         Ben        1      53        19
         Ben        2      22        87
         Ben        3      19        45
         Cat        1      22        87
         Cat        2      67        43
         Cat        3      45        32
    ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA')
    
    ly.mean <- lapply(split(my.data, my.data$Month), function(x) c(Mean = colMeans(x[,3:4])))
    ly.mean <- as.data.frame(do.call("rbind", ly.mean))
    ly.mean <- cbind(Month = rownames(ly.mean), ly.mean)
    
    my.data <- merge(my.data, ly.mean, by = 'Month')
    my.data
    

    使用sapplysplit

    my.data <- read.table(text = '
         Name     Month  Rate1     Rate2
         Aira       1      12        23
         Aira       2      18        73
         Aira       3      19        45
         Ben        1      53        19
         Ben        2      22        87
         Ben        3      19        45
         Cat        1      22        87
         Cat        2      67        43
         Cat        3      45        32
    ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA')
    my.data
    
    sy.mean <- t(sapply(split(my.data, my.data$Month), function(x) colMeans(x[,3:4])))
    colnames(sy.mean) <- c('Rate1.mean', 'Rate2.mean')
    sy.mean <- data.frame(Month = rownames(sy.mean), sy.mean, stringsAsFactors = FALSE)
    my.data <- merge(my.data, sy.mean, by = 'Month')
    my.data
    

    使用aggregate

    my.data <- read.table(text = '
         Name     Month  Rate1     Rate2
         Aira       1      12        23
         Aira       2      18        73
         Aira       3      19        45
         Ben        1      53        19
         Ben        2      22        87
         Ben        3      19        45
         Cat        1      22        87
         Cat        2      67        43
         Cat        3      45        32
    ', header = TRUE, stringsAsFactors = FALSE, na.strings = 'NA')
    
    my.summary <- with(my.data, aggregate(list(Rate1, Rate2), by = list(Month), 
                       FUN = function(x) { mon.mean = mean(x, na.rm = TRUE) } ))
    
    my.summary <- do.call(data.frame, my.summary)
    colnames(my.summary) <- c('Month', 'Rate1.mean', 'Rate2.mean')
    my.summary
    
    my.data <- merge(my.data, my.summary, by = 'Month')
    my.data
    

    编辑:2020 年 6 月 28 日

    这里我使用aggregate 按组获取整个matrix 的列means,其中组在外部vector 中定义:

    my.group <- c(1,2,1,2,2,3,1,2,3,3)
    
    my.data <- matrix(c(   1,    2,    3,    4,    5,
                          10,   20,   30,   40,   50,
                           2,    4,    6,    8,   10,
                          20,   30,   40,   50,   60,
                          20,   18,   16,   14,   12,
                        1000, 1100, 1200, 1300, 1400,
                           2,    3,    4,    3,    2,
                          50,   40,   30,   20,   10,
                        1001, 2001, 3001, 4001, 5001,
                        1000, 2000, 3000, 4000, 5000), nrow = 10, ncol = 5, byrow = TRUE)
    my.data
    
    my.summary <- aggregate(list(my.data), by = list(my.group), FUN = function(x) { my.mean = mean(x, na.rm = TRUE) } )
    my.summary
    #  Group.1          X1       X2          X3       X4          X5
    #1       1    1.666667    3.000    4.333333    5.000    5.666667
    #2       2   25.000000   27.000   29.000000   31.000   33.000000
    #3       3 1000.333333 1700.333 2400.333333 3100.333 3800.333333
    

    【讨论】:

      【解决方案6】:

      我描述了两种方法,一种基于 data.table,另一种基于 reshape2 包。 data.table 的方式已经有了答案,不过我已经尽力让它更简洁更详细了。

      数据是这样的:

       d <- structure(list(Name = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 3L, 
      3L, 3L), .Label = c("Aira", "Ben", "Cat"), class = "factor"), 
          Month = c(1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L), Rate1 = c(12L, 
          18L, 19L, 53L, 22L, 19L, 22L, 67L, 45L), Rate2 = c(23L, 73L, 
          45L, 19L, 87L, 45L, 87L, 43L, 32L)), .Names = c("Name", "Month", 
      "Rate1", "Rate2"), class = "data.frame", row.names = c(NA, -9L
      ))
      head(d)
        Name Month Rate1 Rate2
      1 Aira     1    12    23
      2 Aira     2    18    73
      3 Aira     3    19    45
      4  Ben     1    53    19
      5  Ben     2    22    87
      6  Ben     3    19    45
      
      
      library("reshape2")
      mym <- melt(d, id = c("Name"))
      res <- dcast(mym, Name ~ variable, mean)
      res
      #Name Month    Rate1    Rate2
      #1 Aira     2 16.33333 47.00000
      #2  Ben     2 31.33333 50.33333
      #3  Cat     2 44.66667 54.00000
      

      使用data.table:

      # At first, I convert the data.frame to data.table and then I group it 
      setDT(d)
      d[, .(Rate1 = mean(Rate1), Rate2 = mean(Rate2)), by = .(Name)]
      #   Name    Rate1    Rate2
      #1: Aira 16.33333 47.00000
      #2:  Ben 31.33333 50.33333
      #3:  Cat 44.66667 54.00000
      

      还有另一种方法是避免使用 .SD 在 data.table 中为 j 写入许多参数

      d[, lapply(.SD, mean), by = .(Name)]
      #   Name Month    Rate1    Rate2
      #1: Aira     2 16.33333 47.00000
      #2:  Ben     2 31.33333 50.33333
      #3:  Cat     2 44.66667 54.00000
      

      如果我们只想拥有 Rate1 和 Rate2,那么我们可以使用 .SDcols,如下所示:

      d[, lapply(.SD, mean), by = .(Name), .SDcols = 3:4]
      #  Name    Rate1    Rate2
      #1: Aira 16.33333 47.00000
      #2:  Ben 31.33333 50.33333
      #3:  Cat 44.66667 54.00000
      

      【讨论】:

      【解决方案7】:

      您也可以使用sqldf 包完成此操作,如下所示:

      library(sqldf)
      
      x <- read.table(text='Name     Month  Rate1     Rate2
      Aira       1      12        23
                      Aira       2      18        73
                      Aira       3      19        45
                      Ben        1      53        19
                      Ben        2      22        87
                      Ben        3      19        45
                      Cat        1      22        87
                      Cat        2      67        43
                      Cat        3      45        32', header=TRUE)
      
      sqldf("
      select 
        Name
        ,avg(Rate1) as Rate1_float
        ,avg(Rate2) as Rate2_float
        ,avg(Rate1) as Rate1
        ,avg(Rate2) as Rate2
      from x
      group by 
        Name
      ")
      
      #  Name Rate1_float Rate2_float Rate1 Rate2
      #1 Aira    16.33333    47.00000    16    47
      #2  Ben    31.33333    50.33333    31    50
      #3  Cat    44.66667    54.00000    44    54
      

      我最近转换为dplyr,如其他答案所示,但sqldf 很好,因为大多数数据分析师/数据科学家/开发人员至少对 SQL 有一定的了解。通过这种方式,我认为它往往比dplyr 或上面介绍的其他解决方案更易于阅读。

      更新:在回复下面的评论时,我尝试更新如上所示的代码。但是,行为并不像我预期的那样。似乎只有当列别名与原始列名匹配时才会执行列定义(即int vs float)。当您指定新名称时,将返回聚合列而不进行四舍五入。

      【讨论】:

      • 你能评论一下四舍五入吗?
      • 嗨@partickt(很抱歉耽搁了),这是此处描述的 SQL 的细微差别:stackoverflow.com/questions/18493976/…。如您所见,由于 SQL 以 int 开头,因此将其保留为 intavg(cast(Ratei as float)) 应该得到一个十进制值,可以根据需要用 round 换行。 avg(Ratei*1.0) 也应该强制转换为 float...也没有运行
      【解决方案8】:

      您也可以使用通用函数 cbind()lm() 而不使用拦截:

      cbind(lm(d$Rate1~-1+d$Name)$coef,lm(d$Rate2~-1+d$Name)$coef)
      >               [,1]     [,2]
      >d$NameAira 16.33333 47.00000
      >d$NameBen  31.33333 50.33333
      >d$NameCat  44.66667 54.00000
      

      【讨论】:

        猜你喜欢
        • 2022-06-23
        相关资源
        最近更新 更多