【问题标题】:replace NA values of two data frames with matching ID and dates in R用 R 中匹配的 ID 和日期替换两个数据帧的 NA 值
【发布时间】:2021-07-04 13:02:09
【问题描述】:

我有两个行列长度不同的数据框

data.frame(
  stringsAsFactors = FALSE,
              Date = c("01/01/2000", "01/01/2010", "01/01/2020"),
           Germany = c(5, 8, 9),
            France = c(4, NA, 7),
        Luxembourg = c(10, 6, 3)
) -> df1
data.frame(
  stringsAsFactors = FALSE,
              Date = c("01/01/1990", "01/01/2000", "01/01/2010", "01/01/2020"),
           Germany = c(1, 9, 7, NA),
            France = c(10, 3, 9, 6),
        Luxembourg = c(10, NA, NA, 7),
           Belgium = c(NA, 8, 1, 9)
) -> df2

我必须创建第三个 df (df3) 其中,

  1. 通过匹配 IDsDatesdf1 的 NA 值替换为 df2 的值,反之亦然 ( df2 中的 NA 替换为 df1)
  2. df1 的值是优先级 (=TRUE)
  3. 所有不在一个数据框中的列(如本例中的比利时)也应包含在 df3 中

df3 应如下所示:

任何帮助将不胜感激

【问题讨论】:

    标签: r


    【解决方案1】:

    我们可以加入 on 'Date' 并使用 fcoalesce 将 NA 替换为相应的非 NA

    library(data.table)
    nm2 <- intersect(names(df2)[-1], names(df1)[-1])
    df3 <- copy(df2)
    setDT(df3)[df1, (nm2) := Map(fcoalesce, mget(nm2),
           mget(paste0('i.', nm2))), on = .(Date)]
    

    -输出

    df3
    #         Date Germany France Luxembourg Belgium
    #1: 01/01/1990       1     10         10      NA
    #2: 01/01/2000       9      3         10       8
    #3: 01/01/2010       7      9          6       1
    #4: 01/01/2020       9      6          7       9
    

    或者这可以通过tidyverse来完成

    library(dplyr)
    library(stringr)
    left_join(df2, df1, by = 'Date') %>% 
       mutate(Date, across(ends_with(".x"), 
        ~ coalesce(., get(str_replace(cur_column(), "\\.x$", ".y"))))) %>% 
       select(-ends_with('.y')) %>% 
       rename_with(~ str_remove(., "\\.x$"), ends_with('.x'))
    

    【讨论】:

    • @AnoushiravanR 是的,您可以在dplyrleft_join(df3, df1, by = 'Date') 中使用coalesce,然后您可能需要获取.x.y 列,因为这些列在有重复
    • @AnoushiravanR 你可以做left_join(df2, df1, by = 'Date') %&gt;% mutate(Date, across(ends_with(".x"), ~ coalesce(., get(str_replace(cur_column(), "\\.x", ".y"))))) %&gt;% select(-ends_with('.y')) %&gt;% rename_with(~ str_remove(., "\\.x$"), ends_with('.x'))
    • @AnoushiravanR 原因是我只循环 .x, then get the column names with cur_column(), replace the suffix part with .y` 到 get 列值,然后使用 coalesce 做对应列的替换
    • @AnoushiravanR . 匹配任何字符。所以我们需要用\\. 转义它。另外,最好使用\\.x$ 在字符串末尾指定
    • 我明白了。我只是迟早要学会它。亲爱的@akrun,我非常感谢你的时间和慷慨。
    【解决方案2】:

    这是另一个data.table 选项

    cols <- setdiff(intersect(names(df1), names(df2)), "Date")
    setDT(df1)[setDT(df2),
      on = "Date"
    ][
      ,
      c(cols) :=
        Map(
          fcoalesce,
          .SD[, cols, with = FALSE],
          .SD[, paste0("i.", cols), with = FALSE]
        )
    ][,
      .SD,
      .SDcols = patterns("^[^i]")
    ]
    

    给予

             Date Germany France Luxembourg Belgium
    1: 01/01/1990       1     10         10      NA
    2: 01/01/2000       5      4         10       8
    3: 01/01/2010       8      9          6       1
    4: 01/01/2020       9      7          3       9
    

    【讨论】:

      【解决方案3】:

      基础 R 解决方案:

      # Store as a variable a list denoting each data.frame's column names: 
      # cnames => character vector
      cnames <- list(names(df1), names(df2))
      
      # Determine which vector of names is required in the resulting data.frame 
      # required_vecs => character vector
      required_vecs <- cnames[[which.max(lengths(cnames))]]
      
      # Merge the data: full_data => data.frame
      full_data <- merge(
        df1,
        df2,
        by = "Date",
        all = TRUE
      )
      
      # Resolve the vector names of vectors requiring coalescing: 
      # clsce_required_vecs=> character vector
      clsce_required_vecs <- setdiff(intersect(names(df1), names(df2)), c("Date"))
      
      # Resolve the vector names of vectors not requiring coalescing: 
      # nt_rqrd_vecs => character vector
      nt_rqrd_vecs <- setdiff(unlist(cnames), clsce_required_vecs)
      
      # Split-Apply-Combine data requiring coalescing: coalesced_data => data.frame
      coalesced_data <- setNames(
        data.frame(
          do.call(
            cbind, 
            lapply(
              clsce_required_vecs, 
              function(x) {
                # Subset the data to only contain relevant vectors: sbst => data.frame
                sbst <- full_data[,grepl(x, names(full_data))]
                # Split each column (of the same data) into a data.frame in a list:
                # same_vecs => list of data.frames
                same_vecs <- split.default(sbst, seq_len(ncol(sbst)))
                # Rename the data.frames as required and row-bind them into a single df:
                # vector => GlobalEnv()
                Reduce(
                  function(y, z){
                    replace(y, is.na(y), z[is.na(y)])
                  }, 
                  do.call(cbind, same_vecs)
                )
              }
            )
          ), row.names = NULL), 
      clsce_required_vecs)
          
      # Column bind and order the columns:
      res <- cbind(full_data[, nt_rqrd_vecs], coalesced_data)[,required_vecs]
      

      【讨论】:

        【解决方案4】:
        library(tidyverse)
        library(lubridate)
        
        df1 <- tibble::tribble(
          ~Date, ~Germany, ~France, ~Luxembourg,
          "01/01/2000",        5,       4,          10,
          "01/01/2010",        8,      NA,           6,
          "01/01/2020",        9,       7,           3
        )
        df2 <- tibble::tribble(
          ~Date, ~Germany, ~France, ~Luxembourg, ~Belgium,
          "01/01/1990",        1,      10,          10,       NA,
          "01/01/2000",        9,       3,          NA,        8,
          "01/01/2010",        7,       9,          NA,        1,
          "01/01/2020",       NA,       6,           7,        9
        )
        
        bind_rows(df1 %>%
                    mutate(priority = 1),
                  df2 %>%
                    mutate(priority = 2)) %>%
          mutate(Date = lubridate::dmy(Date)) %>%
          group_by(Date) %>%
          arrange(priority) %>%
          summarise(across(-priority, ~ first(na.omit(.))))
        #> # A tibble: 4 x 5
        #>   Date       Germany France Luxembourg Belgium
        #>   <date>       <dbl>  <dbl>      <dbl>   <dbl>
        #> 1 1990-01-01       1     10         10      NA
        #> 2 2000-01-01       5      4         10       8
        #> 3 2010-01-01       8      9          6       1
        #> 4 2020-01-01       9      7          3       9
        

        【讨论】:

          【解决方案5】:

          仅使用mutate(across..dplyr 方法。

          我还建议使用full_join 而不是left_joinright_join,因为full_join 将从df1df2 获取所有行,而不是左连接或右连接。

          data.frame(
            stringsAsFactors = FALSE,
            Date = c("01/01/2000", "01/01/2010", "01/01/2020"),
            Germany = c(5, 8, 9),
            France = c(4, NA, 7),
            Luxembourg = c(10, 6, 3)
          ) -> df1
          data.frame(
            stringsAsFactors = FALSE,
            Date = c("01/01/1990", "01/01/2000", "01/01/2010", "01/01/2020"),
            Germany = c(1, 9, 7, NA),
            France = c(10, 3, 9, 6),
            Luxembourg = c(10, NA, NA, 7),
            Belgium = c(NA, 8, 1, 9)
          ) -> df2
          
          library(dplyr)
          
          
          df1 %>% full_join(df2, by = 'Date', suffix = c('_x', '_y')) %>%
            mutate(across(ends_with('_x'), ~coalesce(., get(sub('_x', '_y', cur_column()))),
                          .names = '{sub("_x", "", {.col})}')) %>%
            select(!ends_with('_x') & !ends_with('_y'))
          
          #>         Date Belgium Germany France Luxembourg
          #> 1 01/01/2000       8       5      4         10
          #> 2 01/01/2010       1       8      9          6
          #> 3 01/01/2020       9       9      7          3
          #> 4 01/01/1990      NA       1     10         10
          

          reprex package (v2.0.0) 于 2021 年 5 月 18 日创建

          【讨论】:

            猜你喜欢
            • 2021-06-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-10-26
            • 2017-01-30
            • 2014-04-10
            • 2018-12-11
            • 1970-01-01
            相关资源
            最近更新 更多