Jesse Kerr
Jesse Kerr

Reputation: 341

How can I access a column name within a function in a mutate call?

I am mutating a column to create a new one in R using a function I wrote.

Within my function, I want to send a message that includes the name of the column that is being changed.

How can I access the column name that is being mutated from inside of the function within the mutate call?

Reproducible Example:

data <- tribble(
 ~colB, 
  1, 
  2, 
  3
)

# Function that will be used in the mutate
add1 <- function(numeric_vector) {
  return(1 +numeric_vector)

  # I want to message the user the name of the column they are mutating
  # This simply returns the entire vector
  message("You mutated", numeric vector)

  # This returns 'numeric_vector'
  message("You mutated", quo_name(quo(numeric_vector)))

}

# Desired Output:
data %>% 
  mutate(colC = add1(colB))

You mutated colB
 colB  colC
 <dbl> <dbl>
   1     2
   2     3
   3     4

Upvotes: 1

Views: 446

Answers (2)

G. Grothendieck
G. Grothendieck

Reputation: 269441

Use substitute which returns a name class object. We have wrapped the message call in an on.exit to ensure that it is run after the calculation so that it is not run if the calculation fails. If that is not important then replace on.exit(message(...)) with just message(...). Note that add1 itself does not use any packages.

library(dplyr)

add1 <- function(numeric_vector) {
  on.exit(message("You mutated ", substitute(numeric_vector)))
  1 + numeric_vector
}

BOD %>% mutate(Time = add1(Time))

giving:

You mutated Time
  Time demand
1    2    8.3
2    3   10.3
3    4   19.0
4    5   16.0
5    6   15.6
6    8   19.8

rlang

To use rlang make use of enexpr from that package. dplyr will make it available. enexpr returns a name class object.

enexpr is similar to substitute but one difference that will affect processing is that substitute will extract the code portion of a promise whether or not the promise has been forced (i.e. evaluated); however, enexpr will extract the code of unforced promises but will extract the value of forced promises. Since we want the code portion we must ensure that enexpr(numeric_vector) is run before numeric_vector is used in a calculation. To ensure that we introduce a new variable arg_name which is run at the beginning ensuring that enexpr has an unforced argument.

library(dplyr)

add2 <- function(numeric_vector) {
  arg_name <- enexpr(numeric_vector)
  on.exit(message("You mutated ", arg_name))
  1 + numeric_vector
}

BOD %>% mutate(Time = add2(Time))

Upvotes: 3

MrFlick
MrFlick

Reputation: 206167

I think you want

add1 <- function(numeric_vector) {
  message(paste("You mutated", quo_name(enquo(numeric_vector))))
  return(1 + numeric_vector)
}

Note that you have to print your message before the return(). Nothing after the return() will run in a function because you exit when you hit that statement. Also, you can use enquo() to grab the variable to get it's name. And you need to get it's name while it's still in a promise state, so that means before you actually use it's value.

Upvotes: 2

Related Questions