Using ..variable_name to create a variable that won't duplicate an existing variable_name

I'm working on a package that takes a user-submitted data frame as input. I need to add a bunch of variables to that data frame, but do not want to accidentally create new variables with the same variable names as the user.

My current solution is to use 'double-dotted' variables, as in ..weights, which 'protects' my code against any use case where the user might have a column called weights in the input. The code passes R CMD CHECK, but is this good practice?

Minimal example below.


# example problem ---------------------------------------------------------

double_up <- function(df) {
  require(dplyr)

  vars <- names(df)
  df$weights <- 2
  return(select(mutate_at(df, vars, ~ .x * weights), -weights))

}

# this works correctly
double_up(tibble(foo = 1:3, bar = 4:6))

# this doesn't, `weights` column gets dropped
double_up(tibble(foo = 1:3, weights = 1:3))

# my current solution -----------------------------------------------------

double_up2 <- function(df) {
  require(dplyr)
  
  vars <- names(df)
  df$..weights <- 2
  return(select(mutate_at(df, vars, ~ .x * ..weights), -..weights))
  
}

# this works correctly
double_up2(tibble(foo = 1:3, bar = 4:6))

# this also works correctly
double_up2(tibble(foo = 1:3, weights = 1:3))

# ... is this acceptable?

Footnotes:

  1. I'm not using .variable_name to avoid confusion with data-masking and the like.
  2. I understand the best option would be to create ..variable_name as a distinct vector, but I am not sure I can do that in my case, as I have some operations to conduct on it later on that take place within the data frame (if that makes sense).

Hi @frbr ,

would you be satisfied with

# only returns unique name
unique_col_name <- function(df, base_name) {
  new_name <- base_name
  while (new_name %in% names(df)) {
    new_name <- paste0("_", new_name)
  }
  return(new_name)
}

so a helper function which will deduce a unique colum based on some preseted value. So in the end your function would be


double_up2 <- function(df) {
  vars <- names(df)
  
  # Get unique name and add column
  weight_col_name <- unique_col_name(df, "weights")
  df[[weight_col_name]] <- 2
  
  # Convert to symbol for tidy evaluation
  weight_col <- sym(weight_col_name)
  
  # Apply transformation and remove helper column
  select(mutate_at(df, vars, ~ .x * !!weight_col), -!!weight_col)
}

Examples:

df1 <- tibble(foo = 1:3, bar = 4:6)
df2 <- tibble(foo = 1:3, weights = 1:3, `_weights` = 10:12)

print(double_up2(df1))  # Works fine
print(double_up2(df2))  # Creates "__weights" and works fine