Konvertieren Sie data.frame-Spalten von Faktoren in Zeichen


352

Ich habe einen Datenrahmen. Nennen wir ihn 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-

Ich möchte die Zeilen dieses Datenrahmens verketten (dies wird eine andere Frage sein). Aber schau:

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

BobDie Spalten sind Faktoren. Also zum Beispiel:

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

Ich fange nicht an, das zu verstehen, aber ich denke, dies sind Indizes für die Faktoren der Säulen (des Hofes des Königs Caractacus) von bob? Nicht was ich brauche.

Seltsamerweise kann ich die Säulen bobvon Hand durchgehen und es tun

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

das funktioniert gut. Und nach einigem Tippen kann ich einen data.frame erhalten, dessen Spalten eher Zeichen als Faktoren sind. Meine Frage lautet also: Wie kann ich das automatisch tun? Wie konvertiere ich einen data.frame mit Faktorspalten in einen data.frame mit Zeichenspalten, ohne jede Spalte manuell durchgehen zu müssen?

Bonusfrage: Warum funktioniert der manuelle Ansatz?


3
wäre schön, wenn du die frage reproduzierbar machen würdest, also die struktur von einschließen bob.
Jangorecki

Antworten:


362

Ich folge nur Matt und Dirk. Wenn Sie Ihren vorhandenen Datenrahmen neu erstellen möchten, ohne die globale Option zu ändern, können Sie ihn mit einer apply-Anweisung neu erstellen:

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

Dadurch werden alle Variablen in die Klasse "Zeichen" konvertiert. Wenn Sie nur Faktoren konvertieren möchten, siehe Mareks Lösung unten .

Wie @hadley hervorhebt, ist das Folgende prägnanter.

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

In beiden Fällen wird lapplyeine Liste ausgegeben. []Aufgrund der magischen Eigenschaften von R behält die Verwendung von im zweiten Fall jedoch die data.frame-Klasse des bobObjekts bei, wodurch die Notwendigkeit entfällt, as.data.framemit dem Argument zurück in einen data.frame zu konvertieren stringsAsFactors = FALSE.


27
Shane, das wird auch numerische Spalten in Zeichen verwandeln.
Dirk Eddelbuettel

@Dirk: Das stimmt, obwohl nicht klar ist, ob das hier ein Problem ist. Es ist klar, dass es die beste Lösung ist, Dinge im Voraus richtig zu erstellen. Ich denke nicht, dass es einfach ist , Datentypen automatisch über einen Datenrahmen hinweg zu konvertieren. Eine Möglichkeit ist , die oben zu verwenden , aber dann zu verwenden , type.convertnachdem alles Gießen character, dann Neufassung factorszurück zu characterwieder.
Shane

Dies scheint Zeilennamen zu verwerfen.
Piccolbo

2
@piccolbo hast du bob[] <- im Beispiel verwendet oder bob <- ?; der erste behält den data.frame; Der zweite ändert den data.frame in eine Liste und löscht die Rownamen. Ich werde die Antwort aktualisieren
David LeBauer

6
Eine Variante, die nur Faktorspalten mit einer anonymen Funktion in Zeichen konvertiert: iris[] <- lapply(iris, function(x) if (is.factor(x)) as.character(x) else {x})
Stefan F

313

Um nur Faktoren zu ersetzen:

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

Im Paket dplyr in Version 0.5.0 wurde eine neue Funktion mutate_ifeingeführt :

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

Das Paket purrr von RStudio bietet eine weitere Alternative:

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

Ich arbeite leider nicht für mich. Ich weiß nicht warum. Wahrscheinlich, weil ich Colnamen habe?
Autumnsault

@mohawkjohn Sollte kein Problem sein. Sie haben Fehler oder Ergebnisse nicht wie erwartet?
Marek

2
Hinweis: Die purrrZeile gibt eine Liste zurück, keine data.frame!
RoyalTS

Dies funktioniert auch, wenn Sie bereits ieinen Vektor von haben colnames().
Verbamour

39

Die globale Option

stringAsFactors: Die Standardeinstellung für die Argumente data.frame und read.table.

Möglicherweise möchten Sie dies FALSEin Ihren Startdateien festlegen (z. B. ~ / .Rprofile). Bitte sehen Sie help(options).


5
Das Problem dabei ist, dass wenn Sie Ihren Code in einer Umgebung ausführen, in der diese .Rprofile-Datei fehlt, Fehler auftreten!
Waferthin

4
Ich neige dazu, es am Anfang von Skripten aufzurufen, anstatt es im .Rprofile festzulegen.
Gregmacfarlane

22

Wenn Sie verstehen, wie Faktoren gespeichert werden, können Sie vermeiden, anwendungsbasierte Funktionen zu verwenden, um dies zu erreichen. Was keineswegs bedeutet, dass die Anwendungslösungen nicht gut funktionieren.

Faktoren sind als numerische Indizes strukturiert, die an eine Liste von „Ebenen“ gebunden sind. Dies wird angezeigt, wenn Sie einen Faktor in einen numerischen Wert umwandeln. Damit:

> 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

Die in der letzten Zeile zurückgegebenen Zahlen entsprechen den Pegeln des Faktors.

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

Beachten Sie, dass levels()ein Array von Zeichen zurückgegeben wird. Mit dieser Tatsache können Sie Faktoren einfach und kompakt in Zeichenfolgen oder Zahlen wie diese umwandeln:

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

Dies funktioniert auch für numerische Werte, sofern Sie Ihren Ausdruck einschließen 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

Diese Antwort behebt das Problem nicht. Wie konvertiere ich alle Faktorspalten in meinem Datenrahmen in Zeichen? as.character(f)ist sowohl in der Lesbarkeit als auch in der Effizienz besser zu levels(f)[as.numeric(f)]. Wenn Sie klug sein wollen, können Sie levels(f)[f]stattdessen verwenden. Beachten Sie, dass Sie beim Konvertieren eines Faktors mit numerischen Werten einige Vorteile as.numeric(levels(f))[f]erzielen, z. B. as.numeric(as.character(f))weil Sie nur die Ebenen in numerische Werte und dann in Teilmengen konvertieren müssen. as.character(f)ist gut so wie es ist.
De Novo

20

Wenn Sie einen neuen Datenrahmen wünschen, bobcin dem jeder Faktorvektor in bobfeinen Zeichenvektor konvertiert wird, versuchen Sie Folgendes:

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

Wenn Sie es dann zurückkonvertieren möchten, können Sie einen logischen Vektor erstellen, dessen Spalten Faktoren sind, und diesen verwenden, um den Faktor selektiv anzuwenden

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

2
+1, um nur das zu tun, was nötig war (dh nicht den gesamten Datenrahmen in ein Zeichen zu konvertieren). Diese Lösung ist robust gegenüber einem data.frame, der gemischte Typen enthält.
Joshua Ulrich

3
Dieses Beispiel sollte sich im Abschnitt "Beispiele" für rapply befinden, z. B.: Stat.ethz.ch/R-manual/R-devel/library/base/html/rapply.html . Weiß jemand, wie man das verlangt?
mpettis

Wenn Sie mit einem Datenrahmen enden möchten, schließen Sie den Rapply einfach in einen data.frame-Aufruf ein (unter Verwendung des auf das Argument FALSE gesetzten
StringsAsFactors

13

Normalerweise mache ich diese Funktion von all meinen Projekten getrennt. Schnell und einfach.

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

8

Eine andere Möglichkeit besteht darin, es mit apply zu konvertieren

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

Und eine bessere (die vorherige ist von der Klasse 'Matrix')

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

Folgen Sie @ Shane Kommentar: Um data.frame zu erhalten, tun Sieas.data.frame(lapply(...
aL3xa

7

Update: Hier ist ein Beispiel für etwas, das nicht funktioniert. Ich dachte, es würde, aber ich denke, dass die Option stringsAsFactors nur für Zeichenfolgen funktioniert - sie lässt die Faktoren in Ruhe.

Versuche dies:

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

Wenn Sie Probleme mit Faktoren haben, die Zeichen sein sollten, gibt es im Allgemeinen eine stringsAsFactorsEinstellung, die Ihnen helfen kann (einschließlich einer globalen Einstellung).


1
Dies funktioniert, wenn er es beim Erstellen zunächst festlegt bob(aber nicht nachträglich).
Shane

Recht. Ich wollte nur klarstellen, dass dies das Problem per se nicht löst - aber danke, dass Sie bemerkt haben, dass es es verhindert.
Matt Parker

7

Oder Sie können versuchen transform:

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

Stellen Sie einfach sicher, dass Sie jeden Faktor, den Sie konvertieren möchten, in Zeichen umwandeln.

Oder Sie können so etwas tun und alle Schädlinge mit einem Schlag töten:

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)

Es ist keine gute Idee, die Daten in Code wie diesen zu verschieben, ich könnte das tunsapply Teil separat (eigentlich ist es viel einfacher, dies so zu tun), aber Sie verstehen, dass ... ich den Code nicht überprüft habe, weil Ich bin nicht zu Hause, also hoffe ich, dass es funktioniert! =)

Dieser Ansatz hat jedoch einen Nachteil ... Sie müssen die Spalten anschließend neu organisieren, während transformSie mit dem tun können, was Sie möchten , jedoch auf Kosten des "Code-Schreibens im Fußgängerstil" ...

Also da ... =)


6

Zu Beginn Ihres Datenrahmens schließen Sie ein stringsAsFactors = FALSE, um alle Missverständnisse zu ignorieren.


4

Wenn Sie data.tablepackage für die Operationen an data.frame verwenden würden, liegt das Problem nicht vor.

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

Wenn Ihr Datensatz bereits eine Faktorspalte enthält und Sie diese in Zeichen konvertieren möchten, können Sie Folgendes tun.

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 umgeht den von Marek vorgeschlagenen sapply Fix: 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.Es ist einfacher, den DF zu reparieren und den DT neu zu erstellen.
Matt Chambers

2

Das funktioniert bei mir - ich habe mir endlich einen Einzeiler ausgedacht

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

2

Diese Funktion macht den Trick

df <- stacomirtools::killfactor(df)

2

Vielleicht eine neuere Option?

library("tidyverse")

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

1

Sie sollten verwenden convertin hablarmit kompatibeler lesbarer Syntax gibt , die tidyverse: Rohre

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

was dir gibt:

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

1

Mit dem dplyrgeladenen Paket verwenden

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

wenn Sie nur die phenotype-column speziell ändern möchten.


0

Dies funktioniert, indem alle in Zeichen und dann die Zahlen in Zahlen umgewandelt werden:

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

Angepasst von: Holen Sie sich automatisch Spaltentypen von Excel-Tabellen

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.