web-dev-qa-db-fra.com

Réordonne 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 catégoriques factor. L'ordre des niveaux pour ces facteurs n'est pas ce que je veux qu'ils soient. 

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 alors un non-sens total).

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 level. Ainsi, 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.

105
crangos

Utilisez l'argument levels 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
107
Jonathan Chang

un peu plus, juste pour l'enregistrement

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

19
George Dontas

donc, ce que vous voulez, dans R Lexicon, est de ne changer que leslabelspour une variable de facteur donnée (c.-à-d., laissez les données ainsi que le facteur niveaux, inchangées).

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

étant donné que vous souhaitez modifier uniquement le mappage datapoint-to-label et non les données ou le schéma factoriel (comment les points de données sont-ils regroupés dans des groupes individuels ou des valeurs factorielles, il peut être utile de savoir comment le mappage est défini à l'origine lorsque vous créez le facteur pour la première fois.

les règles sont simples:

  • les étiquettes sont mappées aux niveaux par la valeur d'index (c'est-à-dire que la valeurat niveaux [2] reçoit l'étiquette, étiquette [2]);
  • les niveaux de facteurs peuvent être définis explicitement en les passant via l'argument levels; ou
  • si aucune valeur n'est fournie pour l'argument levels, la valeur par défaut est utilisée, qui correspond au résultat appelant 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 labels, la valeur par défaut est used, qui est uniquement le vecteur levels
7
doug

Traiter avec les facteurs dans R est un travail assez particulier, je dois l'avouer ... Pendant que vous réorganisez les niveaux de facteurs, 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 de niveau (qui dirait, non?), 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 levels puis essayez de la relier avec factor. Ne le fais pas !!! Faire ne pas utilisez levels ou vous fouillerez les choses (à moins que vous ne sachiez exactement ce que vous faites). 

Une petite suggestion: évitez de nommer vos objets avec un nom identique à celui de R (df est une fonction de densité pour la distribution F, letters donne des lettres de l'alphabet minuscule). Dans ce cas particulier, votre code ne serait pas défectueux, mais il peut parfois l'ê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 ceci (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.frame avec df et letters au lieu de g et le résultat sera OK. En réalité, ce code est identique à celui que vous avez posté, seuls les noms sont changés. Cette partie factor(dtf$letter, levels = letters[4:1]) ne génère pas d'erreur, mais elle peut être déconcertante!

Lisez attentivement le manuel ?factor! 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 utiliser la syntaxe ggplot pour que nous puissions vous aider davantage sur celui-ci!

À votre santé!!!

Modifier:

ggplot2 nécessite en réalité de changer les niveaux et les valeurs? Hm ... je vais creuser celui-ci ...

6
aL3xa

Depuis que cette question était active, Hadley a publié son nouveau paquet forcats pour manipuler les facteurs et je le trouve scandaleusement utile. Exemples tirés 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 ajouter plus de niveaux:

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

Et bien d'autres fonctions fct_xxx() utiles.

4
Joe

Je souhaite ajouter un autre cas où les niveaux pourraient être des chaînes portant des chiffres accompagnés de caractères spéciaux: 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 facteurs en fonction de la valeur numérique, sans écrire explicitement les niveaux, nous pouvons 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. 

3
joel.wilson

Voici ma fonction pour réorganiser les facteurs d'une image donnée:

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

0
Boern