【问题标题】:How to fill one data frame with data from another while retaining NAs from the first如何用另一个数据框填充一个数据框,同时保留第一个数据框的 NA
【发布时间】:2022-01-04 01:41:43
【问题描述】:

我有 2 个列名相同但行数不同的数据框。第一个数据框 (a) 看起来与此类似:

a = data.frame("Site"=c(1,2,3,4,7,9,10,11,13,14), 
               "v1"=c(0,0,0,0,0,0,0,0,0,0), 
               "v2"=c(0,0,0,0,NA,NA,NA,0,0,0), 
               "v3"=c(0,0,0,NA,0,NA,0,0,0,0), 
               "v4"=c(0,0,0,0,0,0,0,0,NA,NA), 
               "v5"=c(0,0,0,0,0,NA,0,NA,0,0)) 

注意:站点 5、6、8 和 12 是故意缺失的。

第二个数据框 (b) 如下所示:

b = data.frame("Site"=c(2,3,4,7,10,14),
               "v1"=c(1,NA,2,1,NA,NA),
               "v2"=c(1,1,NA,NA,NA,NA),
               "v3"=c(NA,1,NA,NA,NA,1),
               "v4"=c(1,NA,4,1,NA,NA),
               "v5"=c(1,NA,2,1,1,3))

我想要实现的是:

desired = data.frame("Site"=c(1,2,3,4,7,9,10,11,13,14), 
                     "v1"=c(0,1,0,2,1,0,0,0,0,0), 
                     "v2"=c(0,1,1,0,NA,NA,NA,0,0,0), 
                     "v3"=c(0,0,1,NA,0,NA,0,0,0,1), 
                     "v4"=c(0,1,0,4,1,0,0,0,NA,NA), 
                     "v5"=c(0,1,0,2,1,NA,1,NA,0,3))

我将数据帧 b 中的数据“注入”(我确信有更好的术语)到数据帧 a 中,然而我想用零替换 b 中的任何 NA,并且保持 NA 保持原样。

我找到并尝试了这段代码:

cols <- colnames(a)[colnames(a) %in% colnames(b)]
rows <- rownames(a)[rownames(a) %in% rownames(b)]

a[rows, cols] <- b[rows, cols]

但它带来了 NA。我考虑先用零替换 NA,但即便如此,它也会删除我目前在数据帧 a 中想要保留的 NA。

也许一个 for 循环或 tidyverse 中的东西是要走的路,但我什至不知道从哪里开始。任何帮助将不胜感激!

【问题讨论】:

    标签: r dataframe dplyr na


    【解决方案1】:

    我建议您首先将b 中的每个NA 值替换为0,然后使用inner_join 将结果与a 中对应的Site 值合并。然后,您可以将a 的非NA 值替换为其在b 中的相应值,而使a 中的NA 值保持不变。最后,我们将修改后的数据框与a 的子集绑定,Site 的值在b 中不存在。

    library(dplyr)
    
    a %>%
      inner_join(b %>%
                   mutate(across(!Site, ~ replace(.x, is.na(.x), 0))), 
                 by = "Site") %>%
      mutate(across(ends_with(".x"), ~ ifelse(!is.na(.x), get(gsub("(.*\\.)x", "\\1y", cur_column())), 
                                              .x))) %>%
      select(!ends_with("y")) %>%
      rename_with(~ gsub("(.*)\\.x", "\\1", .), ends_with(".x")) %>%
      bind_rows(a %>% 
                  filter(!Site %in% unique(b$Site))) %>%
      arrange(Site)
    
    
       Site v1 v2 v3 v4 v5
    1     1  0  0  0  0  0
    2     2  1  1  0  1  1
    3     3  0  1  1  0  0
    4     4  2  0 NA  4  2
    5     7  1 NA  0  1  1
    6     9  0 NA NA  0 NA
    7    10  0 NA  0  0  1
    8    11  0  0  0  0 NA
    9    13  0  0  0 NA  0
    10   14  0  0  1 NA  3
    

    也是我的好朋友Onyambu提出的一个绝妙而简洁的解决方案:

    rbind(a, b) %>% 
      group_by(Site) %>%  
      summarise(across(everything(), ~ 
                         if(any(!is.na(.x))) max(.x, na.rm = TRUE) else NA))
    

    【讨论】:

    • 这太好了,感谢您的帮助!
    【解决方案2】:
    i <- match(b$Site, a$Site)
    a_nas <- is.na(a)
    for (j in seq(2, ncol(a))) {
      a[i, j] <- ifelse(is.na(b[[j]]), 0, b[[j]])
    }
    a[a_nas] <- NA
    
    all.equal(desired, a)
    # [1] TRUE
    

    【讨论】:

    • 感谢 for 循环的使用!
    【解决方案3】:
    merge(b, a, by = 'Site', all = TRUE) %>%
      split.default(sub('.x|.y', '', names(.))) %>%
      map_df(~coalesce(!!!.x))
    
    # A tibble: 10 x 6
        Site    v1    v2    v3    v4    v5
       <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
     1     1     0     0     0     0     0
     2     2     1     1     0     1     1
     3     3     0     1     1     0     0
     4     4     2     0    NA     4     2
     5     7     1    NA     0     1     1
     6     9     0    NA    NA     0    NA
     7    10     0    NA     0     0     1
     8    11     0     0     0     0    NA
     9    13     0     0     0    NA     0
    10    14     0     0     1    NA     3
    

    【讨论】:

    • @AnoushiravanR rbind(a, b)%&gt;% group_by(Site)%&gt;% summarise(across(everything(), ~if(any(!is.na(.x))) max(.x, na.rm = TRUE) else NA)) 是合并两者的另一种方式。
    • 我猜,解决方案不正确。它为 MWE 提供了正确的答案,但总的来说它可能不正确。例如,如果有人设置了b[3,4]=50,那么NA 中的a[4,4] 将不会被保留。
    猜你喜欢
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 2018-12-08
    • 2022-12-09
    • 1970-01-01
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    相关资源
    最近更新 更多