【问题标题】:Incorrect evaluation of the parameters of a function using non standard evaluation?使用非标准评估对函数参数的评估不正确?
【发布时间】:2018-08-01 00:14:59
【问题描述】:

假设我们有一些向量和数据框:

a <- c(1, 2, 0, 1)
b <- c(6, 4)
df1 <- data_frame(x = c(6, 8, 12), y = c(24, 18, 16))

我们使用非标准评估编写一个函数,计算数据框列的平均值和向量的平均值。

calculate_means <- function(df, column, vector) {
  column <- enquo(column)
  summarise(df, mean_column = mean(!!column), mean_vector = mean(vector))
}

calculate_means(df1, x, a)
# A tibble: 1 x 2
  mean_column mean_vector
        <dbl>       <dbl>
1        8.67        1.00

calculate_means(df1, y, b)
# A tibble: 1 x 2
  mean_column mean_vector
        <dbl>       <dbl>
1        19.3        5.00

按预期工作。但是如果我们编写相同的函数但为参数选择其他名称会发生​​什么?

calculate_means <- function(df, x, y) {
  x <- enquo(x)
  summarise(df, mean_column = mean(!!x), mean_vector = mean(y))
}

calculate_means(df1, x, a)
# A tibble: 1 x 2
  mean_column mean_vector
        <dbl>       <dbl>
1        8.67        19.3

calculate_means(df1, y, b)
# A tibble: 1 x 2
  mean_column mean_vector
        <dbl>       <dbl>
1        19.3        19.3

第一个参数的评估与以前相同,但第二个参数始终评估数据帧的“y”列。 它不应该分别评估向量“a”和“b”吗?

【问题讨论】:

    标签: r dplyr non-standard-evaluation


    【解决方案1】:

    summarise 的实际参数中的变量首先在summarise 的第一个参数中指定的数据帧中查找,如果在该数据帧中找不到,则仅在summarise 的调用者中查找。因此,通过将y 硬编码为summarise 参数,它总是会首先在df1 中查找它。

    1) 我们可以使用 !!避免这种情况。论据!!不在 data 参数中查找。

    calculate_means2 <- function(df, x, y) {
      x <- enquo(x)
      summarise(df, mean_column = mean(!!x), mean_vector = mean(!!y))
    }
    
    calculate_means2(df1, y, b)
    # A tibble: 1 x 2
      mean_column mean_vector
            <dbl>       <dbl>
    1        19.3        5.00
    

    2) 我们可以使用as_quosure 来强调这一点。这会将y 的值放入quosure 公式中。在示例中,y &lt;- as_quosure(y) 将导致新的 quosure 包含 ~c(6, 4)

    calculate_means3 <- function(df, x, y) {
      x <- enquo(x)
      y <- rlang::as_quosure(y)
      summarise(df, mean_column = mean(!!x), mean_vector = mean(!!y))
    }
    
    calculate_means3(df1, y, b)
    # A tibble: 1 x 2
      mean_column mean_vector
            <dbl>       <dbl>
    1        19.3        5.00
    

    3)当然,我们可以只使用不太可能在数据框中使用的正式参数名称:

    calculate_means4 <- function(df, x, y.) {
      x <- enquo(x)
      summarise(df, mean_column = mean(!!x), mean_vector = mean(y.))
    }
    
    calculate_means4(df1, y, b)
    # A tibble: 1 x 2
      mean_column mean_vector
            <dbl>       <dbl>
    1        19.3        5.00
    

    【讨论】:

    • 第一个解决方案非常清晰和简单。但我想知道在什么情况下我们希望我们创建的函数的参数在没有 !! 的情况下进行评估当它们在摘要中使用时。看来我们总是想用!!使用我们函数的参数。那么为什么不是默认值呢?当我们想要在每次调用函数时评估具有相同值的东西时,情况就不同了,在这种情况下,我们不会在函数中为该值创建参数,所以不需要 !!
    • 无论先看哪一个,如果你想要另一个,仍然需要以某种方式指定它。
    【解决方案2】:

    我们可以使用globalenv() 来获取对象列表,通过将对象名称作为字符串传递来获取对象的值,并在summarise 语句中使用它

    calculate_means <- function(df, x, y) {
      x <- enquo(x)
      y <- quo_name(enquo(y))
      v1 <- globalenv()[[y]]
    
      df %>%
           summarise(mean_column = mean(!! x),
           mean_vector = mean(v1))
     }
    
    calculate_means(df1, x, a)
    # A tibble: 1 x 2
    #   mean_column mean_vector
    #        <dbl>       <dbl>
    #1        8.67        1.00
    
    calculate_means(df1, y, b)
    # A tibble: 1 x 2
    #   mean_column mean_vector
    #        <dbl>       <dbl>
    #1        19.3        5.00
    

    假设,如果我们还需要获取'y'列的mean

    calculate_means <- function(df, x, y) {
      x <- enquo(x)
      y <- quo_name(enquo(y))
      v1 <- globalenv()[[y]]
    
      df %>%
           summarise(mean_column = mean(!! x),
           mean_vector = mean(v1),
           mean_column2 = mean(.data$y))
     }
    
    calculate_means(df1, x, a)
    # A tibble: 1 x 3
    #  mean_column mean_vector mean_column2
    #        <dbl>       <dbl>        <dbl>
    #1        8.67        1.00         19.3
    

    【讨论】:

    • 是的。我理解你,但我不明白这是预期的行为。如果我们想创建一个函数并且我们希望其中一个参数是一个变量,我们是否需要“取消”它?如果我调用一个函数并为参数“y”传递一个值“a”,我可以理解我没有正确执行它并且函数会引发错误。但是我不明白为什么它使用参数的名称作为使用的值。
    • @bienqueda quo_name 步骤适用于 globalenv 我不确定您的函数的预期行为。据我了解,您有一个与全局 env 对象中相同的列,在这里您想要获取全局变量的平均值而不是列。关于enquo,用于将符号转换为quosure
    猜你喜欢
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 2015-12-01
    相关资源
    最近更新 更多