Riordina i livelli di un fattore senza modificare l'ordine dei valori


124

Ho un data frame con alcune variabili numeriche e alcune factorvariabili categoriali . L'ordine dei livelli per questi fattori non è come voglio che siano.

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

Se cambio l'ordine dei livelli, le lettere non sono più con i numeri corrispondenti (i miei dati sono una sciocchezza totale da questo punto in poi).

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

Voglio semplicemente cambiare l' ordine dei livelli , quindi durante la stampa le barre vengono mostrate nell'ordine desiderato, che potrebbe differire dall'ordine alfabetico predefinito.


1
Qualcuno potrebbe darmi un suggerimento sul motivo per cui l'assegnazione ai livelli (...) cambia l'ordine delle voci nel data frame, come mostra crangos nella domanda? Mi sembra terribilmente poco intuitivo e indesiderato. Oggi ho passato un po 'di tempo a eseguire il debug di un problema causato da questo. Penso che potrebbe esserci una ragione per questo comportamento che non riesco a vedere, o almeno una spiegazione ragionevole del motivo per cui accade.
Anton

Risposte:


120

Usa l' levelsargomento di 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

1
Grazie, ha funzionato. Per qualche strana ragione ggplot ora ha cambiato correttamente l'ordine nella legenda, ma non nella trama. Strano.
crangos

7
ggplot2 mi ha richiesto di cambiare sia l'ordine dei livelli (vedi sopra) che l'ordine dei valori del data frame. df <- df [nrow (df): 1,] # reverse
crangos

@crangos, penso che ggplot utilizzi l'ordine alfabetico dei livelli e talvolta ignora i livelli dei fattori personalizzati. Conferma e includi il numero di versione.
smci

22

ancora un po ', solo per la cronaca

## 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])

Potresti anche trovare utili Relevel e combination_factor .


2
La tua prima risposta non funziona per me. Ma funziona:reorder(df$letters, seq(4,1))
Alex Holcombe

1
Ho una situazione molto strana in cui il "riordino" funziona su un set di dati, non su un altro. Sull'altro set di dati, genera un errore "Errore in tapply (X = X, INDEX = x, FUN = FUN, ...): argomento" X "mancante, senza impostazione predefinita". Non sono sicuro di quale sia la soluzione a questo problema. Non riesco a trovare alcuna differenza rilevante tra i set di dati.
CoderGuy123

10

Dall'ultima volta che questa domanda è stata attiva, Hadley ha rilasciato il suo nuovo forcatspacchetto per la manipolazione dei fattori e lo trovo scandalosamente utile. Esempi dal data frame dell'OP:

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

Per invertire i livelli:

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

Per aggiungere più livelli:

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

E molte altre utili fct_xxx()funzioni.


È ancora disponibile?
Joshua Rosenberg

1
Si vuole scrivere un codice come questo: df %>% mutate(letters = fct_rev(letters)).
jazzurro

9

quindi quello che vuoi, nel lessico R, è cambiare solo le etichette per una data variabile fattore (cioè, lasciare i dati così come i livelli dei fattori , invariati).

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

dato che si desidera modificare solo la mappatura datapoint-label dati e non i dati o lo schema dei fattori (il modo in cui i punti dati sono raggruppati in bin individuali o valori dei fattori, potrebbe essere utile sapere come la mappatura è originariamente impostata quando si crea inizialmente il fattore.

le regole sono semplici:

  • le etichette sono mappate ai livelli in base al valore dell'indice (cioè, al valore ai livelli [2] viene assegnata l'etichetta, etichetta [2]);
  • i livelli dei fattori possono essere impostati esplicitamente passandoli tramite l' argomento livelli ; o
  • se non viene fornito alcun valore per l'argomento livelli, viene utilizzato il valore predefinito che è il risultato della chiamata univoca sul vettore dati passato (per l' argomento dati );
  • le etichette possono essere impostate esplicitamente tramite l'argomento etichette; o
  • se non viene fornito alcun valore per l'argomento delle etichette, viene utilizzato il valore predefinito che è solo il vettore dei livelli

1
Non so perché questo non sia così votato come la risposta accettata. Questo è molto più informativo.
Rambatino

12
Se utilizzi questo approccio, i tuoi dati sono etichettati in modo errato.
Nazer

4
in realtà sì non so cosa fare con questo, la risposta sembra voler etichettare erroneamente i dati per motivi di trama? ugh. ripristinato all'originale. utenti attenti
rawr

7

Affrontare i fattori in R è un lavoro piuttosto peculiare, devo ammetterlo ... Mentre riordini i livelli dei fattori, non stai riordinando i valori numerici sottostanti. Ecco una piccola dimostrazione:

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

Ora, se converti questo fattore in numerico, otterrai:

# 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

Come puoi vedere ... cambiando i livelli, cambi solo i livelli (chi lo direbbe, eh?), Non i valori numerici! Ma, quando usi la factorfunzione come suggerito da @Jonathan Chang, succede qualcosa di diverso: cambi i valori numerici stessi.

Stai ricevendo di nuovo un errore perché lo fai levelse poi provi a rilanciarlo con factor. Non farlo !!! Evitare Non utilizzare levelso ti complicare le cose (a meno che non si sa esattamente cosa si sta facendo).

Un piccolo suggerimento: evita di nominare i tuoi oggetti con un nome identico agli oggetti di R ( dfè la funzione di densità per la distribuzione F, lettersfornisce lettere dell'alfabeto minuscole). In questo caso particolare, il tuo codice non sarebbe difettoso, ma a volte può essere ... ma questo può creare confusione, e non lo vogliamo, vero?!? =)

Invece, usa qualcosa di simile (vado dall'inizio ancora una volta):

> 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

Nota che puoi anche nominarti data.framecon dfe lettersinvece di g, e il risultato sarà OK. In realtà, questo codice è identico a quello che hai pubblicato, cambiano solo i nomi. Questa partefactor(dtf$letter, levels = letters[4:1]) non genererebbe un errore, ma può essere fonte di confusione!

Leggi ?factorattentamente il manuale! Qual è la differenza tra factor(g, levels = letters[4:1])e factor(g, labels = letters[4:1])? Cosa c'è di simile in levels(g) <- letters[4:1]e g <- factor(g, labels = letters[4:1])?

Puoi inserire la sintassi ggplot, così possiamo aiutarti di più su questo!

Saluti!!!

Modificare:

ggplot2richiede effettivamente di modificare sia i livelli che i valori? Hm ... lo tirerò fuori questo ...


3

Vorrei aggiungere un altro caso in cui i livelli potrebbero essere stringhe che trasportano numeri insieme ad alcuni caratteri speciali: come nell'esempio sotto

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

I livelli predefiniti di xè:

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

Qui se vogliamo riordinare i livelli dei fattori in base al valore numerico, senza scrivere esplicitamente i livelli, quello che potremmo fare è

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

Spero che questa possa essere considerata un'informazione utile per i futuri lettori.


0

Ecco la mia funzione per riordinare i fattori di un determinato dataframe:

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

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.