Search code examples
rtidyversense

How to select, duplicate, and rename multiple columns in tibble with tidy evaluation semantics?


I'd like to copy a set of variables in my tibble so I can have variable_unmodified and variable values in downstream evaluation. I've come up with a hacky version using the old-style underscore NSE select_() function and .dots, but would like to use the newer NSE approach of tidy evaluation semantics.

This does what I want:

tibble_to_max <- tibble(
  "a_col" = c("1", "2", "3", "4"),
  "max_1" = c("3;4", "2{3}4", "7", ".{1}"),
  "max_2" = c("3;4", "2{3}4", "7", ".{1}")
)

cols_to_max <- c("max_1", "max_2")

unparsed_names <-  paste0(cols_to_max, "_unparsed")

tibble_to_max %>%
  bind_cols(select_(., .dots = setNames(cols_to_max, unparsed_names)))

output:

# A tibble: 4 x 5
  a_col max_1 max_2 max_1_unparsed max_2_unparsed
  <chr> <chr> <chr>          <chr>          <chr>
1     1   3;4   3;4            3;4            3;4
2     2 2{3}4 2{3}4          2{3}4          2{3}4
3     3     7     7              7              7
4     4  .{1}  .{1}           .{1}           .{1}

But if I try to do it with select() and !!, .dots doesn't work as I expected:

tibble_to_max %>%
  bind_cols(select(., .dots = setNames(!!cols_to_max, !!unparsed_names)))

The columns are not named as desired:

# A tibble: 4 x 5
  a_col max_1 max_2 .dots1 .dots2
  <chr> <chr> <chr>  <chr>  <chr>
1     1   3;4   3;4    3;4    3;4
2     2 2{3}4 2{3}4  2{3}4  2{3}4
3     3     7     7      7      7
4     4  .{1}  .{1}   .{1}   .{1}

What is the correct way to do this? Also, bonus points for avoiding defining unparsed_names as a separate variable...


Solution

  • Thanks to @CPak for getting me on the right path. This accomplishes what I was shooting for, and uses tidy evaluation semantics instead of select_():

    tibble_to_max <- tibble(
      "a_col" = c("1", "2", "3", "4"),
      "max_1" = c("3;4", "2{3}4", "7", ".{1}"),
      "max_2" = c("3;4", "2{3}4", "7", ".{1}")
    )
    
    cols_to_max <- c("max_1", "max_2")
    
    tibble_to_max %>%
      bind_cols(
        select_at(., 
          .vars = !!cols_to_max, 
          .funs = funs(paste0(., "_unparsed"))
          )
        )