Réorganiser les niveaux d’un facteur sans changer l’ordre des valeurs

J’ai un bloc de données avec des variables numériques et des variables de factor catégoriques. L’ordre des niveaux pour ces facteurs n’est pas comme je le souhaite.

 numbers <- 1:4 letters <- factor(c("a", "b", "c", "d")) df <- data.frame(numbers, letters) df # numbers letters # 1 1 a # 2 2 b # 3 3 c # 4 4 d 

Si je change l’ordre des niveaux, les lettres ne sont plus avec leurs numéros correspondants (mes données sont un non-sens total à partir de ce moment).

 levels(df$letters) <- c("d", "c", "b", "a") df # numbers letters # 1 1 d # 2 2 c # 3 3 b # 4 4 a 

Je veux simplement changer l’ordre des niveaux , donc lors du traçage, les barres sont affichées dans l’ordre souhaité, ce qui peut différer de l’ordre alphabétique par défaut.

Utilisez l’argument de levels de factor :

 df <- data.frame(f = 1:4, g = letters[1:4]) df # fg # 1 1 a # 2 2 b # 3 3 c # 4 4 d levels(df$g) # [1] "a" "b" "c" "d" df$g <- factor(df$g, levels = letters[4:1]) # levels(df$g) # [1] "d" "c" "b" "a" df # fg # 1 1 a # 2 2 b # 3 3 c # 4 4 d 

un peu plus, juste pour le compte rendu

 ## reorder is a base function df$letters <- reorder(df$letters, new.order=letters[4:1]) library(gdata) df$letters <- reorder.factor(df$letters, letters[4:1]) 

Vous pouvez également trouver utile Relevel et combine_factor .

Donc, ce que vous voulez, dans Lexicon, c’est de ne modifier que les étiquettes pour une variable de facteur donnée (c.-à-d. laisser les données ainsi que les niveaux de facteurs inchangés).

 df$letters = factor(df$letters, labels=c("d", "c", "b", "a")) 

étant donné que vous ne souhaitez modifier que le mappage point à point et non le schéma de données ou de facteur (la manière dont les points de données sont regroupés dans des bacs individuels ou des valeurs de facteur, il peut être utile de savoir comment le facteur.

les règles sont simples:

  • les étiquettes sont mappées aux niveaux par la valeur d’index (c’est-à-dire que la valeur aux niveaux [2] est étiquetée, étiquette [2]);
  • les niveaux de facteur peuvent être définis explicitement en les passant via l’argument des niveaux ; ou
  • si aucune valeur n’est fournie pour l’argument levels, la valeur par défaut est utilisée, à savoir l’appel de résultat unique sur le vecteur de données transmis (pour l’argument data );
  • les étiquettes peuvent être définies explicitement via l’argument labels; ou
  • si aucune valeur n’est fournie pour l’argument étiquettes, la valeur par défaut est utilisée, c’est-à-dire uniquement le vecteur de niveaux

Faire face à des facteurs dans R est un travail assez particulier, je dois admettre … En réorganisant les niveaux de facteur, vous ne réorganisez pas les valeurs numériques sous-jacentes. Voici une petite démonstration:

 > numbers = 1:4 > letters = factor(letters[1:4]) > dtf <- data.frame(numbers, letters) > dtf numbers letters 1 1 a 2 2 b 3 3 c 4 4 d > sapply(dtf, class) numbers letters "integer" "factor" 

Maintenant, si vous convertissez ce facteur en numérique, vous obtiendrez:

 # return underlying numerical values 1> with(dtf, as.numeric(letters)) [1] 1 2 3 4 # change levels 1> levels(dtf$letters) <- letters[4:1] 1> dtf numbers letters 1 1 d 2 2 c 3 3 b 4 4 a # return numerical values once again 1> with(dtf, as.numeric(letters)) [1] 1 2 3 4 

Comme vous pouvez le voir … en changeant de niveau, vous ne modifiez que les niveaux (qui dirait, hein?), Pas les valeurs numériques! Mais, lorsque vous utilisez la fonction factor comme l’a suggéré @ Jonathan Chang, quelque chose de différent se produit: vous modifiez les valeurs numériques elles-mêmes.

Vous obtenez encore une fois une erreur parce que vous faites des levels et que vous essayez de le mettre en évidence avec un factor . Ne le fais pas !!! N’utilisez pas de levels ou vous risquez de tout gâcher (sauf si vous savez exactement ce que vous faites).

Une suggestion simple: évitez de nommer vos objects avec un nom identique à celui des objects de R ( df est la fonction de densité pour la dissortingbution F, les letters donnent des lettres alphabétiques minuscules). Dans ce cas particulier, votre code ne serait pas défectueux, mais parfois cela peut être … mais cela peut créer de la confusion, et nous ne le voulons pas, n’est-ce pas?!? =)

Au lieu de cela, utilisez quelque chose comme ça (je recommencerai dès le début):

 > dtf <- data.frame(f = 1:4, g = factor(letters[1:4])) > dtf fg 1 1 a 2 2 b 3 3 c 4 4 d > with(dtf, as.numeric(g)) [1] 1 2 3 4 > dtf$g <- factor(dtf$g, levels = letters[4:1]) > dtf fg 1 1 a 2 2 b 3 3 c 4 4 d > with(dtf, as.numeric(g)) [1] 4 3 2 1 

Notez que vous pouvez également vous nommer data.frame avec df et des letters au lieu de g , et le résultat sera OK. En fait, ce code est identique à celui que vous avez posté, seuls les noms sont modifiés. Ce factor(dtf$letter, levels = letters[4:1]) ne produirait pas d’erreur, mais cela peut être source de confusion!

Lisez attentivement le manuel du ?factor Quelle est la différence entre le factor(g, levels = letters[4:1]) et le factor(g, labels = letters[4:1]) ? Qu’est-ce qui est similaire dans les levels(g) <- letters[4:1] et g <- factor(g, labels = letters[4:1]) ?

Vous pouvez mettre la syntaxe de ggplot, donc nous pouvons vous aider plus sur celui-ci!

À votre santé!!!

Modifier:

ggplot2 nécessite réellement de changer les niveaux et les valeurs? Hm ... Je vais creuser celui-ci ...

Depuis la dernière fois que cette question a été posée, Hadley a publié son nouveau package forcats pour manipuler les facteurs et je trouve cela extrêmement utile. Exemples du bloc de données du PO:

 levels(df$letters) # [1] "a" "b" "c" "d" 

Pour inverser les niveaux:

 library(forcats) fct_rev(df$letters) %>% levels # [1] "d" "c" "b" "a" 

Pour append plus de niveaux:

 fct_expand(df$letters, "e") %>% levels # [1] "a" "b" "c" "d" "e" 

Et beaucoup d’autres fonctions fct_xxx() utiles.

Je souhaite append un autre cas où les niveaux pourraient être des chaînes portant des nombres avec des caractères spéciaux: comme dans l’exemple ci-dessous

 df <- data.frame(x = c("15-25", "0-4", "5-10", "11-14", "100+")) 

Les niveaux par défaut de x sont:

 df$x # [1] 15-25 0-4 5-10 11-14 100+ # Levels: 0-4 100+ 11-14 15-25 5-10 

Ici, si nous voulons réorganiser les niveaux de facteur en fonction de la valeur numérique, sans écrire explicitement les niveaux, nous pourrions faire

 library(gtools) df$x <- factor(df$x, levels = mixedsort(df$x)) df$x # [1] 15-25 0-4 5-10 11-14 100+ # Levels: 0-4 5-10 11-14 15-25 100+ as.numeric(df$x) # [1] 4 1 2 3 5 

J'espère que cela peut être considéré comme une information utile pour les futurs lecteurs.

Voici ma fonction pour réorganiser les facteurs d’un dataframe donné:

 reorderFactors <- function(df, column = "my_column_name", desired_level_order = c("fac1", "fac2", "fac3")) { x = df[[column]] lvls_src = levels(x) idxs_target <- vector(mode="numeric", length=0) for (target in desired_level_order) { idxs_target <- c(idxs_target, which(lvls_src == target)) } x_new <- factor(x,levels(x)[idxs_target]) df[[column]] <- x_new return (df) } 

Usage: reorderFactors(df, "my_col", desired_level_order = c("how","I","want"))