bienqueda
bienqueda

Reputation: 89

Incorrect evaluation of the parameters of a function using non standard evaluation?

Suppose we have some vectors and dataframes:

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

We write a function using non standard evaluation that calculates the mean of a column of the dataframe and the mean of a vector.

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

That works as expected. But what happens if we write the same function but choosing another names for the parameters?

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

The first parameter is evaluating the same as before, but the second parameter is always evaluating the column "y" of the dataframe. Shouldn't it be evaluating the vectors "a" and "b" respectively?

Upvotes: 1

Views: 41

Answers (2)

G. Grothendieck
G. Grothendieck

Reputation: 269481

Variables in the actual arguments to summarise are first looked up in the data frame specified in the first argument to summarise and are only looked up in the caller to summarise if not found in that data frame. Thus by hard coding y into a summarise argument it will always look for it in df1 first.

1) We can use !! to avoid this. The argument to !! is not looked up in the data argument.

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) We could use as_quosure to emphasize this. That will put the value of y in the quosure formula. In the example, y <- as_quosure(y) would cause the new quosure to contain ~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) Of course, we could just use a formal argument name that is unlikely to be used in the data frame:

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

Upvotes: 0

akrun
akrun

Reputation: 887028

We can use globalenv() to get the list of objects, get the value of the object by passing the object name as string and use that in the summarise statement

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

Suppose, if we also need to get the mean of 'y' column

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

Upvotes: 0

Related Questions