tidyverse/forcats

`fct_relevel()` should gain `fct_recode()` functionality

Closed this issue · 3 comments

Hi,

Whenever I use fct_relevel(), I always feel like it should have some of fct_recode() functionalities:

library(forcats)
x <- fct(c("apple", "bear", "apple", "bear"))
x
#> [1] apple bear  apple bear 
#> Levels: apple bear
fct_recode(x, animal = "bear", fruit = "apple")
#> [1] fruit  animal fruit  animal
#> Levels: fruit animal
fct_relevel(x, animal = "bear", fruit = "apple")
#> [1] apple bear  apple bear 
#> Levels: bear apple

# expected output:
x %>% 
  fct_recode(animal = "bear", fruit = "apple") %>% 
  fct_relevel("animal", "fruit")
#> [1] fruit  animal fruit  animal
#> Levels: animal fruit

Created on 2022-10-17 with reprex v2.0.2

I don't think this could break any existing code, but if this is very unfortunately not achievable, I would at least expect some warning saying that fct_relevel() is not expecting a named ellipsis.

I think check_dots_unnamed() is the best option for now. At some point it seems like we should rethink the design of the forcats functions, but now doesn't feel like the time.

@DanChaltiel There was a great function that did exactly what I think you (and a few others) have requested at #45 (comment). We were using it a lot until @hadley's "fix" here broke it, though I admit it did require knowing what you were doing and relied on the fact that the names were passed through unchanged so maybe the news quote is only half unfair.

I can give you this version which is working for me, although it feels a bit more brittle

library(forcats)

fct_reorg = function(fac, ...) {
  fct_relevel(fct_recode(fac, ...), names(rlang::dots_list(...)))
}

x <- fct(c("apple", "bear", "apple", "bear"))
x
#> [1] apple bear  apple bear 
#> Levels: apple bear
fct_recode(x, animal = "bear", fruit = "apple")
#> [1] fruit  animal fruit  animal
#> Levels: fruit animal

# expected output:
out_pipe = x %>% 
  fct_recode(animal = "bear", fruit = "apple") %>% 
  fct_relevel("animal", "fruit")
out_pipe 
#> [1] fruit  animal fruit  animal
#> Levels: animal fruit

# or using the function

out_reorg = fct_reorg(x, animal = "bear", fruit = "apple")
out_reorg
#> [1] fruit  animal fruit  animal
#> Levels: animal fruit

identical(out_pipe, out_reorg)
#> [1] TRUE

# works with splicing
vars2recode_and_relevel = c(animal = "bear", fruit = "apple")
out_splice = fct_reorg(x, !!!vars2recode_and_relevel)
out_splice
#> [1] fruit  animal fruit  animal
#> Levels: animal fruit

identical(out_pipe, out_splice)
#> [1] TRUE

Created on 2023-05-15 with reprex v2.0.2

I agree that there should be a forcats function that can do this properly.

Thanks for the function, that will be very helpful :-)

At some point it seems like we should rethink the design of the forcats functions, but now doesn't feel like the time.

I guess we will have to wait a bit more until it becomes part of forcats though.