【问题标题】:R: merge 2 data frames, one of them with repeated measures, keeping NAs where appropriateR:合并2个数据帧,其中一个重复测量,在适当的地方保留NA
【发布时间】:2021-01-29 03:55:21
【问题描述】:

假设我有 2 个数据框想要mergedf1 对每个样本进行了重复测量(我实际上不知道有多少,并且每个样本可能不同),而 df2 对于相同的样本只有一个测量值。

作为 MWE,如下所示:

> df1=data.frame(letter=rep(LETTERS[1:5],each=3), val1=1:15)
> df2=data.frame(letter=LETTERS[1:5], val2=16:20)
> df1
   letter val1
1       A    1
2       A    2
3       A    3
4       B    4
5       B    5
6       B    6
7       C    7
8       C    8
9       C    9
10      D   10
11      D   11
12      D   12
13      E   13
14      E   14
15      E   15
> df2
  letter val2
1      A   16
2      B   17
3      C   18
4      D   19
5      E   20

我想merge 他们以反映这一点。到目前为止,我可以做到:

> merge(df1, df2)
   letter val1 val2
1       A    1   16
2       A    2   16
3       A    3   16
4       B    4   17
5       B    5   17
6       B    6   17
7       C    7   18
8       C    8   18
9       C    9   18
10      D   10   19
11      D   11   19
12      D   12   19
13      E   13   20
14      E   14   20
15      E   15   20

但理想情况下,我需要这个:

> merge(df1, df2, all=T)
   letter rep val1 val2
1       A   1    1   16
2       A   2    2   NA
3       A   3    3   NA
4       B   1    4   17
5       B   2    5   NA
6       B   3    6   NA
7       C   1    7   18
8       C   2    8   NA
9       C   3    9   NA
10      D   1   10   19
11      D   2   11   NA
12      D   3   12   NA
13      E   1   13   20
14      E   2   14   NA
15      E   3   15   NA

但是我从一开始就没有 rep 列,所以我应该添加它,但我不知道如何...或者,也许 merge 有一些选项只列出第一个匹配项val2 专栏...

有什么帮助吗?这应该很容易,但我进入循环并检查以添加 rep 列,这可能不是这样。

【问题讨论】:

    标签: r dataframe merge


    【解决方案1】:

    我们可以将两个数据集连接在一起并创建一个rep 列,该列是每个letter 的行号,然后将val2 转换为NA,第一行除外。

    library(dplyr)
    
    inner_join(df1, df2, by = 'letter') %>% 
      group_by(letter) %>% 
      mutate(rep = row_number(), 
             val2 = replace(val2, -1, NA))
    
    #  letter  val1  val2   rep
    #   <chr>  <int> <int> <int>
    # 1 A          1    16     1
    # 2 A          2    NA     2
    # 3 A          3    NA     3
    # 4 B          4    17     1
    # 5 B          5    NA     2
    # 6 B          6    NA     3
    # 7 C          7    18     1
    # 8 C          8    NA     2
    # 9 C          9    NA     3
    #10 D         10    19     1
    #11 D         11    NA     2
    #12 D         12    NA     3
    #13 E         13    20     1
    #14 E         14    NA     2
    #15 E         15    NA     3
    

    要替换多个这样的 val 列而不一一明确提及它们,我们可以使用 across

    inner_join(df1, df2, by = 'letter') %>% 
      group_by(letter) %>% 
      mutate(rep = row_number(), 
             across(val2:valn, ~replace(., -1, NA)))
    

    在基础 R 中:

    df3 <- merge(df1, df2)
    cols <- c('val2')
    df3[duplicated(df3[c('letter', cols)]), cols] <- NA
    

    【讨论】:

    • Hhmm 但我真正需要的是适当插入的NAs 以反映val2 没有重复措施(仅在同一天从val1 获取1 个值).. . 我实际上不需要rep 列,只是作为实现这一目标的一种手段
    • 另外,考虑到我的实际数据框不仅有 val1val2,而且还有数十个(可能超过 100 个)不同名称的值列
    • 我更正了在val2 中得到NA 的答案。对于第二条评论,您的意思是您不想单独替换列吗?看看我的编辑是否回答了这个问题。如果您不需要 rep 列,可以从答案中删除 rep = row_number()
    • 到达那里,问题是值列名称可以有任何名称,而不仅仅是 val1:n
    • across 中,您可以使用列名模式starts_with('val')ends_with('val')contains('val')。或者您可以使用列号2:20c(2:5, 8:11),如果您无法创建要应用此功能的列组,则需要手动指定它们。
    【解决方案2】:

    这是一个基本版本,只需要知道替换为 NA 的列。

    merged <- merge(df1, df2, by = "letter")
    do_not_clean <- "letter"
    nms <- setdiff(names(merged), do_not_clean)
    spl <- by(merged, merged$letter, function(y) {
      y[nms] <- lapply(y[nms], function(x) replace(x, duplicated(x), NA))
      y$rep <- seq_len(nrow(y))
      y
    })
    out <- do.call(rbind, spl)
    out
    #      letter val1 val2 rep
    # A.1       A    1   16   1
    # A.2       A    2   NA   2
    # A.3       A    3   NA   3
    # B.4       B    4   17   1
    # B.5       B    5   NA   2
    # B.6       B    6   NA   3
    # C.7       C    7   18   1
    # C.8       C    8   NA   2
    # C.9       C    9   NA   3
    # D.10      D   10   19   1
    # D.11      D   11   NA   2
    # D.12      D   12   NA   3
    # E.13      E   13   20   1
    # E.14      E   14   NA   2
    # E.15      E   15   NA   3
    

    【讨论】:

      【解决方案3】:

      您可以使用merge 函数将它们合并:

      library(dplyr)
      
      
      df3 <- merge(df1, df2, by = "letter", all = T) %>% 
        group_by(letter) %>% 
        mutate(rep = row_number(),
               val2 = replace(val2, -1, NA))
      
      
      df3
      # A tibble: 15 x 4
      # Groups:   letter [5]
         letter  val1  val2   rep
         <chr>  <int> <int> <int>
       1 A          1    16     1
       2 A          2    NA     2
       3 A          3    NA     3
       4 B          4    17     1
       5 B          5    NA     2
       6 B          6    NA     3
       7 C          7    18     1
       8 C          8    NA     2
       9 C          9    NA     3
      10 D         10    19     1
      11 D         11    NA     2
      12 D         12    NA     3
      13 E         13    20     1
      14 E         14    NA     2
      15 E         15    NA     3
      

      【讨论】:

        猜你喜欢
        • 2017-08-08
        • 2017-05-28
        • 1970-01-01
        • 2016-01-31
        • 2022-01-25
        • 1970-01-01
        • 2020-02-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多