Search code examples
rr-factor

Order factor levels according to the order in which the levels appear in the data


This is part of a data frame I loaded from the internet using readHTMLtable:

head(tt,59)
    year         sport                      event      athlete_id  medal
1   1896 Track & Field                   100m Men      BURKETOM01   GOLD
2   1896 Track & Field                   100m Men      HOFMAFRI01 SILVER
3   1896 Track & Field                   100m Men       LANEFRA01 BRONZE
4   1896 Track & Field                   100m Men      SZOKOALA01 BRONZE
5   1896 Track & Field                   400m Men      BURKETOM01   GOLD
6   1896 Track & Field                   400m Men      JAMISHER01 SILVER
7   1896 Track & Field                   400m Men      GMELICHA01 BRONZE
8   1896 Track & Field                   800m Men      FLACKTED01   GOLD
9   1896 Track & Field                   800m Men D<C1>NIN<C1>N01 SILVER
10  1896 Track & Field                   800m Men      GOLEMDEM01 BRONZE
11  1896 Track & Field                  1500m Men      FLACKTED01   GOLD
12  1896 Track & Field                  1500m Men      BLAKEART01 SILVER
13  1896 Track & Field                  1500m Men      LERMUALB01 BRONZE
14  1896 Track & Field               Marathon Men      LOUISSPI01   GOLD
15  1896 Track & Field               Marathon Men      VASILCHA01 SILVER
16  1896 Track & Field               Marathon Men      KELLNGYU01 BRONZE
17  1896 Track & Field           110m Hurdles Men      CURTITOM01   GOLD
18  1896 Track & Field           110m Hurdles Men      GOULDGRA01 SILVER
19  1896 Track & Field              High Jump Men      CLARKELL01   GOLD
20  1896 Track & Field              High Jump Men      CONNOJAM01 SILVER
21  1896 Track & Field              High Jump Men      GARREBOB01 SILVER
22  1896 Track & Field             Pole Vault Men       HOYTBIL01   GOLD
23  1896 Track & Field             Pole Vault Men      TYLERALB01 SILVER
24  1896 Track & Field             Pole Vault Men      THEODIOA01 BRONZE
25  1896 Track & Field             Pole Vault Men      DAMASEVA01 BRONZE
26  1896 Track & Field              Long Jump Men      CLARKELL01   GOLD
27  1896 Track & Field              Long Jump Men      GARREBOB01 SILVER
28  1896 Track & Field              Long Jump Men      CONNOJAM01 BRONZE
29  1896 Track & Field            Triple Jump Men      CONNOJAM01   GOLD
30  1896 Track & Field            Triple Jump Men   TUFF<C8>ALE01 SILVER
31  1896 Track & Field            Triple Jump Men      PERSAIOA01 BRONZE
32  1896 Track & Field               Shot Put Men      GARREBOB01   GOLD
33  1896 Track & Field               Shot Put Men      GOUSKMIL01 SILVER
34  1896 Track & Field               Shot Put Men      PAPASGEO01 BRONZE
35  1896 Track & Field           Discus Throw Men      GARREBOB01   GOLD
36  1896 Track & Field           Discus Throw Men      PARASPAN01 SILVER
37  1896 Track & Field           Discus Throw Men      VERSISOT01 BRONZE
38  1896       Cycling 2000m Sprint (Scratch) Men      MASSOPAU01   GOLD
39  1896       Cycling 2000m Sprint (Scratch) Men      NIKOLSTA01 SILVER
40  1896       Cycling 2000m Sprint (Scratch) Men   FLAMEL<C9>O01 BRONZE
41  1896       Cycling   Individual Road Race Men      KONSTARI01   GOLD
42  1896       Cycling   Individual Road Race Men      GOEDRAUG01 SILVER
43  1896       Cycling   Individual Road Race Men      BATTEEDW01 BRONZE
44  1896       Cycling               One-Lap Race      MASSOPAU01   GOLD
45  1896       Cycling               One-Lap Race      NIKOLSTA01 SILVER
46  1896       Cycling               One-Lap Race      SCHMAADO01 BRONZE
47  1896       Cycling            10km Track Race      MASSOPAU01   GOLD
48  1896       Cycling            10km Track Race   FLAMEL<C9>O01 SILVER
49  1896       Cycling            10km Track Race      SCHMAADO01 BRONZE
50  1896       Cycling           100km Track Race   FLAMEL<C9>O01   GOLD
51  1896       Cycling           100km Track Race      KOLETGEO01 SILVER
52  1896       Cycling               12-Hour Race      SCHMAADO01   GOLD
53  1896       Cycling               12-Hour Race      KEEPIFRA01 SILVER
54  1896       Fencing           Foil, Individual      GRAVEEUG01   GOLD
55  1896       Fencing           Foil, Individual      CALLOHEN01 SILVER
56  1896       Fencing           Foil, Individual      PIERRPER01 BRONZE
57  1896       Fencing          Sabre, Individual      GEORGIOA01   GOLD
58  1896       Fencing          Sabre, Individual      KARAKTEL01 SILVER
59  1896       Fencing          Sabre, Individual      NIELSHOL01 BRONZE

As you can see the variable sport is a factor. When I check the levels this is what I get:

levels(tt$sport)
[1] "Cycling"       "Fencing"       "Gymnastics"    "Shooting"      "Swimming"      "Tennis" 
[7] "Track & Field" "Weightlifting" "Wrestling 

For some reason the order in which the levels appear does not match the order in the data frame. I am looking for a way in which using levels function will give me a list of the levels organized according to the first appearance, something like that:

levels(medals.df$tt)
[1] "Track & Field" "Cycling"       "Fencing"       "Gymnastics"    "Shooting"    "Swimming"
[7] "Tennis"        "Weightlifting" "Wrestling"

Now another thing to keep in mind is that the column sport is not in a "block design", meaning the first 59 rows have all the same values adjacent but it is not like this throughout the entire data frame.


Solution

  • Note that I had to tweak your dataset so that all the levels you list appear, and do so in the order you specified. From there, I wrote a simple function that outputs the levels in the order they appear in the dataset. The key is to use which (which lists the row numbers of observations that match a criterion), min (which selects the lowest value), and order (which tells you the order to use to go from the lowest to the highest).

    d <- read.table(text="rn    year    sport          event      athlete_id  medal
    1   1896 'Track & Field'                   '100m Men'      'BURKETOM01'   'GOLD'
    53  1896       'Cycling'               '12-Hour Race'      'KEEPIFRA01' 'SILVER'
    54  1896       'Fencing'           'Foil, Individual'      'GRAVEEUG01'   'GOLD'
    55  1896       'Gymnastics'           'Foil, Individual'      'CALLOHEN01' 'SILVER'
    56  1896       'Shooting'           'Foil, Individual'      'PIERRPER01' 'BRONZE'
    57  1896       'Swimming'          'Sabre, Individual'      'GEORGIOA01'   'GOLD'
    58  1896       'Tennis'          'Sabre, Individual'      'KARAKTEL01' 'SILVER'
    58  1896       'Weightlifting'          'Sabre, Individual'      'KARAKTEL01' 'SILVER'
    59  1896       'Wrestling'          'Sabre, Individual'      'NIELSHOL01' 'BRONZE'", 
                    header=T)
    
    levels(d$sport)
    # [1] "Cycling"       "Fencing"       "Gymnastics"    "Shooting"     
    # [5] "Swimming"      "Tennis"        "Track & Field" "Weightlifting"
    # [9] "Wrestling"    
    
    level.order <- function(var){
      l <- levels(var)
      o <- c()
      for(i in 1:length(l)){
        o[i] <- min(which(var==l[i]))
      }
      return(l[order(o)])
    }
    level.order(d$sport)
    # [1] "Track & Field" "Cycling"       "Fencing"       "Gymnastics"   
    # [5] "Shooting"      "Swimming"      "Tennis"        "Weightlifting"
    # [9] "Wrestling"    
    

    From here, if you wanted to change the default ordering (alphabetical) to the order the levels show up in the dataset, you would use factor. Consider:

    levels(d$sport)
    # [1] "Cycling"       "Fencing"       "Gymnastics"    "Shooting"     
    # [5] "Swimming"      "Tennis"        "Track & Field" "Weightlifting"
    # [9] "Wrestling"    
    d$sport <- factor(d$sport, levels=level.order(d$sport))
    levels(d$sport)
    # [1] "Track & Field" "Cycling"       "Fencing"       "Gymnastics"   
    # [5] "Shooting"      "Swimming"      "Tennis"        "Weightlifting"
    # [9] "Wrestling"