将行添加到数据框架中,每列的总和为

我有一个数据框架,我想在其中添加一个额外的行,以累计每列的值。例如,假设我有这样的数据:

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 列上。

还有什么更好的办法吗?

114226 次浏览

您的数据中是需要 Language 列,还是将该列看作 row.names更合适?这会将 data.frame 从4个对3个变量的观察变为4个对2个变量的观察(Files & 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

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

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

郑重声明,如果你不是绝对需要 Language专栏,我更喜欢 Chase 的回答。

如果(1)我们不需要第一列上的 "Language"标题,那么我们可以使用行名来表示它; 如果(2)可以将最后一行标记为 "Sum"而不是 "Total",那么我们可以像这样使用 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

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

  • 第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行现在是错误的!

就我个人而言,我会计算出列和并将它们存储在一个单独的向量中。

如果你强迫你的专栏使用数字,你最初的直觉会起作用:

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

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

由于您提到这是导出显示之前的最后一步,为了清楚起见,您可能需要在列名中包含空格(即“ Grand Total”)。如果是这样,下面将确保创建的 data.frame 将绑定到原始数据集,而不会因列名不匹配而导致错误:

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


colnames(dfTotals) <- names(x)


rbind(x, dfTotals)

试试这个

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

请参阅看门人软件包中的 adorn_totals():

library(janitor)
x %>%
adorn_totals("row")


#>  Language Files   LOC
#>       C++  4009 15328
#>      Java   210   876
#>    Python    35   200
#>     Total  4254 16404

数值列保留为类 numeric。

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

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")))

试试这个

library(tibble)
x %>% add_row( Language="Total",Files = sum(.$Files),LOC = sum(.$LOC) )
df %>% bind_rows(purrr::map_dbl(.,sum))

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

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