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

124

J'ai une base de données avec des variables numériques et des factorvariables catégorielles . L'ordre des niveaux de ces facteurs n'est pas celui que je 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 totalement absurdes à partir de maintenant).

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é - qui peut différer de l'ordre alphabétique par défaut.

crangos
la source
1
Quelqu'un pourrait-il me dire pourquoi l'affectation aux niveaux (...) change l'ordre des entrées dans le bloc de données, comme le montre crangos dans la question? Cela me semble terriblement peu intuitif et indésirable. J'ai passé du temps à déboguer moi-même un problème causé par cela aujourd'hui. Je pense qu'il pourrait y avoir une raison à ce comportement que je ne peux pas voir cependant, ou au moins une explication raisonnable pour pourquoi cela se produit.
Anton

Réponses:

120

Utilisez l' levelsargument de factor:

df <- data.frame(f = 1:4, g = letters[1:4])
df
#   f g
# 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
#   f g
# 1 1 a
# 2 2 b
# 3 3 c
# 4 4 d
Jonathan Chang
la source
1
Merci, cela a fonctionné. Pour une raison étrange, ggplot a maintenant correctement changé l'ordre dans la légende, mais pas dans l'intrigue. Bizarre.
crangos
7
ggplot2 m'a obligé à changer à la fois, l'ordre des niveaux (voir ci-dessus) et l'ordre des valeurs de la trame de données. df <- df [nrow (df): 1,] # reverse
crangos
@crangos, je pense que ggplot utilise l'ordre alphabétique des niveaux et ignore parfois les niveaux de facteurs personnalisés. Veuillez confirmer et inclure le numéro de version.
smci
22

un peu plus, juste pour mémoire

## 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 .

George Dontas
la source
2
Votre première réponse ne fonctionne pas pour moi. Mais ça marche:reorder(df$letters, seq(4,1))
Alex Holcombe
1
J'ai une situation très étrange où le «réapprovisionnement» fonctionne sur un ensemble de données, pas sur un autre. Sur l'autre jeu de données, il renvoie une erreur "Erreur dans tapply (X = X, INDEX = x, FUN = FUN, ...): l'argument" X "est manquant, sans valeur par défaut". Je ne sais pas quelle est la solution à ce problème. Je ne trouve aucune différence pertinente entre les ensembles de données.
CoderGuy123
10

Depuis que cette question était pour la dernière fois active, Hadley a publié son nouveau forcatspackage pour manipuler les facteurs et je le trouve scandaleusement utile. Exemples de la trame de données de l'OP:

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 ajouter plus de niveaux:

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

Et bien d'autres fct_xxx()fonctions utiles .

Joe
la source
Est-ce toujours disponible?
Joshua Rosenberg
1
Vous voulez écrire un code comme ceci: df %>% mutate(letters = fct_rev(letters)).
jazzurro
9

donc ce que vous voulez, dans le lexique R, est de ne changer que les étiquettes pour une variable de facteur donnée (c'est-à-dire, laissez les données ainsi que les niveaux de facteur , inchangés).

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

étant donné que vous souhaitez modifier uniquement le mappage point de données à étiquette et non les données ou le schéma de facteur (comment les points de données sont regroupés dans des groupes individuels ou des valeurs de facteur, il peut être utile de savoir comment le mappage est initialement défini lors de la création initiale le facteur.

les règles sont simples:

  • les étiquettes sont mappées aux niveaux par valeur d'index (c'est-à-dire que la valeur aux niveaux [2] reçoit l'étiquette, é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 niveaux, la valeur par défaut est utilisée qui est le résultat appelant unique sur le vecteur de données passé (pour l' argument de données );
  • les étiquettes peuvent être définies explicitement via l'argument labels; ou
  • si aucune valeur n'est fournie pour l'argument labels, la valeur par défaut est utilisée qui est juste le vecteur des niveaux
doug
la source
1
Je ne sais pas pourquoi ce n'est pas aussi voté que la réponse acceptée. C'est beaucoup plus informatif.
Rambatino
12
Si vous utilisez cette approche, vos données sont mal étiquetées.
Nazer
4
en fait ouais, je ne sais pas quoi faire avec ça, la réponse semble vouloir mal étiqueter les données par souci de traçage? Pouah. restauré à l'original. méfiez
rawr
7

Traiter les facteurs dans R est un travail assez particulier, je dois admettre ... Tout 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 changez que les niveaux (qui le dirait, hein?), Pas les valeurs numériques! Mais, lorsque vous utilisez la factorfonction comme @Jonathan Chang l'a suggéré, quelque chose de différent se produit: vous changez les valeurs numériques elles-mêmes.

Vous obtenez une fois de plus une erreur parce que vous le faites levelset essayez ensuite de la revaloriser factor. Ne fais pas ça !!! Ne pas utiliserlevels ou vous allez tout gâcher (sauf si vous savez exactement ce que vous faites).

Une petite suggestion: évitez de nommer vos objets avec un nom identique aux objets de R ( dfest la fonction de densité pour la distribution F, lettersdonne des lettres minuscules de l'alphabet). Dans ce cas particulier, votre code ne serait pas défectueux, mais parfois il peut l'être ... mais cela peut créer de la confusion, et nous ne voulons pas de cela, n'est-ce pas?!? =)

Au lieu de cela, utilisez quelque chose comme ça (je vais recommencer depuis le début):

> dtf <- data.frame(f = 1:4, g = factor(letters[1:4]))
> dtf
  f g
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
  f g
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.frameavec dfet lettersau 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. Cette partiefactor(dtf$letter, levels = letters[4:1]) ne ferait pas d'erreur, mais elle peut être déroutante!

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

Vous pouvez mettre la syntaxe ggplot, afin que nous puissions vous aider davantage sur celui-ci!

À votre santé!!!

Éditer:

ggplot2nécessite réellement de changer les niveaux et les valeurs? Hm ... je vais creuser celui-ci ...

aL3xa
la source
3

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

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

Les niveaux par défaut de xsont:

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, ce que nous pourrions faire est

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.

joel.wilson
la source
0

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"))

Boern
la source