Search code examples
rdplyrforcats

Reordering factors by group using fct_relevel only change factor order in first group


Context: I need to use factor order to make arrange diplays a table in a certain way. I.e I want to get a "Total" line at the end of each group.

Problem: using fct_relevel I acheive the expected output only within the first group.

Repex:

library(dplyr)
library(forcats)

total_masses <- starwars %>% 
  group_by(species) %>% 
  summarise(mass = sum(mass),
            name = "Total mass")

df <- bind_rows(starwars, total_masses) %>% 
  select(species, name, mass) %>% 
  group_by(species) %>% 
  mutate(name = fct_relevel(name, "Total mass", after = Inf)) %>% 
  arrange(mass, name, species) 
df
#> # A tibble: 125 x 3
#> # Groups:   species [38]
#>    species        name                   mass
#>    <chr>          <fct>                 <dbl>
#>  1 Aleena         Ratts Tyerell            15
#>  2 Aleena         Total mass               15
#>  3 Yoda's species Total mass               17 # I expect this line to be at the end of the Yoda's species group
#>  4 Yoda's species Yoda                     17
#>  5 Ewok           Total mass               20
#>  6 Ewok           Wicket Systri Warrick    20
#>  7 Droid          R2-D2                    32
#>  8 Droid          R5-D4                    32
#>  9 Dug            Total mass               40
#> 10 Dug            Sebulba                  40
#> # ... with 115 more rows

Created on 2021-05-31 by the reprex package (v2.0.0)

I expect each Total mass to be at the end of each species category:

   species        name                   mass
   <chr>          <fct>                 <dbl>
 1 Aleena         Ratts Tyerell            15
 2 Aleena         Total mass               15
 3 Yoda's species Yoda                     17
 4 Yoda's species Total mass               17
etc...

I feel like the solution is very obvious but I was not able to find the right keywords to solve it... Any tip is welcome!


Solution

  • Then just do the fct_relevel without grouping, then group again and do anything you want within group.

    library(dplyr)
    library(forcats)
    
    total_masses <- starwars %>% 
      group_by(species) %>% 
      summarise(mass = sum(mass),
        name = "Total mass")
    
    # your codes
    df <- bind_rows(starwars, total_masses) %>% 
      select(species, name, mass) %>% 
      group_by(species) %>% 
      mutate(name = fct_relevel(name, "Total mass", after = Inf)) %>% 
      arrange(mass, name, species) 
    # Which result Total Mass is at 2nd so it will be above other levels
    levels(df$name)
    #>  [1] "Ratts Tyerell"         "Total mass"            "Dexter Jettster"      
    #>  [4] "Ki-Adi-Mundi"          "Mas Amedda"            "Zam Wesell"           
    #>  [7] "BB8"                   "C-3PO"                 "IG-88"                
    ...
    

    Instead of that you can ungroup then fct_relevel, then group_by then arrange.

    df <- bind_rows(starwars, total_masses) %>% 
      select(species, name, mass) %>% ungroup() %>%
      mutate(name = fct_relevel(name, "Total mass", after = Inf)) %>% 
      group_by(species) %>% 
      arrange(mass, name, species) 
    
    # Output
    df
    #> # A tibble: 125 x 3
    #> # Groups:   species [38]
    #>    species        name                   mass
    #>    <chr>          <fct>                 <dbl>
    #>  1 Aleena         Ratts Tyerell            15
    #>  2 Aleena         Total mass               15
    #>  3 Yoda's species Yoda                     17
    #>  4 Yoda's species Total mass               17
    #>  5 Ewok           Wicket Systri Warrick    20
    #>  6 Ewok           Total mass               20
    #>  7 Droid          R2-D2                    32
    #>  8 Droid          R5-D4                    32
    #>  9 Dug            Sebulba                  40
    #> 10 Dug            Total mass               40
    #> # … with 115 more rows
    
    # As you see now Total Mass is at the last item
    levels(df$name)
    #>  [1] "Ackbar"                "Adi Gallia"            "Anakin Skywalker"     
    #>  [4] "Arvel Crynyd"          "Ayla Secura"           "Bail Prestor Organa"  
    #>  [7] "Barriss Offee"         "BB8"                   "Ben Quadinaros"       
    #> [10] "Beru Whitesun lars"    "Bib Fortuna"           "Biggs Darklighter"    
    #> [13] "Boba Fett"             "Bossk"                 "C-3PO"                
    #> [16] "Captain Phasma"        "Chewbacca"             "Cliegg Lars"          
    #> [19] "Cordé"                 "Darth Maul"            "Darth Vader"          
    #> [22] "Dexter Jettster"       "Dooku"                 "Dormé"                
    #> [25] "Dud Bolt"              "Eeth Koth"             "Finis Valorum"        
    #> [28] "Finn"                  "Gasgano"               "Greedo"               
    #> [31] "Gregar Typho"          "Grievous"              "Han Solo"             
    #> [34] "IG-88"                 "Jabba Desilijic Tiure" "Jango Fett"           
    #> [37] "Jar Jar Binks"         "Jek Tono Porkins"      "Jocasta Nu"           
    #> [40] "Ki-Adi-Mundi"          "Kit Fisto"             "Lama Su"              
    #> [43] "Lando Calrissian"      "Leia Organa"           "Lobot"                
    #> [46] "Luke Skywalker"        "Luminara Unduli"       "Mace Windu"           
    #> [49] "Mas Amedda"            "Mon Mothma"            "Nien Nunb"            
    #> [52] "Nute Gunray"           "Obi-Wan Kenobi"        "Owen Lars"            
    #> [55] "Padmé Amidala"         "Palpatine"             "Plo Koon"             
    #> [58] "Poe Dameron"           "Poggle the Lesser"     "Quarsh Panaka"        
    #> [61] "Qui-Gon Jinn"          "R2-D2"                 "R4-P17"               
    #> [64] "R5-D4"                 "Ratts Tyerell"         "Raymus Antilles"      
    #> [67] "Rey"                   "Ric Olié"              "Roos Tarpals"         
    #> [70] "Rugor Nass"            "Saesee Tiin"           "San Hill"             
    #> [73] "Sebulba"               "Shaak Ti"              "Shmi Skywalker"       
    #> [76] "Sly Moore"             "Tarfful"               "Taun We"              
    #> [79] "Tion Medon"            "Wat Tambor"            "Watto"                
    #> [82] "Wedge Antilles"        "Wicket Systri Warrick" "Wilhuff Tarkin"       
    #> [85] "Yarael Poof"           "Yoda"                  "Zam Wesell"           
    #> [88] "Total mass"
    

    Created on 2021-05-31 by the reprex package (v2.0.0)