为列表

时间:2018-03-16 19:05:28

标签: r sum lapply coercion

我有一个包含多个数据框的列表。示例数据:

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

除“名称”之外的所有列都只包含1,0和NA,因此我认为可能它们被读作因子而不是数字。我第一次尝试强制数字(看起来像下面的函数但没有“unlist”)导致错误(对象类型列表不能被强制输入'double')所以我根据{{3中的答案尝试了以下内容}}

ls <- lapply(ls, function(x) {
  x[,-1] <- as.numeric(unlist(x[,-1]))
})

但这只是给我一个数字字符串列表,而不是我想要的数据帧列表。任何有关修复原始colSums功能或成功将数据转换为数字的建议都将非常感谢!

5 个答案:

答案 0 :(得分:5)

你非常接近!您当前的函数仅返回最后一行,因为默认情况下函数会返回最后一行中的任何对象。所以你需要类似下面这样的东西。 as.character是因为字符串是作为因素输入的,这不允许您以正确的方式将"Sum"放入框架中。

一般情况下,除非这是某种输出存储汇总统计信息作为表中的一行而不是一个非常整洁的做法,因为有些行包含数据而其他行不包含数据会让人感到困惑。

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。

答案 1 :(得分:1)

为了完整起见,这里也是一个data.table解决方案。将字符值添加到因子列时,data.table更容忍。不需要显式类型转换。

此外,我想建议替换&#34; data.frames&#34;。

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

答案 2 :(得分:0)

另一种选择可以是rbindMap

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)

答案 3 :(得分:0)

lapply(ls,function(i) 
data.frame(rbind(apply(i,2,as.vector),c("Sum",colSums(i[,-1],na.rm = TRUE) ))))

答案 4 :(得分:0)

您可以使用IsRefreshing=”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