Search code examples
rpipedplyrforcatsacross

Updating fct_relevel solution since across took over mutate_at


The solution to piping fct_relevel to mutate a subset of variables no longer works since mutate_at has been deprecated. I'm having a tough time piecing together an updated solution using mutate(across(...), fct_relevel(., ....) , since I get an error that

"Problem with `mutate()` input `..2`.
i `..2 = ~...`.
x `..2` must be a vector, not a `formula` object."

but there is no formula

Let's say I have

f1 <- factor(c("a", "b", "c", "d"))
g2 <- factor(c("a", "b", "c", "d"))
t3 <- factor(c("a", "b", "c", "d"))
f5 <- factor(c("m", "l", "f", "p"))
y8 <- factor(c("a", "b", "c", "d"))
df <- tibble(f1, g2, t3, f5, y8)

and I want to fct_relevel f1, g2, and y8 (so non-sequential variables) so that you have the factors ordered as "c", "d", "b", "a" for all three of these variables.


Solution

  • Probably this:

    library(dplyr) # 1.0.7
    library(forcats) # 0.5.1
    
    f1 <- factor(c("a", "b", "c", "d"))
    g2 <- factor(c("a", "b", "c", "d"))
    t3 <- factor(c("a", "b", "c", "d"))
    f5 <- factor(c("m", "l", "f", "p"))
    y8 <- factor(c("a", "b", "c", "d"))
    df <- tibble(f1, g2, t3, f5, y8)
    
    
    df_1 <- df %>%
      mutate(across(c(f1, g2, y8), forcats::fct_relevel, "c", "d", "b", "a"))
    
    levels(df$f1)
    #> [1] "c" "d" "b" "a"
    levels(df$g2)
    #> [1] "c" "d" "b" "a"
    levels(df$t3)
    #> [1] "a" "b" "c" "d"
    levels(df$f5)
    #> [1] "f" "l" "m" "p"
    levels(df$y8)
    #> [1] "c" "d" "b" "a"
    

    Created on 2021-07-25 by the reprex package (v2.0.0)

    Below I add two more syntaxes that are equivalent to the one above.

    # option 2
    df_2 <- df %>%
      mutate(across(c(f1, g2, y8), ~forcats::fct_relevel(., "c", "d", "b", "a")))
    
    # option 3
    df_3 <- df %>%
      mutate(across(c(f1, g2, y8), ~forcats::fct_relevel(.x, "c", "d", "b", "a")))
    
    identical(df_1, df_2) # TRUE 
    identical(df_1, df_3) # TRUE
    

    The way the above fct_relevel() operates is that it treats each column (i.e., f1, g2, y8) as a vector passed to fct_relevel, plugged in instead of the . or .x. It "knows" to do such "plugging" because we use the ~ right before the function.