1
votes

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)

1
What's the desired result when you run your function with across(). Do you just want all the columns to be "Something"? You still need to use := for new variable names. across() can't help you with that.MrFlick
I'm not sure I understand what across() would add here. Does mutate(across(!!.vars, ~paste("Something"))) do what you want?Jon Spring
@JonSpring, actually you don't even need the paste(), right? Just mutate(across(!!.vars, ~"Something"))MrFlick
Yes, good point!Jon Spring
@MrFlick Your 2nd suggestion worked! I'll add it to my original post! Thanks! I was so damn close initally.nmck160

1 Answers

1
votes

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)