4

这是我之前的问题的更复杂的后续。答案是使用矩阵,但这不适用于具有不同模式值的数据帧。

我想将不同大小的数据框与字符列和整数列结合起来,并根据多个条件计算它们的总和。

条件

  1. 仅针对具有匹配“名称”值的行计算总和
  2. 仅针对匹配的列名计算总和
  3. 如果一个单元格df4不是 0 也不是 NA,那么总和应该是df3 + df4
  4. 否则总和应该是df1 + df2 + df3

例子

> df1 <- data.frame(Name=c("Joe","Ann","Lee","Dan"), "1"=c(0,1,5,2), "2"=c(3,1,0,0), "3"=c(2,0,2,2), "4"=c(2,1,3,4))
> df1
  Name X1 X2 X3 X4
1  Joe  0  3  2  2
2  Ann  1  1  0  1
3  Lee  5  0  2  3
4  Dan  2  0  2  4

> df2 <- data.frame(Name=c("Joe","Ann","Ken"), "1"=c(3,4,1), "2"=c(2,3,0), "3"=c(2,4,3))
> df2
  Name X1 X2 X3
1  Joe  3  2  2
2  Ann  4  3  4
3  Ken  1  0  3

> df3 <- data.frame(Name=c("Lee","Ben"), "1"=c(1,3), "2"=c(3,4), "3"=c(4,3))
> df3
  Name X1 X2 X3
1  Lee  1  3  4
2  Ben  3  4  3

条件取决于此框架:

> df4 <- data.frame(Name=c("Lee","Ann","Dan"), "1"=c(6,0,NA), "2"=c(0,0,4), "3"=c(0,NA,0))
> df4
   Name  X1  X2  X3
1   Lee   6   0   0
2   Ann   0   0  NA 
3   Dan  NA   4   0

对于上述示例,这是预期的结果(* 值取决于 df4):

> dfsum
  Name  X1  X2  X3  X4
1  Joe   3   5   4   2
2  Ann   5   4   4   1
3  Lee   7*  3   6   3
4  Dan   2   4*  2   4
5  Ken   1   0   3  NA
6  Ben   3   4   3  NA

可能的步骤?

首先将 df1、df2、df3、df4 扩展为 5 列 6 行,用 NA 填充缺失的数据。

然后对于每个数据帧:

  1. 按“名称”对行进行排序
  2. 将“名称”列与“X1”...“X4”分开
  3. 将“X1”...“X4”列转换为矩阵
  4. 计算矩阵的总和,就像我的另一个问题的答案一样,附加条件 1
  5. 将结果矩阵转换为数据框
  6. cbind“名称”列与结果数据框

如何在 R 中做到这一点?


解决方案

@Ricardo Saporta 的解决方案几乎没有什么变化:

添加, padValue=NA)四个 addCols()。

如此处所回答,将 sumD3D4 和dtsum的定义替换为:

plus <- function(x) {
  if(all(is.na(x))){
    c(x[0],NA)} else {
      sum(x,na.rm = TRUE)}
}

sumD3D4  <- setkey(rbind(dt3, dt4)[,lapply(.SD, plus), by = Name], "Name")
dtsum <- setkey(rbind(dt1, dt2, dt3)[, lapply(.SD, plus), by=Name], "Name")
4

1 回答 1

3

如果您使用 data.table 而不是 data.frame,您可以使用它的by=xxxx功能,按名称添加。下面的代码应该会给你预期的结果。

请注意,我正在用额外的空列填充 data.tables。但是,我们condTrue在此之前进行计算。

library(data.table)
dt1 <- data.table(df1)
dt2 <- data.table(df2)
dt3 <- data.table(df3)
dt4 <- data.table(df4)

# make sure all dt's have the same columns 
#-----------------------------------------#

# identify which dt4 satisfy the condition 
condTrue <- as.data.table(which(!(is.na(dt4) | dt4==0), arr.ind=TRUE))

# ignore column "Name" from dt4
condTrue <- condTrue[col>1]

# convert from (row, col) index to ("Name", columnName) 
condTrue <- data.table(Name=dt4[condTrue$row, Name], colm=names(dt4)[condTrue$col], key="Name")


# First make a list of all the unique column names
allColumnNames <- unique(c(names(dt1), names(dt2), names(dt3), names(dt4)))

# add columns as necessary, using addCols (definted below)
addCols(dt1, allColumnNames)
addCols(dt2, allColumnNames)
addCols(dt3, allColumnNames)
addCols(dt4, allColumnNames)


sumD3D4  <- setkey(rbind(dt3, dt4)[, lapply(.SD, sum), by=Name], "Name")
dtsum    <- setkey(rbind(dt1, dt2, dt3)[, lapply(.SD, sum), by=Name], "Name")

for (Nam in condTrue$Name) {
  colsRepl <- condTrue[.(Nam)]$colm
  valsRepl <- unlist(sumD3D4[.(Nam), c(colsRepl), with=FALSE])
  dtsum[.(Nam), c(colsRepl) :=  as.list(valsRepl)]
}

dtsum
#    Name 1 2 3 4
# 1:  Ann 5 4 4 1
# 2:  Ben 3 4 3 0
# 3:  Dan 2 4 2 4
# 4:  Joe 3 5 4 2
# 5:  Ken 1 0 3 0
# 6:  Lee 7 3 6 3

addCols <- function(x, cols, padValue=0)  {
  # adds to x any columns that are in cols but not in x
  # Returns TRUE  if columns were added
  #         FALSE if no columns added 
  colsMissing <- setdiff(cols, names(x))

  # grab the actual DT name that was passed to function
  dtName <- as.character(match.call()[2])

  if (length(colsMissing)) {
    get(dtName, envir=parent.frame(1))[, c(colsMissing) := padValue]  
    return(TRUE)
  }

  return(FALSE)
}
于 2013-02-23T21:09:55.900 回答