Converti colonne data.frame da fattori a caratteri


352

Ho un frame di dati. Chiamiamolo bob:

> head(bob)
                 phenotype                         exclusion
GSM399350 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399351 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399352 3- 4- 8- 25- 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399353 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399354 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-
GSM399355 3- 4- 8- 25+ 44+ 11b- 11c- 19- NK1.1- Gr1- TER119-

Vorrei concatenare le righe di questo frame di dati (questa sarà un'altra domanda). Ma guarda:

> class(bob$phenotype)
[1] "factor"

BobLe colonne sono fattori. Quindi, per esempio:

> as.character(head(bob))
[1] "c(3, 3, 3, 6, 6, 6)"       "c(3, 3, 3, 3, 3, 3)"      
[3] "c(29, 29, 29, 30, 30, 30)"

Non comincio a capirlo, ma suppongo che questi siano indici nei livelli dei fattori delle colonne (della corte del re caractacus) di bob? Non è quello di cui ho bisogno.

Stranamente posso passare attraverso le colonne di bobmano e farlo

bob$phenotype <- as.character(bob$phenotype)

che funziona benissimo. E, dopo aver digitato un po ', posso ottenere un data.frame le cui colonne sono caratteri piuttosto che fattori. Quindi la mia domanda è: come posso farlo automaticamente? Come faccio a convertire un data.frame con colonne fattore in un data.frame con colonne carattere senza dover passare manualmente attraverso ogni colonna?

Domanda bonus: perché funziona l'approccio manuale?


3
sarebbe bello se la domanda fosse riproducibile, quindi includi la struttura di bob.
jangorecki,

Risposte:


362

Sto solo seguendo Matt e Dirk. Se si desidera ricreare il frame di dati esistente senza modificare l'opzione globale, è possibile ricrearlo con un'istruzione apply:

bob <- data.frame(lapply(bob, as.character), stringsAsFactors=FALSE)

In questo modo tutte le variabili verranno convertite in "carattere" di classe, se si desidera convertire solo i fattori, vedere la soluzione di Marek di seguito .

Come sottolinea @hadley, quanto segue è più conciso.

bob[] <- lapply(bob, as.character)

In entrambi i casi, lapplygenera un elenco; tuttavia, a causa delle proprietà magiche di R, l'uso di []nel secondo caso mantiene la classe data.frame bobdell'oggetto, eliminando così la necessità di riconvertire in un data.frame usando as.data.framecon l'argomento stringsAsFactors = FALSE.


27
Shane, trasformerà anche le colonne numeriche in caratteri.
Dirk Eddelbuettel,

@Dirk: è vero, anche se non è chiaro se questo sia un problema qui. Chiaramente, creare le cose correttamente in anticipo è la soluzione migliore. Non penso che sia facile convertire automaticamente i tipi di dati in un frame di dati. Un'opzione è quella di utilizzare quanto sopra ma quindi utilizzare type.convertdopo aver lanciato tutto per character, quindi rifondere factorsnuovamentecharacter nuovo.
Shane,

Questo sembra scartare i nomi delle righe.
piccolbo,

2
@piccolbo hai usato bob[] <- nell'esempio o bob <- ?; il primo mantiene data.frame; il secondo modifica data.frame in un elenco, eliminando i rownames. Aggiornerò la risposta
David LeBauer l'

6
Una variante che converte solo le colonne fattoriali in carattere usando una funzione anonima: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

Per sostituire solo i fattori:

i <- sapply(bob, is.factor)
bob[i] <- lapply(bob[i], as.character)

Nel pacchetto dplyr nella versione 0.5.0 è mutate_ifstata introdotta una nuova funzione :

library(dplyr)
bob %>% mutate_if(is.factor, as.character) -> bob

Il pacchetto purrr di RStudio offre un'altra alternativa:

library(purrr)
library(dplyr)
bob %>% map_if(is.factor, as.character) %>% as_tibble -> bob

Purtroppo non funziona per me. Non so perché. Probabilmente perché ho i nomi dei nomi?
Autumnsault,

@mohawkjohn Non dovrebbe essere un problema. Hai ricevuto errori o risultati non come previsto?
Marek,

2
Nota: la purrrriga restituisce un elenco, non un data.frame!
RoyalTS

Questo funziona anche se hai già iun vettore di colnames().
verbamour

39

L'opzione globale

stringsAsFactors: l'impostazione predefinita per gli argomenti di data.frame e read.table.

potrebbe essere qualcosa che si desidera impostare FALSEnei file di avvio (ad es. ~ / .Rprofile). Si prega di consultare help(options).


5
Il problema è che quando esegui il tuo codice in un ambiente in cui manca quel file .Rprofile otterrai dei bug!
waferthin

4
Tendo a chiamarlo all'inizio degli script piuttosto che l'impostazione è nel .profilo.
gregmacfarlane,

22

Se si capisce come vengono memorizzati i fattori, è possibile evitare di utilizzare le funzioni basate sull'applicazione per ottenere questo risultato. Il che non significa affatto che le soluzioni di applicazione non funzionino bene.

I fattori sono strutturati come indici numerici legati a un elenco di "livelli". Questo può essere visto se si converte un fattore in numerico. Così:

> fact <- as.factor(c("a","b","a","d")
> fact
[1] a b a d
Levels: a b d

> as.numeric(fact)
[1] 1 2 1 3

I numeri restituiti nell'ultima riga corrispondono ai livelli del fattore.

> levels(fact)
[1] "a" "b" "d"

Si noti che levels()restituisce una matrice di caratteri. Puoi usare questo fatto per convertire facilmente e in modo compatto i fattori in stringhe o numeri come questo:

> fact_character <- levels(fact)[as.numeric(fact)]
> fact_character
[1] "a" "b" "a" "d"

Questo funziona anche con valori numerici, a condizione che tu abbia inserito la tua espressione as.numeric().

> num_fact <- factor(c(1,2,3,6,5,4))
> num_fact
[1] 1 2 3 6 5 4
Levels: 1 2 3 4 5 6
> num_num <- as.numeric(levels(num_fact)[as.numeric(num_fact)])
> num_num
[1] 1 2 3 6 5 4

Questa risposta non risolve il problema, che è come convertire in carattere tutte le colonne dei fattori nel mio frame di dati. as.character(f), è migliore sia in leggibilità che in efficienza levels(f)[as.numeric(f)]. Se volessi essere intelligente, potresti usare levels(f)[f]invece. Si noti che quando si converte un fattore con valori numerici, si ottiene un certo vantaggio da as.numeric(levels(f))[f]over, ad esempio as.numeric(as.character(f)), ma ciò è dovuto al fatto che è necessario convertire solo i livelli in numerici e quindi sottoinsiemi. as.character(f)va bene così com'è.
De Novo,

20

Se vuoi un nuovo frame di dati in bobccui ogni vettore di fattore bobfviene convertito in un vettore di caratteri, prova questo:

bobc <- rapply(bobf, as.character, classes="factor", how="replace")

Se si desidera quindi riconvertirlo, è possibile creare un vettore logico di quali colonne sono fattori e utilizzarlo per applicare selettivamente il fattore

f <- sapply(bobf, class) == "factor"
bobc[,f] <- lapply(bobc[,f], factor)

2
+1 per fare solo ciò che era necessario (cioè non convertire l'intero data.frame in carattere). Questa soluzione è affidabile per un data.frame che contiene tipi misti.
Joshua Ulrich,

3
Questo esempio dovrebbe essere nella sezione "Esempi" per rapply, come in: stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html . Qualcuno sa come richiedere che sia così?
mpettis,

Se vuoi finire con un frame di dati, avvolgi semplicemente il rapply in una chiamata data.frame (usando le stringheAsFactors impostato sull'argomento FALSE)
Taylored Web Sites

13

In genere faccio questa funzione a parte tutti i miei progetti. Facile e veloce.

unfactorize <- function(df){
  for(i in which(sapply(df, class) == "factor")) df[[i]] = as.character(df[[i]])
  return(df)
}

8

Un altro modo è quello di convertirlo usando Apply

bob2 <- apply(bob,2,as.character)

E uno migliore (il precedente è di classe 'matrice')

bob2 <- as.data.frame(as.matrix(bob),stringsAsFactors=F)

Seguendo il commento di @Shane: per ottenere data.frame, fareas.data.frame(lapply(...
aL3xa

7

Aggiornamento: ecco un esempio di qualcosa che non funziona. Ho pensato che sarebbe successo, ma penso che l'opzione stringheAsFactors funziona solo su stringhe di caratteri - lascia da soli i fattori.

Prova questo:

bob2 <- data.frame(bob, stringsAsFactors = FALSE)

In generale, ogni volta che si verificano problemi con fattori che dovrebbero essere personaggi, c'è stringsAsFactorsun'impostazione da qualche parte per aiutarti (inclusa un'impostazione globale).


1
Questo funziona, se lo imposta durante la creazione bob(ma non dopo il fatto).
Shane,

Giusto. Volevo solo essere chiaro che questo non risolve il problema, di per sé - ma grazie per aver notato che lo impedisce.
Matt Parker,

7

Oppure puoi provare transform:

newbob <- transform(bob, phenotype = as.character(phenotype))

Assicurati di inserire tutti i fattori che desideri convertire in personaggio.

Oppure puoi fare qualcosa del genere e uccidere tutti i parassiti con un colpo:

newbob_char <- as.data.frame(lapply(bob[sapply(bob, is.factor)], as.character), stringsAsFactors = FALSE)
newbob_rest <- bob[!(sapply(bob, is.factor))]
newbob <- cbind(newbob_char, newbob_rest)

Non è una buona idea inserire i dati nel codice in questo modo, potrei fare la sapplyparte separatamente (in realtà, è molto più facile farlo in quel modo), ma ottieni il punto ... Non ho controllato il codice, perché Non sono a casa, quindi spero che funzioni! =)

Questo approccio, tuttavia, ha un rovescio della medaglia ... devi riorganizzare le colonne in seguito, mentre con transformte puoi fare quello che vuoi, ma a costo di "scrivere codice in stile pedonale" ...

Quindi lì ... =)


6

All'inizio del frame di dati includere stringsAsFactors = FALSEignorare tutti i malintesi.


4

Se si utilizza il data.tablepacchetto per le operazioni su data.frame, il problema non è presente.

library(data.table)
dt = data.table(col1 = c("a","b","c"), col2 = 1:3)
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

Se hai già un fattore colonne nel tuo set di dati e vuoi convertirle in carattere, puoi fare quanto segue.

library(data.table)
dt = data.table(col1 = factor(c("a","b","c")), col2 = 1:3)
sapply(dt, class)
#     col1      col2 
# "factor" "integer" 
upd.cols = sapply(dt, is.factor)
dt[, names(dt)[upd.cols] := lapply(.SD, as.character), .SDcols = upd.cols]
sapply(dt, class)
#       col1        col2 
#"character"   "integer" 

DT elude la correzione sapply proposta da Marek: In [<-.data.table(*tmp*, sapply(bob, is.factor), : Coerced 'character' RHS to 'double' to match the column's type. Either change the target column to 'character' first (by creating a new 'character' vector length 1234 (nrows of entire table) and assign that; i.e. 'replace' column), or coerce RHS to 'double' (e.g. 1L, NA_[real|integer]_, as.*, etc) to make your intent clear and for speed. Or, set the column type correctly up front when you create the table and stick to it, please.è più facile riparare il DF e ricreare il DT.
Matt Chambers,

2

Questo funziona per me - ho finalmente immaginato una fodera

df <- as.data.frame(lapply(df,function (y) if(class(y)=="factor" ) as.character(y) else y),stringsAsFactors=F)

2

Questa funzione fa il trucco

df <- stacomirtools::killfactor(df)

2

Forse un'opzione più recente?

library("tidyverse")

bob <- bob %>% group_by_if(is.factor, as.character)

1

Si dovrebbe usare convertin hablarcui fornisce sintassi leggibile compatibile con le tidyversepipe:

library(dplyr)
library(hablar)

df <- tibble(a = factor(c(1, 2, 3, 4)),
             b = factor(c(5, 6, 7, 8)))

df %>% convert(chr(a:b))

che ti dà:

  a     b    
  <chr> <chr>
1 1     5    
2 2     6    
3 3     7    
4 4     8   

1

Con l' dplyruso del pacchetto caricato

bob=bob%>%mutate_at("phenotype", as.character)

se si desidera modificare phenotypespecificamente solo la colonna.


0

Questo funziona trasformando tutto in carattere e poi il numerico in numerico:

makenumcols<-function(df){
  df<-as.data.frame(df)
  df[] <- lapply(df, as.character)
  cond <- apply(df, 2, function(x) {
    x <- x[!is.na(x)]
    all(suppressWarnings(!is.na(as.numeric(x))))
  })
  numeric_cols <- names(df)[cond]
  df[,numeric_cols] <- sapply(df[,numeric_cols], as.numeric)
  return(df)
}

Adattato da: ottieni automaticamente i tipi di colonna del foglio Excel

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.