为列表中的每个数据框创建一个包含列总和的新行
Create a new row containing column sums for every data frame in a list
我有一个包含多个数据框的列表。示例数据:
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1))
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0))
ls <- list(df1, df2)
对于每个数据框,我想在底部创建一个包含每列总和的新行。所以对于 df1 是这样的:
Name E1 E2
"A" 0 1
"B" NA 0
"C" 1 1
Sum 1 2
这是我试过的:
ls <- lapply(ls, function(x) {
x[nrow(x)+1, -1] <- colSums(x[,-1], na.rm=TRUE)
})
我收到以下错误消息:
Error in colSums(x[,-1], na.rm = TRUE) : 'x' must be numeric
除了 "Names" 之外,我的所有专栏都只包含 1、0 和 NA,所以我认为它们可能被解读为因子而不是数字。我第一次尝试强制转换为数字(看起来像下面的函数但没有 "unlist")导致错误(对象类型列表不能强制输入 'double')所以我根据答案尝试了以下操作在 this other post:
ls <- lapply(ls, function(x) {
x[,-1] <- as.numeric(unlist(x[,-1]))
})
但这只是给了我一个数字字符串列表,而不是我想要的数据框列表。非常感谢任何有关修复我的原始 colSums
函数或成功将我的数据转换为数字的建议!
你很亲近!您当前的功能仅 return 最后一行,因为默认情况下 return 最后一行上的任何对象都会起作用。所以你需要像下面这样的东西。 as.character
是因为字符串是作为因子输入的,这不会让你以正确的方式将 "Sum"
放入框架中。
但一般来说,除非这是为了某种输出,将摘要统计信息存储为 table 中的一行,否则这不是一个非常整洁的做法,因为有些行包含数据而其他行不包含数据会变得混乱.
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1))
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0))
ls <- list(df1, df2)
lapply(ls, function(x) {
x[nrow(x)+1, -1] <- colSums(x[,-1], na.rm=TRUE)
x[, 1] <- as.character(x[, 1])
x[nrow(x), 1] <- "Sum"
return(x)
})
#> [[1]]
#> Name E1 E2
#> 1 A 0 1
#> 2 B NA 0
#> 3 C 1 1
#> 4 Sum 1 2
#>
#> [[2]]
#> Name E1 E2
#> 1 A 1 0
#> 2 C 0 0
#> 3 F 1 0
#> 4 Sum 2 0
由 reprex package (v0.2.0) 创建于 2018-03-16。
另一种选择是使用 rbind
和 Map
作为:
Map(rbind, ls, lapply(ls,
function(x)sapply(x,
function(x)if(class(x) == "character"){ "Sum:" }else{ sum(x, na.rm = TRUE)})))
# [[1]]
# Name E1 E2
# 1 A 0 1
# 2 B <NA> 0
# 3 C 1 1
# 4 Sum: 1 2
#
# [[2]]
# Name E1 E2
# 1 A 1 0
# 2 C 0 0
# 3 F 1 0
# 4 Sum: 2 0
数据
注意:Name
列已更改为上述解决方案的“字符”。
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1),
stringsAsFactors = FALSE)
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0),
stringsAsFactors = FALSE)
ls <- list(df1, df2)
lapply(ls,function(i)
data.frame(rbind(apply(i,2,as.vector),c("Sum",colSums(i[,-1],na.rm = TRUE) ))))
您可以使用 rbind
:
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1), stringsAsFactors = FALSE)
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0), stringsAsFactors = FALSE)
ls <- list(df1, df2)
ls <- lapply(ls, function(x) {
x <- rbind(x, c(
"Sum",
sum(x[, "E1"], na.rm = TRUE),
sum(x[, "E2"], na.rm = TRUE)))
})
ls
产生
[[1]]
Name E1 E2
1 A 0 1
2 B <NA> 0
3 C 1 1
4 Sum 1 2
[[2]]
Name E1 E2
1 A 1 0
2 C 0 0
3 F 1 0
4 Sum 2 0
为了完整起见,这里也提供一个data.table
的解决方案。 data.table
在将字符值添加到因子列时容忍度更高。不需要显式类型转换。
此外,我想推荐一个替代“data.frames列表”的方法。
library(data.table)
lapply(ls, function(x) rbind(setDT(x),
x[, c(.(Name = "sum"), lapply(.SD, sum, na.rm = TRUE)), .SDcols = c("E1", "E2")]
))
Name E1 E2
1: A 0 1
2: B NA 0
3: C 1 1
4: sum 1 2
[[2]]
Name E1 E2
1: A 1 0
2: C 0 0
3: F 1 0
4: sum 2 0
Name
列仍然是因子,但有一个额外的因子水平,可以通过对结果应用 str()
看出:
List of 2
$ :Classes ‘data.table’ and 'data.frame': 4 obs. of 3 variables:
..$ Name: Factor w/ 4 levels "A","B","C","sum": 1 2 3 4
..$ E1 : num [1:4] 0 NA 1 1
..$ E2 : num [1:4] 1 0 1 2
..- attr(*, ".internal.selfref")=<externalptr>
$ :Classes ‘data.table’ and 'data.frame': 4 obs. of 3 variables:
..$ Name: Factor w/ 4 levels "A","C","F","sum": 1 2 3 4
..$ E1 : num [1:4] 1 0 1 2
..$ E2 : num [1:4] 0 0 0 0
..- attr(*, ".internal.selfref")=<externalptr>
替代 data.frames
列表
如果列表中的data.frames都具有相同的结构,即相同的列数、类型和名称,那么我更愿意将数据存储在一个对象中:
library(data.table)
DT <- rbindlist(ls, idcol = "df.id")
DT
df.id Name E1 E2
1: 1 A 0 1
2: 1 B NA 0
3: 1 C 1 1
4: 2 A 1 0
5: 2 C 0 0
6: 2 F 1 0
每行的来源由 df.id
中的数字标识。现在,我们可以使用分组而不是循环遍历列表的元素,例如,
DT[, lapply(.SD, sum, na.rm = TRUE), .SDcols = c("E1", "E2"), by = df.id]
df.id E1 E2
1: 1 1 2
2: 2 2 0
或者,如果要将 sum
行散布在原始数据中:
rbind(
DT,
DT[, c(.(Name = "sum"), lapply(.SD, sum, na.rm = TRUE)), .SDcols = c("E1", "E2"), by = df.id]
)[order(df.id)]
df.id Name E1 E2
1: 1 A 0 1
2: 1 B NA 0
3: 1 C 1 1
4: 1 sum 1 2
5: 2 A 1 0
6: 2 C 0 0
7: 2 F 1 0
8: 2 sum 2 0
我有一个包含多个数据框的列表。示例数据:
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1))
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0))
ls <- list(df1, df2)
对于每个数据框,我想在底部创建一个包含每列总和的新行。所以对于 df1 是这样的:
Name E1 E2
"A" 0 1
"B" NA 0
"C" 1 1
Sum 1 2
这是我试过的:
ls <- lapply(ls, function(x) {
x[nrow(x)+1, -1] <- colSums(x[,-1], na.rm=TRUE)
})
我收到以下错误消息:
Error in colSums(x[,-1], na.rm = TRUE) : 'x' must be numeric
除了 "Names" 之外,我的所有专栏都只包含 1、0 和 NA,所以我认为它们可能被解读为因子而不是数字。我第一次尝试强制转换为数字(看起来像下面的函数但没有 "unlist")导致错误(对象类型列表不能强制输入 'double')所以我根据答案尝试了以下操作在 this other post:
ls <- lapply(ls, function(x) {
x[,-1] <- as.numeric(unlist(x[,-1]))
})
但这只是给了我一个数字字符串列表,而不是我想要的数据框列表。非常感谢任何有关修复我的原始 colSums
函数或成功将我的数据转换为数字的建议!
你很亲近!您当前的功能仅 return 最后一行,因为默认情况下 return 最后一行上的任何对象都会起作用。所以你需要像下面这样的东西。 as.character
是因为字符串是作为因子输入的,这不会让你以正确的方式将 "Sum"
放入框架中。
但一般来说,除非这是为了某种输出,将摘要统计信息存储为 table 中的一行,否则这不是一个非常整洁的做法,因为有些行包含数据而其他行不包含数据会变得混乱.
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1))
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0))
ls <- list(df1, df2)
lapply(ls, function(x) {
x[nrow(x)+1, -1] <- colSums(x[,-1], na.rm=TRUE)
x[, 1] <- as.character(x[, 1])
x[nrow(x), 1] <- "Sum"
return(x)
})
#> [[1]]
#> Name E1 E2
#> 1 A 0 1
#> 2 B NA 0
#> 3 C 1 1
#> 4 Sum 1 2
#>
#> [[2]]
#> Name E1 E2
#> 1 A 1 0
#> 2 C 0 0
#> 3 F 1 0
#> 4 Sum 2 0
由 reprex package (v0.2.0) 创建于 2018-03-16。
另一种选择是使用 rbind
和 Map
作为:
Map(rbind, ls, lapply(ls,
function(x)sapply(x,
function(x)if(class(x) == "character"){ "Sum:" }else{ sum(x, na.rm = TRUE)})))
# [[1]]
# Name E1 E2
# 1 A 0 1
# 2 B <NA> 0
# 3 C 1 1
# 4 Sum: 1 2
#
# [[2]]
# Name E1 E2
# 1 A 1 0
# 2 C 0 0
# 3 F 1 0
# 4 Sum: 2 0
数据
注意:Name
列已更改为上述解决方案的“字符”。
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1),
stringsAsFactors = FALSE)
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0),
stringsAsFactors = FALSE)
ls <- list(df1, df2)
lapply(ls,function(i)
data.frame(rbind(apply(i,2,as.vector),c("Sum",colSums(i[,-1],na.rm = TRUE) ))))
您可以使用 rbind
:
df1 <- data.frame(Name=c("A", "B", "C"), E1=c(0, NA, 1), E2=c(1, 0, 1), stringsAsFactors = FALSE)
df2 <- data.frame(Name=c("A", "C", "F"), E1=c(1, 0, 1), E2=c(0, 0, 0), stringsAsFactors = FALSE)
ls <- list(df1, df2)
ls <- lapply(ls, function(x) {
x <- rbind(x, c(
"Sum",
sum(x[, "E1"], na.rm = TRUE),
sum(x[, "E2"], na.rm = TRUE)))
})
ls
产生
[[1]]
Name E1 E2
1 A 0 1
2 B <NA> 0
3 C 1 1
4 Sum 1 2
[[2]]
Name E1 E2
1 A 1 0
2 C 0 0
3 F 1 0
4 Sum 2 0
为了完整起见,这里也提供一个data.table
的解决方案。 data.table
在将字符值添加到因子列时容忍度更高。不需要显式类型转换。
此外,我想推荐一个替代“data.frames列表”的方法。
library(data.table)
lapply(ls, function(x) rbind(setDT(x),
x[, c(.(Name = "sum"), lapply(.SD, sum, na.rm = TRUE)), .SDcols = c("E1", "E2")]
))
Name E1 E2 1: A 0 1 2: B NA 0 3: C 1 1 4: sum 1 2 [[2]] Name E1 E2 1: A 1 0 2: C 0 0 3: F 1 0 4: sum 2 0
Name
列仍然是因子,但有一个额外的因子水平,可以通过对结果应用 str()
看出:
List of 2 $ :Classes ‘data.table’ and 'data.frame': 4 obs. of 3 variables: ..$ Name: Factor w/ 4 levels "A","B","C","sum": 1 2 3 4 ..$ E1 : num [1:4] 0 NA 1 1 ..$ E2 : num [1:4] 1 0 1 2 ..- attr(*, ".internal.selfref")=<externalptr> $ :Classes ‘data.table’ and 'data.frame': 4 obs. of 3 variables: ..$ Name: Factor w/ 4 levels "A","C","F","sum": 1 2 3 4 ..$ E1 : num [1:4] 1 0 1 2 ..$ E2 : num [1:4] 0 0 0 0 ..- attr(*, ".internal.selfref")=<externalptr>
替代 data.frames
列表如果列表中的data.frames都具有相同的结构,即相同的列数、类型和名称,那么我更愿意将数据存储在一个对象中:
library(data.table)
DT <- rbindlist(ls, idcol = "df.id")
DT
df.id Name E1 E2 1: 1 A 0 1 2: 1 B NA 0 3: 1 C 1 1 4: 2 A 1 0 5: 2 C 0 0 6: 2 F 1 0
每行的来源由 df.id
中的数字标识。现在,我们可以使用分组而不是循环遍历列表的元素,例如,
DT[, lapply(.SD, sum, na.rm = TRUE), .SDcols = c("E1", "E2"), by = df.id]
df.id E1 E2 1: 1 1 2 2: 2 2 0
或者,如果要将 sum
行散布在原始数据中:
rbind(
DT,
DT[, c(.(Name = "sum"), lapply(.SD, sum, na.rm = TRUE)), .SDcols = c("E1", "E2"), by = df.id]
)[order(df.id)]
df.id Name E1 E2 1: 1 A 0 1 2: 1 B NA 0 3: 1 C 1 1 4: 1 sum 1 2 5: 2 A 1 0 6: 2 C 0 0 7: 2 F 1 0 8: 2 sum 2 0