【问题标题】:Count number of rows per group and add result to original data frame计算每组的行数并将结果添加到原始数据框
【发布时间】:2011-11-19 00:27:48
【问题描述】:

假设我有一个data.frame 对象:

df <- data.frame(name=c('black','black','black','red','red'),
                 type=c('chair','chair','sofa','sofa','plate'),
                 num=c(4,5,12,4,3))

现在我想计算nametype 的每个组合的行数(观察值)。可以这样做:

table(df[ , c("name","type")])

或者也可以使用plyr,(虽然我不确定如何)。

但是,如何将结果合并到原始数据框中?所以结果会是这样的:

df
#    name  type num count
# 1 black chair   4     2
# 2 black chair   5     2
# 3 black  sofa  12     1
# 4   red  sofa   4     1
# 5   red plate   3     1

count 现在存储聚合结果。

plyr 的解决方案也可能很有趣,但我想看看如何使用基础 R 来完成。

【问题讨论】:

    标签: r count aggregate r-faq


    【解决方案1】:

    基础 R 中的一个简单行:

    df$count = table(interaction(df[, (c("name", "type"))]))[interaction(df[, (c("name", "type"))])]
    

    为了清晰/高效,两行相同:

    fact = interaction(df[, (c("name", "type"))])
    df$count = table(fact)[fact]
    

    【讨论】:

      【解决方案2】:

      使用data.table

      library(data.table)
      dt = as.data.table(df)
      
      # or coerce to data.table by reference:
      # setDT(df)
      
      dt[ , count := .N, by = .(name, type)]
      

      对于data.table 1.8.2 之前的替代方案,请参阅编辑历史记录。


      使用dplyr

      library(dplyr)
      df %>%
        group_by(name, type) %>%
        mutate(count = n())
      

      或者简单地说:

      add_count(df, name, type)
      

      使用plyr

      plyr::ddply(df, .(name, type), transform, count = length(num))
      

      【讨论】:

      • 你需要“setkeyv(dt, c('name', 'type'))”吗?
      【解决方案3】:

      使用sqldf包:

      library(sqldf)
      
      sqldf("select a.*, b.cnt
             from df a,
                 (select name, type, count(1) as cnt
                  from df
                  group by name, type) b
            where a.name = b.name and
                  a.type = b.type")
      
      #    name  type num cnt
      # 1 black chair   4   2
      # 2 black chair   5   2
      # 3 black  sofa  12   1
      # 4   red  sofa   4   1
      # 5   red plate   3   1
      

      【讨论】:

        【解决方案4】:

        这应该做你的工作:

        df_agg <- aggregate(num~name+type,df,FUN=NROW)
        names(df_agg)[3] <- "count"
        df <- merge(df,df_agg,by=c('name','type'),all.x=TRUE)
        

        【讨论】:

          【解决方案5】:

          您距离将行数合并到基础数据集中仅一步之遥。

          使用broom 包中的tidy() 函数,将频率表转换为数据框并与df 进行内连接:

          df <- data.frame(name=c('black','black','black','red','red'),
                                   type=c('chair','chair','sofa','sofa','plate'),
                                   num=c(4,5,12,4,3))
          library(broom)
          df <- merge(df, tidy(table(df[ , c("name","type")])), by=c("name","type"))
          df
             name  type num Freq
          1 black chair   4    2
          2 black chair   5    2
          3 black  sofa  12    1
          4   red plate   3    1
          5   red  sofa   4    1
          

          【讨论】:

            【解决方案6】:

            两行替代方法是生成一个 0 的变量,然后用 split&lt;-splitlengths 填充它,如下所示:

            # generate vector of 0s
            df$count <-0L
            
            # fill it in
            split(df$count, df[c("name", "type")]) <- lengths(split(df$num, df[c("name", "type")]))
            

            这将返回所需的结果

            df
               name  type num count
            1 black chair   4     2
            2 black chair   5     2
            3 black  sofa  12     1
            4   red  sofa   4     1
            5   red plate   3     1
            

            本质上,RHS 计算每个名称类型组合的长度,返回一个长度为 6 的命名向量,其中“red.chair”和“black.plate”为 0。这通过split &lt;- 被馈送到 LHS,它接受向量并在给定的位置适当地添加值。这基本上就是ave 所做的,正如您所见,ave 的倒数第二行是

            split(x, g) <- lapply(split(x, g), FUN)
            

            不过,lengthssapply(list, length) 的优化版本。

            【讨论】:

              【解决方案7】:

              基本的R 函数aggregate 将使用单行获得计数,但将这些计数添加回原始data.frame 似乎需要一些处理。

              df <- data.frame(name=c('black','black','black','red','red'),
                               type=c('chair','chair','sofa','sofa','plate'),
                               num=c(4,5,12,4,3))
              df
              #    name  type num
              # 1 black chair   4
              # 2 black chair   5
              # 3 black  sofa  12
              # 4   red  sofa   4
              # 5   red plate   3
              
              rows.per.group  <- aggregate(rep(1, length(paste0(df$name, df$type))),
                                           by=list(df$name, df$type), sum)
              rows.per.group
              #   Group.1 Group.2 x
              # 1   black   chair 2
              # 2     red   plate 1
              # 3   black    sofa 1
              # 4     red    sofa 1
              
              my.summary <- do.call(data.frame, rows.per.group)
              colnames(my.summary) <- c(colnames(df)[1:2], 'rows.per.group')
              my.data <- merge(df, my.summary, by = c(colnames(df)[1:2]))
              my.data
              #    name  type num rows.per.group
              # 1 black chair   4              2
              # 2 black chair   5              2
              # 3 black  sofa  12              1
              # 4   red plate   3              1
              # 5   red  sofa   4              1
              

              【讨论】:

                【解决方案8】:

                另一种概括更多的方式:

                df$count <- unsplit(lapply(split(df, df[c("name","type")]), nrow), df[c("name","type")])
                

                【讨论】:

                • 请解释一下这如何概括更多?
                【解决方案9】:

                你可以这样做:

                > ddply(df,.(name,type),transform,count = NROW(piece))
                   name  type num count
                1 black chair   4     2
                2 black chair   5     2
                3 black  sofa  12     1
                4   red plate   3     1
                5   red  sofa   4     1
                

                或许更直观,

                > ddply(df,.(name,type),transform,count = length(num))
                   name  type num count
                1 black chair   4     2
                2 black chair   5     2
                3 black  sofa  12     1
                4   red plate   3     1
                5   red  sofa   4     1
                

                【讨论】:

                  【解决方案10】:

                  你可以使用ave:

                  df$count <- ave(df$num, df[,c("name","type")], FUN=length)
                  

                  【讨论】:

                  • 也可以使用transform(df, count = ave(num, name, type, FUN = length))with 来做的更简洁
                  • 如果你有大量数据,这个命令是SUPERSLOW
                  猜你喜欢
                  • 2021-01-22
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-01-25
                  • 2018-08-03
                  • 2020-03-22
                  • 1970-01-01
                  • 2019-10-23
                  相关资源
                  最近更新 更多