【问题标题】:Use Negation with Select in dplyr 0.7.x在 dplyr 0.7.x 中使用否定和选择
【发布时间】:2017-11-24 17:25:22
【问题描述】:

我正在尝试编写一个函数,该函数需要从结果数据框中排除用户传递的变量。我也借此机会进一步了解新的 dplyr 语法。

该函数的作用类似于数据帧的交叉连接。我想用它作为跨函数参数复制数据的干净方式。

函数的作用如下:

crossjoin_df <- function(df1, df2, temp_col = ".k") {
  df1 <- df1 %>% 
    mutate(!!temp_col :=  1)

  df2 <- df2 %>% 
    mutate(!!temp_col :=  1)

  out <- left_join(df1, df2, by = temp_col)

  # I'm trying to replace the next line
  out[,!names(out)==temp_col] 
} 

params <- data.frame(k = c(11,10),
                 n = c(27,26))

data <- data.frame(a = 1:3,
               b = 4:6)

crossjoin_df(params, data) # 6 row data set

我想看看是否可以用管道选择语句替换最后一个语句。但是,否定似乎不起作用。

我可以得到类似的东西:

out %>% select(!!temp_col)

工作,但这显然只选择.k。我无法得到类似的东西:

out %>% select(-!!temp_col)

上班。

【问题讨论】:

  • 由于temp_var是一个字符串,如果你想用!!取消引用,你需要使用rlang::sym把它变成一个quosure,例如out %&gt;% select(-!!rlang::sym(temp_col))
  • 谢谢,我的理解是我不能让temp_var 裸露,因为我需要它位于mutate 语句的LHS 上。不确定是否有更清洁的方法来解决它。我希望能够只坚持使用 dplyr。

标签: r dplyr rlang


【解决方案1】:

您需要 rlang,它是 dplyr 的后端包,可以启用 tidy eval,无论您是否想继续使用字符串,在这种情况下,您需要 sym 将字符串转换为 quosure:

library(dplyr)

params <- data.frame(k = c(11,10),
                     n = c(27,26))

data <- data.frame(a = 1:3,
                   b = 4:6)

crossjoin_df <- function(df1, df2, temp_col = ".k") {
    df1 <- df1 %>% mutate(!!temp_col :=  1)

    df2 <- df2 %>% mutate(!!temp_col :=  1)

    left_join(df1, df2, by = temp_col) %>% 
        select(-!!rlang::sym(temp_col))
}

crossjoin_df(params, data)
#>    k  n a b
#> 1 11 27 1 4
#> 2 11 27 2 5
#> 3 11 27 3 6
#> 4 10 26 1 4
#> 5 10 26 2 5
#> 6 10 26 3 6

...或切换到 full tidy eval,在这种情况下,您需要 quo_name 将 quosure 转换为名称:

crossjoin_df <- function(df1, df2, temp_col = .k) {
    temp_col <- enquo(temp_col)

    df1 <- df1 %>% mutate(!!rlang::quo_name(temp_col) :=  1)

    df2 <- df2 %>% mutate(!!rlang::quo_name(temp_col) :=  1)

    left_join(df1, df2, by = rlang::quo_name(temp_col)) %>% 
        select(-!!temp_col)
}

crossjoin_df(params, data)
#>    k  n a b
#> 1 11 27 1 4
#> 2 11 27 2 5
#> 3 11 27 3 6
#> 4 10 26 1 4
#> 5 10 26 2 5
#> 6 10 26 3 6

或者,只需使用tidyr::crossing

tidyr::crossing(params, data)
#>    k  n a b
#> 1 11 27 1 4
#> 2 11 27 2 5
#> 3 11 27 3 6
#> 4 10 26 1 4
#> 5 10 26 2 5
#> 6 10 26 3 6

【讨论】:

    【解决方案2】:

    你可以使用one_of,然后用-否定选择:

    out %>% select(-one_of(temp_col))
    

    crossjoin_df <- function(df1, df2, temp_col = ".k") {
      # `$`(df1, temp_col) <- 1
      df1 <- df1 %>% 
        mutate(!!temp_col :=  1)
    
      # `$`(df2, temp_col) <- 1
      df2 <- df2 %>% 
        mutate(!!temp_col :=  1)
    
      left_join(df1, df2, by = temp_col) %>% select(-one_of(temp_col))
    
    } 
    
    params <- data.frame(k = c(11,10),
                     n = c(27,26))
    
    data <- data.frame(a = 1:3,
                   b = 4:6)
    
    crossjoin_df(params, data)
    
    #   k  n a b
    #1 11 27 1 4
    #2 11 27 2 5
    #3 11 27 3 6
    #4 10 26 1 4
    #5 10 26 2 5
    #6 10 26 3 6
    

    【讨论】:

    • 这绝对有效,但我不确定它是否符合 quosures 的新语法/概念的精神。
    • 我的理解是 quosure 的存在是为了增强使用 dplyr 以非标准评估风格进行编程的能力,即弃用 *_ 动词。所以我猜select 带有选择辅助函数仍然是执行此操作的规范方法之一。如果您更喜欢使用 quosure,@alistaire 的评论应该是不错的选择。
    【解决方案3】:

    这应该也可以:

    out %>% select_(paste0("-",temp_col))
    

    【讨论】:

    • 谢谢,但我认为 dplyr 正在远离 *_ 动词
    • 它们不会被维护或提出,但它们不会在几年内或永远不会从包中删除:)。但是,是的,使用@Psidom 的答案可能更好,为了完整起见,我提到了它。
    猜你喜欢
    • 2018-12-25
    • 2018-01-20
    • 2016-01-18
    • 2018-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    相关资源
    最近更新 更多