混合数据帧的总和取决于R中的多个条件

时间:2013-02-23 18:59:40

标签: r dataframe

这是对我之前question的更复杂的跟进。答案是使用矩阵,但这不适用于具有不同模式值的数据帧。

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

条件

  1. 仅针对具有匹配“名称” - 值
  2. 的行计算总和
  3. 仅计算匹配列名称的总和
  4. 如果df4中的单元格不是0且不是NA,则总和应为df3 + df4
  5. 否则总和应为df1 + df2 + df3
  6. 实施例

    > 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. 分开
    4. 将“X1”...“X4”列转换为矩阵
    5. 计算矩阵的总和,如我对其他问题的回答,附加条件1
    6. 将结果矩阵转换为数据框
    7. 使用结果数据框
    8. 绑定“名称”列

      如何在R?

      中完成

      解决方案

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

      在四个addCols()中添加, padValue=NA)

      在回答here时,将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")
      

1 个答案:

答案 0 :(得分: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)
}