【问题标题】:Add (not merge!) two data frames with unequal rows and columns添加(不合并!)两个具有不相等行和列的数据框
【发布时间】:2016-05-11 19:41:12
【问题描述】:

我想有效地将​​两个数据框的条目相加,尽管不能保证数据框具有相同的维度或列名。合并并不是我真正想要的。相反,我想创建一个输出对象,其中包含属于任一添加的数据框的所有行名和列名。在该输出的每个位置,我想对计算值使用以下逻辑:

  • 如果行/列配对属于两个输入数据帧,我希望输出包含它们的总和
  • 如果一对行/列只属于一个输入数据框,我想在输出中包含该值
  • 如果行/列配对不属于任何输入矩阵,我希望在输出中的该位置具有 0。

例如,考虑以下输入数据帧:

df1 = data.frame(x = c(1,2,3), y = c(4,5,6))
rownames(df1) = c("a", "b", "c")
df2 = data.frame(x = c(7,8), z = c(9,10), w = c(2, 3))
rownames(df2) = c("a", "d")
> df1
  x y
a 1 4
b 2 5
c 3 6
> df2
  x  z  w 
a 7  9  2
d 8 10  3

我希望最终结果是

> df2
   x  y   z  w
a  8  4   9  2
b  2  5   0  0
c  3  6   0  0
d  8  0  10  3

到目前为止我做了什么 -

dplyr 中的

bind_rows / bind_cols 可以抛出以下内容: “错误:行数不兼容(3,预期为 2)”

我有重复的列名,所以“合并”也不适用于我的目的 - 由于某种原因返回一个空的 df。

【问题讨论】:

    标签: r dataframe


    【解决方案1】:

    似乎您可以合并行名,然后通过一些额外的修改来处理 NA 的总和和转换为零:

    library(dplyr)
    
    df.new = df1 %>% add_rownames %>%
      full_join(df2 %>% add_rownames, by="rowname") %>%
      mutate_each(funs(replace(., which(is.na(.)), 0))) %>%
      mutate(x = x.x + x.y) %>%
      select(rowname,x,y,z,w)
    

    或者,使用@DavidArenburg 更优雅和可扩展的解决方案:

    df.new = df1 %>% add_rownames %>% 
      full_join(df2 %>% add_rownames) %>% 
      group_by(rowname) %>% 
      summarise_each(funs(sum(., na.rm = TRUE)))
    
    df.new
    
      rowname     x     y     z     w
    1       a     8     4     9     2
    2       b     2     5     0     0
    3       c     3     6     0     0
    4       d     8     0    10     3
    

    【讨论】:

    • 好吧,@DavidArenburg 真的值得称赞,因为我让这件事变得比需要的复杂得多。
    【解决方案2】:

    这似乎是对常见列名(+行名)的某种简单合并,然后是一个简单的聚合,这就是我要解决的方法

    library(data.table)
    merge(setDT(df1, keep.rownames = TRUE), # Convert to data.table + keep rows
          setDT(df2, keep.rownames = TRUE), # Convert to data.table + keep rows
          by = intersect(names(df1), names(df2)), # merge on common column names
          all = TRUE)[, lapply(.SD, sum, na.rm = TRUE), by = rn] # Sum all columns by group                   
    #    rn x y  z w
    # 1:  a 8 4  9 2
    # 2:  b 2 5  0 0
    # 3:  c 3 6  0 0
    # 4:  d 8 0 10 3
    

    是一个非常简单的基础 R 解决方案

    df1$rn <- row.names(df1)
    df2$rn <- row.names(df2)
    res <- merge(df1, df2, all = TRUE)
    rowsum(res[setdiff(names(res), "rn")], res[, "rn"], na.rm = TRUE)
    #   x y  z w
    # a 8 4  9 2
    # b 2 5  0 0
    # c 3 6  0 0
    # d 8 0 10 3
    

    【讨论】:

    • 非常漂亮。我忘记了merge 中的 all = TRUE 参数。
    • merge 默认使用每个数据集名称的intersection。您可以将by= 留空并获得相同的结果。
    • @thelatemail 是的,我忘了它是 base 中的默认值。在data.table 中,如果您没有设置密钥,它将无法工作。
    【解决方案3】:

    首先,我将获取新实体的所有行和列的名称:

    (all.rows <- unique(c(row.names(df1), row.names(df2))))
    # [1] "a" "b" "c" "d"
    (all.cols <- unique(c(names(df1), names(df2))))
    # [1] "x" "y" "z" "w"
    

    然后我会用这些行名和列名(矩阵数据初始化为全 0)构造一个输出矩阵,将 df1df2 添加到该矩阵的相关部分。

    out <- matrix(0, nrow=length(all.rows), ncol=length(all.cols))
    rownames(out) <- all.rows
    colnames(out) <- all.cols
    out[row.names(df1),names(df1)] <- unlist(df1)
    out[row.names(df2),names(df2)] <- out[row.names(df2),names(df2)] + unlist(df2)
    out
    #   x y  z w
    # a 8 4  9 2
    # b 2 5  0 0
    # c 3 6  0 0
    # d 8 0 10 3
    

    【讨论】:

    • 谢谢!我更喜欢这种解决方案而不是 eipi10,因为这不需要重命名,而我需要动态地进行重命名。虽然我通常更喜欢 dplyr,但这个解决方案很有意义。
    【解决方案4】:

    在融化/堆叠的数据帧上使用xtabs

    out <- rbind(cbind(rn=rownames(df1),stack(df1)), cbind(rn=rownames(df2),stack(df2)))
    as.data.frame.matrix(xtabs(values ~ rn + ind, data=out))
    
    #  x y w  z
    #a 8 4 2  9
    #b 2 5 0  0
    #c 3 6 0  0
    #d 8 0 3 10
    

    【讨论】:

      【解决方案5】:

      我不相信公认的(或替代合并)方法是最好的。如果您有共同的行,它将给出不正确的结果,它们将被连接而不是求和。

      这可以通过将 df2 更改为:

      df2 = data.frame(x = c(1,2), y = c(4,5), z = c(9,10), w = c(2, 3))
      rownames(df2) = c("a", "d")
      

      预期结果:

         rn x y  z w
      1:  a 2 8  9 2
      2:  b 2 5  0 0
      3:  c 3 6  0 0
      4:  d 2 5 10 3
      

      实际结果

      merge(setDT(df1, keep.rownames = TRUE), 
        setDT(df2, keep.rownames = TRUE), 
        by = intersect(names(df1), names(df2)), 
        all = TRUE)[, lapply(.SD, sum, na.rm = TRUE), by = rn]
      
         rn x y  z w
      1:  a 1 4  9 2
      2:  b 2 5  0 0
      3:  c 3 6  0 0
      4:  d 2 5 10 3
      

      您需要将外连接与内连接(或左/右连接,合并 all=T/all=F)结合起来。或者使用 plyr 的 rbind.fill :

      基础 R 解决方案

      res <- rbind.fill(df1,df2)
      rowsum(res[setdiff(names(res), "rn")], res[, "rn"], na.rm = TRUE)
      

      数据表解决方案

      as.data.table(rbind.fill(
        setDT(df1, keep.rownames = TRUE),
        setDT(df2, keep.rownames = TRUE)
      ))[, lapply(.SD, sum, na.rm = TRUE), by = rn]
      

      我更喜欢 rbind.fill 方法,因为您可以使用相同的语法“合并”> 2 个数据帧。

      【讨论】:

        猜你喜欢
        • 2015-06-28
        • 2016-07-10
        • 2020-12-04
        • 1970-01-01
        • 2021-03-30
        • 1970-01-01
        • 2021-04-04
        • 1970-01-01
        • 2017-06-24
        相关资源
        最近更新 更多