Search code examples
rdplyrr-factor

Can I use factors as search and replace values in mutate?


I have a list of values that I want to substitute with values from another list, wherever they appear. So for example, wherever "white" appears as a hair color, I want to use "light"; wherever "auburn" appears I want to use "brown. I can accomplish that with:

find_text = c("white", "auburn", "none")
replace_text = c("light", "brown", "bald")

starwars %>%
  filter(gender == "feminine") %>% 
  select(c("name", "hair_color", "species")) %>%
  mutate(hair_color = str_replace(hair_color, find_text[1], replace_text[1]),
         hair_color = str_replace(hair_color, find_text[2], replace_text[2]),
         hair_color = str_replace(hair_color, find_text[3], replace_text[3]),
         )

I thought I could use fct_recode() but that also appears to require a single named string. Is there a cleaner way to go about this?


Solution

  • We can use a named vector to replace with str_replace. Here, it is assumed that we want to replace substring i.e. match and replace a substring rather than a fixed string replacement

    library(dplyr)
    library(stringr)
    starwars %>%
       filter(gender == "feminine") %>% 
       select(c("name", "hair_color", "species")) %>%
       mutate(hair_color_new = str_replace_all(hair_color, 
              set_names(replace_text, find_text)))
    # A tibble: 17 x 4
    #   name               hair_color species    hair_color_new
    #   <chr>              <chr>      <chr>      <chr>         
    # 1 Leia Organa        brown      Human      brown         
    # 2 Beru Whitesun lars brown      Human      brown         
    # 3 Mon Mothma         auburn     Human      brown         
    # 4 Shmi Skywalker     black      Human      black         
    # 5 Ayla Secura        none       Twi'lek    bald          
    # 6 Adi Gallia         none       Tholothian bald          
    # 7 Cordé              brown      Human      brown         
    # 8 Luminara Unduli    black      Mirialan   black         
    # 9 Barriss Offee      black      Mirialan   black         
    #10 Dormé              brown      Human      brown         
    #11 Zam Wesell         blonde     Clawdite   blonde        
    #12 Taun We            none       Kaminoan   bald          
    #13 Jocasta Nu         white      Human      light         
    #14 R4-P17             none       Droid      bald          
    #15 Shaak Ti           none       Togruta    bald          
    #16 Rey                brown      Human      brown         
    #17 Padmé Amidala      brown      Human      brown    
    

    If we want fixed matches, then recode is also useful

    starwars %>%
        filter(gender == "feminine") %>% 
        select(c("name", "hair_color", "species"))  %>% 
        mutate(hair_color_new = recode(hair_color, !!! set_names(replace_text, find_text)))
    # A tibble: 17 x 4
    #   name               hair_color species    hair_color_new
    #   <chr>              <chr>      <chr>      <chr>         
    # 1 Leia Organa        brown      Human      brown         
    # 2 Beru Whitesun lars brown      Human      brown         
    # 3 Mon Mothma         auburn     Human      brown         
    # 4 Shmi Skywalker     black      Human      black         
    # 5 Ayla Secura        none       Twi'lek    bald          
    # 6 Adi Gallia         none       Tholothian bald          
    # 7 Cordé              brown      Human      brown         
    # 8 Luminara Unduli    black      Mirialan   black         
    # 9 Barriss Offee      black      Mirialan   black         
    #10 Dormé              brown      Human      brown         
    #11 Zam Wesell         blonde     Clawdite   blonde        
    #12 Taun We            none       Kaminoan   bald          
    #13 Jocasta Nu         white      Human      light         
    #14 R4-P17             none       Droid      bald          
    #15 Shaak Ti           none       Togruta    bald          
    #16 Rey                brown      Human      brown         
    #17 Padmé Amidala      brown      Human      brown