【问题标题】:Coalescing duplicated variables after left_join using vector of variable names使用变量名向量在 left_join 之后合并重复的变量
【发布时间】:2019-02-24 11:36:56
【问题描述】:

在连接后合并重复列的非 NA 值并删除重复项时,我经常遇到问题。它类似于in this questionthis one 的描述。我想围绕coalesce(可能包括left_join)创建一个小函数,以便在遇到它时在一行中处理它(函数本身当然可以根据需要进行)。

在这样做时,我遇到了 quo_namesquos 描述的 here 等价物的缺失。

对于reprex,将带有识别信息的数据框与其他包含正确值但通常拼写错误的ID 的数据框连接起来。

library(dplyr)
library(rlang)

iris_identifiers <- iris %>% 
  select(contains("Petal"), Species)

iris_alt_name1 <- iris %>% 
  mutate(Species = recode(Species, "setosa" = "stosa")) 

iris_alt_name2 <- iris %>%
  mutate(Species = recode(Species, "versicolor" = "verscolor"))

这个更简单的函数有效:

replace_xy <- function(df, var) {

  x_var <- paste0(var, ".x")
  y_var <- paste0(var, ".y")

  df %>% 
    mutate(!! quo_name(var) := coalesce(!! sym(x_var), !! sym(y_var))) %>% 
    select(-(!! sym(x_var)), -(!! sym(y_var)))

}


iris_full <- iris_identifiers %>% 
  left_join(iris_alt_name1, by = c("Species", "Petal.Length", "Petal.Width")) %>% 
  left_join(iris_alt_name2, by = c("Species", "Petal.Length", "Petal.Width")) %>% 
  replace_xy("Sepal.Length") %>% 
  replace_xy("Sepal.Width")


head(iris_full)
#>   Petal.Length Petal.Width Species Sepal.Length Sepal.Width
#> 1          1.4         0.2  setosa          5.1         3.5
#> 2          1.4         0.2  setosa          4.9         3.0
#> 3          1.4         0.2  setosa          5.0         3.6
#> 4          1.4         0.2  setosa          4.4         2.9
#> 5          1.4         0.2  setosa          5.2         3.4
#> 6          1.4         0.2  setosa          5.5         4.2

但是对于如何实现多个变量的泛化,我有点迷茫,我认为这将是更容易的部分。下面的 sn-p 只是一个绝望的尝试——在尝试了许多变体之后——它大致捕捉了我想要实现的目标。

replace_many_xy <- function(df, vars) {

  x_var <- paste0(vars, ".x")
  y_var <- paste0(vars, ".y")

  df %>% 
    mutate_at(vars(vars), funs(replace_xy(.data, .))) %>% 
    select(-(!!! syms(x_var)), -(!!! syms(y_var)))

}

new_cols <- colnames(iris_alt_name1)
diff_cols <- new_cols [!(new_cols %in% colnames(iris_identifiers))]

iris_full <- iris_identifiers %>% 
  left_join(iris_alt_name1, by = c("Species", "Petal.Length", "Petal.Width")) %>% 
  left_join(iris_alt_name2, by = c("Species", "Petal.Length", "Petal.Width")) %>% 
  replace_many_xy(diff_cols)
#> Warning: Column `Species` joining factors with different levels, coercing
#> to character vector

#> Warning: Column `Species` joining character vector and factor, coercing
#> into character vector
#> Error: Unknown columns `Sepal.Length` and `Sepal.Width`

任何帮助将不胜感激!

【问题讨论】:

    标签: r dplyr tidyeval


    【解决方案1】:

    我们可以使用 {powerjoin} :

    library(powerjoin)
    iris_full <- iris_identifiers %>%
      left_join(iris_alt_name1, by = c("Species", "Petal.Length", "Petal.Width")) %>%
      power_left_join(iris_alt_name2, by = c("Species", "Petal.Length", "Petal.Width"), conflict  = coalesce_xy) %>%
      head()
    
    iris_full
    #   Petal.Length Petal.Width Species Sepal.Length Sepal.Width
    # 1          1.4         0.2  setosa          5.1         3.5
    # 2          1.4         0.2  setosa          4.9         3.0
    # 3          1.4         0.2  setosa          5.0         3.6
    # 4          1.4         0.2  setosa          4.4         2.9
    # 5          1.4         0.2  setosa          5.2         3.4
    # 6          1.4         0.2  setosa          5.5         4.2
    

    power_left_join 是一个改进的left_join,它允许通过conflict 参数处理列冲突,就像我们在这里所做的那样。

    conflict 参数是一个函数,它一个接一个地获取一对冲突的列,从右边合并可以使用需要 conflict = coalesce_yx


    这是一种让你的功能正常工作的方法:

    replace_many_xy <- function(tbl, vars){
      for(var in vars){
        x <- paste0(var,".x")
        y <-  paste0(var,".y")
        tbl <- mutate(tbl, !!sym(var) := coalesce(!!sym(x) , !!sym(y) )) %>%
         select(-one_of(x,y))
      }
      tbl
    }
    iris_full <- iris_identifiers %>%
      left_join(iris_alt_name1, by = c("Species", "Petal.Length", "Petal.Width")) %>%
      left_join(iris_alt_name2, by = c("Species", "Petal.Length", "Petal.Width")) %>%
      replace_many_xy(diff_cols) %>% as_tibble()
    # # A tibble: 372 x 5
    #    Petal.Length Petal.Width Species Sepal.Length Sepal.Width
    #           <dbl>       <dbl> <chr>          <dbl>       <dbl>
    #  1          1.4         0.2 setosa           5.1         3.5
    #  2          1.4         0.2 setosa           4.9         3  
    #  3          1.4         0.2 setosa           5           3.6
    #  4          1.4         0.2 setosa           4.4         2.9
    #  5          1.4         0.2 setosa           5.2         3.4
    #  6          1.4         0.2 setosa           5.5         4.2
    #  7          1.4         0.2 setosa           4.6         3.2
    #  8          1.4         0.2 setosa           5           3.3
    #  9          1.4         0.2 setosa           5.1         3.5
    # 10          1.4         0.2 setosa           4.9         3  
    # # ... with 362 more rows
    

    【讨论】:

    • 感谢穆迪!你的包听起来很有趣,我已经下载了它,很快就会测试它!抱歉,我离开这个项目几天了...我不确定我是否理解最后一个 mutate_at... 我得到以下信息:“mutate_impl(.data, dots) 中的错误:列 @987654329 @ 是不受支持的类型 NULL”。我错过了什么?
    • 感谢@Fons-MA,我仍在研究功能和界面,反馈将非常宝贵。至于mutate_at 的问题,我后来经历过但忘记了我在这里使用过它,它不适用于所有版本的 dplyr 并且应该删除后缀列。我会尽快用更强大的东西更新它
    • 你去吧,我用select(-one_of(x,y))替换了它,我认为它在版本中是稳定的
    • 啊,当然...我没有正确思考...我将dplyr从0.7更新到0.8,一切正常,但我想我会使用select下降只是为了明晰。有什么理由更喜欢mutate 选项?在你的包裹上,我只希望几个月前能看到它!大多数合并现在都完成了(我在进行时编写了可怕的检查功能),所以如果我修改代码的某些部分,我可能只会在不久的将来使用它。我会尽量让你更新。
    • 我只是习惯于将列设置为NULL 以删除它们,并且嵌入的括号越少越好,但是这个例子表明使用mutate_at 这样做是不好的做法,因为它不是对不太旧的版本很健壮。
    猜你喜欢
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多