Reputation: 25
From my point of view, this seems like a fairly easy thing to do, but I'm having such trouble trying to get this to work.
What I'm trying to do, via mutate()
and across()
, is simply to make some variables just evaluate to a constant or a scalar within a custom function.
I'll demonstrate this with a non-across()
implementation:
library(dplyr, warn.conflicts = FALSE)
vars <- c("mpg", "cyl")
Test_Function <- function(.data, .vars) {
Data_Frame <- .data %>%
mutate(
!!.vars[1] := "Something",
!!.vars[2] := 2L
) %>%
select({{.vars}}) %>%
as_tibble()
return(Data_Frame)
}
mtcars %>% Test_Function(vars)
#> # A tibble: 32 x 2
#> mpg cyl
#> <chr> <int>
#> 1 Something 2
#> 2 Something 2
#> 3 Something 2
#> 4 Something 2
#> 5 Something 2
#> 6 Something 2
#> 7 Something 2
#> 8 Something 2
#> 9 Something 2
#> 10 Something 2
#> # ... with 22 more rows
Created on 2021-08-11 by the reprex package (v2.0.1)
When I try to get the same thing accomplished via across()
, I can't get it to work:
library(dplyr, warn.conflicts = FALSE)
vars <- c("mpg", "cyl")
Test_Function <- function(.data, .vars) {
Data_Frame <-
.data %>%
mutate(
across(!!.vars, "Something"), # Doesn't work
across(!!.vars, ~ .x := "Something"), # purrr-style doesn't work
across(!!.vars, ~ assign(.x, "Something")), # purrr-style with assign() doesn't work
across(!!.vars, assign, "Something") # Regular assign() doesn't work
) %>%
select({{.vars}}) %>%
as_tibble()
return(Data_Frame)
}
mtcars %>% Test_Function(vars)
As an aside, I wish I could provide across()
some variables that don't exist in .data
so I can easily create new columns without having to do it manually inside of mutate()
, but as of the time of writing that doesn't work (yet?).
EDIT:
@MrFlick/@JonSpring,and @TimTeaFan gave suggestions that worked for me; the former for just one value, the latter for a list of values. I'll provide minimal reprexes for both of what I went with. Thanks everyone for the help!
@MrFlick/@JonSpring's suggestion, returning a single value:
library(dplyr, warn.conflicts = FALSE)
vars <- c("mpg", "cyl")
Test_Function <- function(.data, .vars) {
Data_Frame <-
.data %>%
mutate(
across(!!.vars, ~ "Something"), # I was so close to this initially, I just missed the tilde.
) %>%
select({{.vars}}) %>%
as_tibble()
return(Data_Frame)
}
mtcars %>% Test_Function(vars)
#> # A tibble: 32 x 2
#> mpg cyl
#> <chr> <chr>
#> 1 Something Something
#> 2 Something Something
#> 3 Something Something
#> 4 Something Something
#> 5 Something Something
#> 6 Something Something
#> 7 Something Something
#> 8 Something Something
#> 9 Something Something
#> 10 Something Something
#> # ... with 22 more rows
Created on 2021-08-12 by the reprex package (v2.0.1)
And now @TimTeaFan's suggestion:
library(dplyr, warn.conflicts = FALSE)
varlist <-
list(
"mpg" = "Something",
"cyl" = 2L
)
vars <- c("mpg", "cyl")
Test_Function <- function(.data, .varlist, .vars) {
Data_Frame <-
.data %>%
mutate(
!!! .varlist # I haven't really undstood the big-bang operator (!!!) before now, so this was a great demonstration!
) %>%
select(!!.vars) %>%
as_tibble()
return(Data_Frame)
}
mtcars %>% Test_Function(varlist, vars)
#> # A tibble: 32 x 2
#> mpg cyl
#> <chr> <int>
#> 1 Something 2
#> 2 Something 2
#> 3 Something 2
#> 4 Something 2
#> 5 Something 2
#> 6 Something 2
#> 7 Something 2
#> 8 Something 2
#> 9 Something 2
#> 10 Something 2
#> # ... with 22 more rows
Created on 2021-08-12 by the reprex package (v2.0.1)
Upvotes: 1
Views: 110
Reputation: 18551
If you just want to assign scalars to a variable, then you neither need across
nor a function to do so. You can just define a list and use the triple bang operator inside dplyr::mutate
or, more close to your case, dplyr::transmute
.
library(dplyr, warn.conflicts = FALSE)
vars <- list("mpg" = "something",
"cyl" = 2L)
mtcars <- head(mtcars)
mtcars %>%
transmute(!!! vars)
#> mpg cyl
#> Mazda RX4 something 2
#> Mazda RX4 Wag something 2
#> Datsun 710 something 2
#> Hornet 4 Drive something 2
#> Hornet Sportabout something 2
#> Valiant something 2
If you want to create new columns programmatically in an across
like way, then an easy {tidyverse} trick is to use purrr::map_dfc
inside dplyr::mutate
. Note that in the example below you can use any other function even expressions depending on other column names:
library(purrr)
mtcars %>%
transmute(map_dfc(vars, ~ .x))
#> mpg cyl
#> Mazda RX4 something 2
#> Mazda RX4 Wag something 2
#> Datsun 710 something 2
#> Hornet 4 Drive something 2
#> Hornet Sportabout something 2
#> Valiant something 2
I also have a package on github, {dplyover} which has a function over
that uses dplyr::across
's syntax to loop over a vector creating new columns similar to purrr::map_dfc
. You can create column names on the fly using the .names
argument.
library(dplyover) # https://github.com/TimTeaFan/dplyover
mtcars %>%
transmute(over(vars, ~ .x,
.names = "{x}_new"))
#> mpg_new cyl_new
#> Mazda RX4 something 2
#> Mazda RX4 Wag something 2
#> Datsun 710 something 2
#> Hornet 4 Drive something 2
#> Hornet Sportabout something 2
#> Valiant something 2
Created on 2021-08-11 by the reprex package (v2.0.1)
Upvotes: 1