nmck160
nmck160

Reputation: 25

Evaluating constants/scalars inside `dplyr`'s `across()`

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

Answers (1)

TimTeaFan
TimTeaFan

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

Related Questions