【问题标题】:Add row to a data frame with total sum for each column将行添加到数据框中,每列的总和
【发布时间】:2011-06-24 05:31:00
【问题描述】:

我有一个数据框,我想在其中添加一个额外的行来汇总每一列的值。例如,假设我有这些数据:

x <- data.frame(Language=c("C++", "Java", "Python"), 
                Files=c(4009, 210, 35), 
                LOC=c(15328,876, 200), 
                stringsAsFactors=FALSE)    

数据如下所示:

  Language Files   LOC
1      C++  4009 15328
2     Java   210   876
3   Python    35   200

我的直觉是这样做:

y <- rbind(x, c("Total", colSums(x[,2:3])))

这有效,它计算总数:

> y
  Language Files   LOC
1      C++  4009 15328
2     Java   210   876
3   Python    35   200
4    Total  4254 16404

问题是 Files 和 LOC 列都被转换成字符串了:

> y$LOC
[1] "15328" "876"   "200"   "16404"

我知道发生这种情况是因为我创建了一个向量c("Total", colSums(x[,2:3]),其输入既是数字又是字符串,并且它将所有元素转换为通用类型,以便所有向量元素都相同。然后同样的事情发生在 Files 和 LOC 列上。

有什么更好的方法来做到这一点?

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    您的数据中是否需要 Language 列,还是将该列视为 row.names 更合适?这会将您的 data.frame 从 3 个变量的 4 个观察值更改为 2 个变量的 4 个观察值(文件和 LOC)。

    x <- data.frame(Files = c(4009, 210, 35), LOC = c(15328,876, 200),
                    row.names = c("C++", "Java", "Python"), stringsAsFactors = FALSE)    
    x["Total" ,] <- colSums(x)
    
    
    > x
           Files   LOC
    C++     4009 15328
    Java     210   876
    Python    35   200
    Total   4254 16404
    

    【讨论】:

    • 就个人而言,我不建议将数据存储在行名中——这就是变量的用途!
    • 总的来说,我同意。我也倾向于遵循@csgillespie 的建议,即不要在同一个对象中混合原始数据和汇总统计数据。然而,正如 OP 指出的那样,在这种情况下这并不是一个真正的问题,因为问题是围绕数据的呈现展开,而不是任何进一步的分析。
    • tidyverse 等价物是什么?
    【解决方案2】:

    这里有一种方法可以让你得到你想要的,但很可能有一个更优雅的解决方案。

    rbind(x, data.frame(Language = "Total", t(colSums(x[, -1]))))
    

    作为记录,如果您不是绝对需要 Language 列,我更喜欢 Chase 的回答。

    【讨论】:

      【解决方案3】:

      如果 (1) 我们不需要第一列上的 "Language" 标题,那么我们可以使用行名来表示它,如果 (2) 可以将最后一行标记为 "Sum" 而不是 @987654323 @ 然后我们可以像这样使用addmargins

      rownames(x) <- x$Language
      addmargins(as.table(as.matrix(x[-1])), 1)
      

      给予:

             Files   LOC
      C++     4009 15328
      Java     210   876
      Python    35   200
      Sum     4254 16404
      

      如果我们确实想要标记为"Language" 的第一列和标记为"Total" 的总行,那么它会更长一点:

      rownames(x) <- x$Language
      Total <- sum
      xa <- addmargins(as.table(as.matrix(x[-1])), 1, FUN = Total)
      data.frame(Language = rownames(xa), as.matrix(xa[]), row.names = NULL)
      

      给予:

        Language Files   LOC
      1      C++  4009 15328
      2     Java   210   876
      3   Python    35   200
      4    Total  4254 16404
      

      【讨论】:

        【解决方案4】:

        您确定要在数据框中包含列总计吗?对我来说,数据框的解释现在取决于行。例如,

        • 第 1-(n-1) 行:有多少文件与特定语言相关联
        • 第 n 行:与所有语言相关联的文件数

        如果您开始对数据进行子集化,这会变得更加混乱。例如,假设您想知道哪些语言有超过 100 个文件:

        > x = data.frame(Files=c(4009, 210, 35), 
                        LOC=c(15328,876, 200), 
                        row.names=c("C++", "Java", "Python"), 
                        stringsAsFactors=FALSE)    
        > x["Total" ,] = colSums(x)
        > x[x$Files > 100,]
               Files   LOC
        C++    4009 15328
        Java    210   876
        Total  4254 16404#But this refers to all languages!
        

        Total 行现在是错误的!

        我个人会计算列总和并将它们存储在单独的向量中。

        【讨论】:

        • 通常我不会为了分析而这样做,但这是为了演示。这是我使用 Sweave 在 LaTeX 文档中生成表格之前的最后一步。
        【解决方案5】:

        如果您将列强制为数字,您的原始直觉会起作用:

        y$LOC <- as.numeric(y$LOC)
        y$Files <- as.numeric(y$Files)
        

        然后应用 colSums() 和 rbind()。

        【讨论】:

          【解决方案6】:

          由于您提到这是导出以进行演示之前的最后一步,因此为清楚起见,您可能会在列名中包含空格(即“总计”)。如果是这样,以下将确保创建的 data.frame 将 rbind 到原始数据集,而不会因列名不匹配而导致错误:

          dfTotals <- data.frame(Language="Total",t(colSums(x[,-1]))))
          
          colnames(dfTotals) <- names(x)  
          
          rbind(x, dfTotals)
          

          【讨论】:

            【解决方案7】:

            试试这个

            y[4,] = c("Total", colSums(y[,2:3]))
            

            【讨论】:

              【解决方案8】:

              查看管理员包中的adorn_totals()

              library(janitor)
              x %>%
                adorn_totals("row")
              
              #>  Language Files   LOC
              #>       C++  4009 15328
              #>      Java   210   876
              #>    Python    35   200
              #>     Total  4254 16404
              

              数字列仍然是数字类。

              免责声明:我创建了这个包,包括 adorn_totals(),它正是为这个任务而制作的。

              【讨论】:

              • 请注意,这个(唯一的)一个问题是现在很难按行总数排序,而我通常想要这样做。 “总计”行最终位于顶部。
              【解决方案9】:

              执行此操作的tidyverse 方法是使用bind_rows(或最终add_row)和summarise 来计算总和。这里的问题是我们想要除一个之外的所有总和,所以一个技巧是:

              summarise_all(x, ~if(is.numeric(.)) sum(.) else "Total")
              

              一行:

              x %>%
                bind_rows(summarise_all(., ~if(is.numeric(.)) sum(.) else "Total"))
              

              使用 dplyr >=1.0 编辑

              也可以使用across(),在这种情况下会稍微冗长一些:

              x %>%
                bind_rows(summarise(.,
                                    across(where(is.numeric), sum),
                                    across(where(is.character), ~"Total")))
              

              【讨论】:

              • 谢谢,你是对的:我的解决方案不是必需的答案。你的答案是正确的。我投票给你并删除了我的条目。
              • 很好,我很高兴将它保存在tidyverse,为此加载另一个包似乎很愚蠢。
              • 不错的答案,如何仅对某个号码进行总和。作为其他列的列数可能不需要总和,而是平均。
              • 如果要在不同的列上使用不同的功能,恐怕需要手动运行summarise(var1=mean(var1), var2= sum(var2), var = "Total")
              • 很好,解决方案。想知道如何用across 而不是summarise_all 编写正确的代码?
              【解决方案10】:

              试试这个

              library(tibble)
              x %>% add_row( Language="Total",Files = sum(.$Files),LOC = sum(.$LOC) )
              

              【讨论】:

                【解决方案11】:
                df %>% bind_rows(purrr::map_dbl(.,sum))
                

                【讨论】:

                • 好的和优雅的解决方案,但你必须删除第一列,然后将其传递给 map_dbl。一种方法是使用 [] 运算符。 x %>% bind_rows(x[,-1] %>% map_dbl(.,sum))
                【解决方案12】:

                扩展Nicolas Ratto的答案,如果你有更多的列,你可以使用

                x %>% add_row(Language = "Total", summarise(., across(where(is.numeric), sum)))
                

                【讨论】:

                • 这个解决方案很好,但是我们在执行时不知道第一列的名称是什么?
                猜你喜欢
                • 2018-10-27
                • 1970-01-01
                • 2013-05-29
                • 2018-08-03
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多