I would like to write a function that mutates user named columns. In the first instance, I would like to pass them as strings, but am also interested to know how to do this with column names.
I have found a working method for string arguments with unlist(list(...)), but I suspect that there is a neater way of doing this.
Here is the example
data <- tibble(
a = 1:10,
b = 21:30,
c = 101:110
)
#working but messy
test_strings <- function(data, ...) {
data %>%
mutate_at(unlist(list(...)),
function(x){x 5})
}
test_strings(data, "a", "b")
# A tibble: 10 x 3
# a b c
# <dbl> <dbl> <int>
# 1 6 26 101
# 2 7 27 102
# 3 8 28 103
# 4 9 29 104
# 5 10 30 105
# 6 11 31 106
# 7 12 32 107
# 8 13 33 108
# 9 14 34 109
# 10 15 35 110
#not working
test_sym <- function(data, ...) {
data %>%
mutate_at(c(...),
function(x){x 5})
}
test_sym(data, a, b)
#Error in check_dot_cols(.vars, .cols) : object 'b' not found
CodePudding user response:
We can use across
test_strings <- function(data, ...) {
data %>%
mutate(across(all_of(c(...)),
~ .x 5))
}
-testing
> test_strings(data, "a", "b")
# A tibble: 10 × 3
a b c
<dbl> <dbl> <int>
1 6 26 101
2 7 27 102
3 8 28 103
4 9 29 104
5 10 30 105
6 11 31 106
7 12 32 107
8 13 33 108
9 14 34 109
10 15 35 110
If we want a general case, capture as symbols and convert to string
test_general <- function(data, ...) {
colnms <- purrr::map_chr(ensyms(...), rlang::as_string)
data %>%
mutate(across(all_of(colnms),
~ .x 5))
}
-testing
> test_general(data, a, b)
# A tibble: 10 × 3
a b c
<dbl> <dbl> <int>
1 6 26 101
2 7 27 102
3 8 28 103
4 9 29 104
5 10 30 105
6 11 31 106
7 12 32 107
8 13 33 108
9 14 34 109
10 15 35 110
> test_general(data, "a", "b")
# A tibble: 10 × 3
a b c
<dbl> <dbl> <int>
1 6 26 101
2 7 27 102
3 8 28 103
4 9 29 104
5 10 30 105
6 11 31 106
7 12 32 107
8 13 33 108
9 14 34 109
10 15 35 110