Domanda Pulizia dei livelli dei fattori (riduzione di più livelli / etichette)


Qual è il modo più efficace (cioè efficiente / appropriato) per ripulire un fattore contenente più livelli che devono essere compressi? Cioè, come combinare due o più livelli di fattore in uno.

Ecco un esempio in cui i due livelli "Sì" e "Y" dovrebbero essere compressi su "Sì" e "No" e "N" collassati su "No":

## Given: 
x <- c("Y", "Y", "Yes", "N", "No", "H")   # The 'H' should be treated as NA

## expectedOutput
[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No  # <~~ NOTICE ONLY **TWO** LEVELS

Un'opzione è ovviamente quella di pulire le stringhe prima di usare la mano sub e amici.

Un altro metodo è consentire l'etichetta duplicata, quindi rilasciarli

## Duplicate levels ==> "Warning: deprecated"
x.f <- factor(x, levels=c("Y", "Yes", "No", "N"), labels=c("Yes", "Yes", "No", "No"))

## the above line can be wrapped in either of the next two lines
factor(x.f)      
droplevels(x.f) 

Però, c'è un modo più efficace?


Mentre so che il levels e labels gli argomenti dovrebbero essere i vettori, ho sperimentato con liste e liste nominate e ho chiamato i vettori per vedere cosa succede Inutile dire che nessuno dei seguenti mi ha avvicinato al mio obiettivo.

  factor(x, levels=list(c("Yes", "Y"), c("No", "N")), labels=c("Yes", "No"))
  factor(x, levels=c("Yes", "No"), labels=list(c("Yes", "Y"), c("No", "N")))

  factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))
  factor(x, levels=c("Y", "Yes", "No", "N"), labels=c(Yes="Y", Yes="Yes", No="No", No="N"))
  factor(x, levels=c("Yes", "No"), labels=c(Y="Yes", Yes="Yes", No="No", N="No"))

44
2017-10-16 17:37


origine


risposte:


Usa il levels funzione, e passargli una lista con nome, con i nomi come nomi desiderati dei livelli e gli elementi come nomi correnti che dovrebbero essere rinominati.

x <- c("Y", "Y", "Yes", "N", "No", "H")
x <- factor(x)
levels(x) <- list(Yes=c("Y", "Yes"), No=c("N", "No"))
x
## [1] Yes  Yes  Yes  No   No   <NA>
## Levels: Yes No

Come menzionato nel levels documentazione; anche vedere gli esempi lì.

valore: per il metodo 'fattore', a             vettore di stringhe di caratteri con lunghezza almeno il numero             di livelli di 'x' o una lista nominata che specifica come rinominare             i livelli.

Questo può anche essere fatto in una riga, come Marek fa qui: https://stackoverflow.com/a/10432263/210673; il levels<- la stregoneria è spiegata qui https://stackoverflow.com/a/10491881/210673.

> `levels<-`(factor(x), list(Yes=c("Y", "Yes"), No=c("N", "No")))
[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No

66
2017-10-16 17:46



Come la domanda è intitolata Pulizia dei livelli dei fattori (riduzione di più livelli / etichette), il forcats il pacchetto dovrebbe essere menzionato anche qui, per motivi di completezza. forcats è apparso su CRAN nell'agosto 2016.

Sono disponibili diverse funzioni di comodità per la pulizia dei livelli dei fattori:

x <- c("Y", "Y", "Yes", "N", "No", "H") 

library(forcats)

Comprimi i livelli dei fattori in gruppi definiti manualmente

fct_collapse(x, Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes

Cambia i livelli dei fattori a mano

fct_recode(x, Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes

Riattiva automaticamente i livelli dei fattori, collassa se necessario

fun <- function(z) {
  z[z == "Y"] <- "Yes"
  z[z == "N"] <- "No"
  z[!(z %in% c("Yes", "No"))] <- NA
  z
}
fct_relabel(factor(x), fun)
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: No Yes

Nota che fct_relabel() funziona con livelli di fattore, quindi si aspetta un fattore come prima discussione. Le altre due funzioni, fct_collapse() e fct_recode(), accetta anche a carattere vettoriale che è una funzionalità non documentata.

Riordina i livelli dei fattori dalla prima apparizione

L'output atteso dato dall'OP è

[1] Yes  Yes  Yes  No   No   <NA>
Levels: Yes No

Qui i livelli sono ordinati così come appaiono x che è diverso dal default (?factor: I livelli di un fattore sono ordinati per impostazione predefinita).

Per essere in linea con l'output atteso, questo può essere ottenuto usando fct_inorder()  prima collassando i livelli:

fct_collapse(fct_inorder(x), Yes = c("Y", "Yes"), No = c("N", "No"), NULL = "H")
fct_recode(fct_inorder(x), Yes = "Y", Yes = "Yes", No = "N", No = "No", NULL = "H")

Entrambi restituiscono l'output atteso con livelli nello stesso ordine, ora.


13
2018-04-13 06:27



Forse un vettore nominato come una chiave potrebbe essere utile:

> factor(unname(c(Y = "Yes", Yes = "Yes", N = "No", No = "No", H = NA)[x]))
[1] Yes  Yes  Yes  No   No   <NA>
Levels: No Yes

Questo sembra molto simile al tuo ultimo tentativo ... ma questo funziona :-)


6
2017-10-16 17:40



Un altro modo è quello di creare una tabella contenente la mappatura:

# stacking the list from Aaron's answer
fmap = stack(list(Yes = c("Y", "Yes"), No = c("N", "No")))

fmap$ind[ match(x, fmap$values) ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

# or...

library(data.table)
setDT(fmap)[x, on=.(values), ind ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

Preferisco in questo modo, poiché lascia un oggetto facilmente ispezionato che riassume la mappa; e il codice data.table sembra proprio come qualsiasi altro membro di quella sintassi.


Certo, se non vuoi un oggetto come fmap riassumendo il cambiamento, può essere un "one-liner":

library(data.table)
setDT(stack(list(Yes = c("Y", "Yes"), No = c("N", "No"))))[x, on=.(values), ind ]
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

5
2018-04-12 20:00



Non conosco il tuo vero caso d'uso, ma lo farei strtrim essere di qualche utilità qui ...

factor( strtrim( x , 1 ) , levels = c("Y" , "N" ) , labels = c("Yes" , "No" ) )
#[1] Yes  Yes  Yes  No   No   <NA>
#Levels: Yes No

2
2017-10-16 17:43



Simile all'approccio di @ Aaron, ma leggermente più semplice sarebbe:

x <- c("Y", "Y", "Yes", "N", "No", "H")
x <- factor(x)
# levels(x)  
# [1] "H"   "N"   "No"  "Y"   "Yes"
# NB: the offending levels are 1, 2, & 4
levels(x)[c(1,2,4)] <- c(NA, "No", "Yes")
x
# [1] Yes  Yes  Yes  No   No   <NA>
# Levels: No Yes

2
2018-06-09 22:10



È possibile utilizzare la funzione seguente per combinare / comprimere più fattori:

combofactor <- function(pattern_vector,
         replacement_vector,
         data) {
 levels <- levels(data)
 for (i in 1:length(pattern_vector))
      levels[which(pattern_vector[i] == levels)] <-
        replacement_vector[i]
 levels(data) <- levels
  data
}

Esempio:

Inizializza x

x <- factor(c(rep("Y",20),rep("N",20),rep("y",20),
rep("yes",20),rep("Yes",20),rep("No",20)))

Controlla la struttura

str(x)
# Factor w/ 6 levels "N","No","y","Y",..: 4 4 4 4 4 4 4 4 4 4 ...

Usa la funzione:

x_new <- combofactor(c("Y","N","y","yes"),c("Yes","No","Yes","Yes"),x)

Ricontrollare la struttura:

str(x_new)
# Factor w/ 2 levels "No","Yes": 2 2 2 2 2 2 2 2 2 2 ...

1
2018-06-17 17:08