Wie konvertiere ich einen Faktor ohne Informationsverlust in eine Ganzzahl \ numerisch?

599

Wenn ich einen Faktor in eine Zahl oder eine Ganzzahl umwandle, erhalte ich die zugrunde liegenden Ebenencodes, nicht die Werte als Zahlen.

f <- factor(sample(runif(5), 20, replace = TRUE))
##  [1] 0.0248644019011408 0.0248644019011408 0.179684827337041 
##  [4] 0.0284090070053935 0.363644931698218  0.363644931698218 
##  [7] 0.179684827337041  0.249704354675487  0.249704354675487 
## [10] 0.0248644019011408 0.249704354675487  0.0284090070053935
## [13] 0.179684827337041  0.0248644019011408 0.179684827337041 
## [16] 0.363644931698218  0.249704354675487  0.363644931698218 
## [19] 0.179684827337041  0.0284090070053935
## 5 Levels: 0.0248644019011408 0.0284090070053935 ... 0.363644931698218

as.numeric(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

as.integer(f)
##  [1] 1 1 3 2 5 5 3 4 4 1 4 2 3 1 3 5 4 5 3 2

Ich muss zurückgreifen paste, um die wahren Werte zu erhalten:

as.numeric(paste(f))
##  [1] 0.02486440 0.02486440 0.17968483 0.02840901 0.36364493 0.36364493
##  [7] 0.17968483 0.24970435 0.24970435 0.02486440 0.24970435 0.02840901
## [13] 0.17968483 0.02486440 0.17968483 0.36364493 0.24970435 0.36364493
## [19] 0.17968483 0.02840901

Gibt es eine bessere Möglichkeit, einen Faktor in einen numerischen Wert umzuwandeln?

Adam SO
quelle
6
Die Ebenen eines Faktors werden sowieso als Zeichendatentyp gespeichert ( attributes(f)), daher glaube ich nicht, dass daran etwas falsch ist as.numeric(paste(f)). Vielleicht ist es besser zu überlegen, warum (im spezifischen Kontext) Sie überhaupt einen Faktor bekommen, und zu versuchen, dies zu stoppen. Ist das decArgument beispielsweise read.tablerichtig eingestellt?
CJB
Wenn Sie einen Datenrahmen verwenden, können Sie die Konvertierung von Hablar verwenden. df %>% convert(num(column)). Oder wenn Sie einen Faktorvektor haben, den Sie verwenden könnenas_reliable_num(factor_vector)
davsjob

Antworten:

711

Siehe den Abschnitt Warnung von ?factor:

Insbesondere ist die as.numericAnwendung auf einen Faktor bedeutungslos und kann durch impliziten Zwang geschehen. Es wird empfohlen , einen Faktor fin ungefähr seine ursprünglichen numerischen Werte umzuwandeln , as.numeric(levels(f))[f]und dies ist etwas effizienter als as.numeric(as.character(f)).

Die FAQ zu R hat ähnliche Ratschläge .


Warum ist as.numeric(levels(f))[f]effizienter als as.numeric(as.character(f))?

as.numeric(as.character(f))ist effektiv as.numeric(levels(f)[f]), also führen Sie die Konvertierung in numerische length(x)Werte anstelle von nlevels(x)Werten durch. Der Geschwindigkeitsunterschied ist am deutlichsten bei langen Vektoren mit wenigen Pegeln. Wenn die Werte größtenteils eindeutig sind, gibt es keinen großen Geschwindigkeitsunterschied. Unabhängig davon, wie Sie die Konvertierung durchführen, ist es unwahrscheinlich, dass dieser Vorgang den Engpass in Ihrem Code darstellt. Machen Sie sich also keine allzu großen Sorgen.


Einige Timings

library(microbenchmark)
microbenchmark(
  as.numeric(levels(f))[f],
  as.numeric(levels(f)[f]),
  as.numeric(as.character(f)),
  paste0(x),
  paste(x),
  times = 1e5
)
## Unit: microseconds
##                         expr   min    lq      mean median     uq      max neval
##     as.numeric(levels(f))[f] 3.982 5.120  6.088624  5.405  5.974 1981.418 1e+05
##     as.numeric(levels(f)[f]) 5.973 7.111  8.352032  7.396  8.250 4256.380 1e+05
##  as.numeric(as.character(f)) 6.827 8.249  9.628264  8.534  9.671 1983.694 1e+05
##                    paste0(x) 7.964 9.387 11.026351  9.956 10.810 2911.257 1e+05
##                     paste(x) 7.965 9.387 11.127308  9.956 11.093 2419.458 1e+05
Joshua Ulrich
quelle
4
Für Timings siehe diese Antwort: stackoverflow.com/questions/6979625/…
Ari B. Friedman
3
Vielen Dank für Ihre Lösung. Kann ich fragen, warum die as.numeric (Ebenen (f)) [f] präziser und schneller ist? Vielen Dank.
Sam
7
@Sam as.character (f) erfordert eine "primitive Suche", um die Funktion as.character.factor () zu finden, die als.numeric (Ebenen (f)) [f] definiert ist.
Jonathan
12
Wenn ich as.numeric (Ebenen (f)) [f] ODER as.numeric (as.character (f)) anwende, erhalte ich eine Warnmeldung: Warnmeldung: NAs durch Zwang eingeführt. Wissen Sie, wo das Problem liegen könnte? Danke !
Maycca
@ Maycca hast du dieses Problem überwunden?
user08041991
91

R verfügt über eine Reihe von (nicht dokumentierten) Komfortfunktionen zum Umrechnen von Faktoren:

  • as.character.factor
  • as.data.frame.factor
  • as.Date.factor
  • as.list.factor
  • as.vector.factor
  • ...

Aber ärgerlicherweise gibt es nichts, was mit dem Faktor -> numerische Konvertierung zu tun hätte . Als Erweiterung von Joshua Ulrichs Antwort würde ich vorschlagen, diese Lücke mit der Definition Ihrer eigenen Redewendung zu überwinden:

as.numeric.factor <- function(x) {as.numeric(levels(x))[x]}

dass Sie am Anfang Ihres Skripts oder noch besser in Ihrer .RprofileDatei speichern können.

Jealie
quelle
14
Es gibt nichts, was die Umwandlung von Faktor in Ganzzahl (oder numerisch) handhaben könnte, da erwartet wird, dass as.integer(factor)die zugrunde liegenden Ganzzahlcodes zurückgegeben werden (wie im Beispielabschnitt von gezeigt ?factor). Es ist wahrscheinlich in Ordnung, diese Funktion in Ihrer globalen Umgebung zu definieren, aber Sie können Probleme verursachen, wenn Sie sie tatsächlich als S3-Methode registrieren.
Joshua Ulrich
1
Das ist ein guter Punkt, und ich stimme zu: Eine vollständige Neudefinition der faktor-> numerischen Konvertierung wird wahrscheinlich viele Dinge durcheinander bringen. Ich fand mich das lästige Schreiben factor->numericUmwandlung viel vor der Realisierung , dass es in der Tat ein Manko von R: einige Komfortfunktion sollte verfügbar sein ... Nannte es as.numeric.factormacht Sinn für mich, aber YMMV.
Jealie
4
Wenn Sie feststellen , selbst zu tun , dass eine Menge , dann sollten Sie etwas stromaufwärts tun es alles zusammen zu vermeiden.
Joshua Ulrich
2
as.numeric.factor gibt NA zurück?
jO.
@jO.: in den Fällen, in denen Sie so etwas wie v=NA;as.numeric.factor(v)oder verwendet haben v='something';as.numeric.factor(v), dann sollte es, sonst haben Sie irgendwo eine seltsame Sache vor sich.
Jealie
33

Am einfachsten wäre es, die unfactorFunktion aus dem Paket varhandle zu verwenden

unfactor(your_factor_variable)

Dieses Beispiel kann ein schneller Start sein:

x <- rep(c("a", "b", "c"), 20)
y <- rep(c(1, 1, 0), 20)

class(x)  # -> "character"
class(y)  # -> "numeric"

x <- factor(x)
y <- factor(y)

class(x)  # -> "factor"
class(y)  # -> "factor"

library(varhandle)
x <- unfactor(x)
y <- unfactor(y)

class(x)  # -> "character"
class(y)  # -> "numeric"
Mehrad Mahmoudian
quelle
Die unfactorFunktion wird zuerst in den Zeichendatentyp und dann wieder in den numerischen Typ konvertiert. Geben Sie unfactoran der Konsole ein und Sie können es in der Mitte der Funktion sehen. Daher gibt es keine bessere Lösung als das, was der Fragesteller bereits hatte.
CJB
Allerdings sind die Ebenen eines Faktors ohnehin vom Zeichentyp, sodass bei diesem Ansatz nichts verloren geht.
CJB
Die unfactorFunktion kümmert sich um Dinge, die nicht in numerische konvertiert werden können. Überprüfen Sie die Beispiele inhelp("unfactor")
Mehrad Mahmoudian
2
@ Selrac Ich habe erwähnt, dass diese Funktion im varhandle- Paket verfügbar ist , was bedeutet, dass Sie das package ( library("varhandle")) zuerst laden sollten (wie ich in der ersten Zeile meiner Antwort erwähnt habe !!)
Mehrad Mahmoudian
1
@Gregor Das Hinzufügen einer Lichtabhängigkeit schadet normalerweise nicht. Wenn Sie nach dem effizientesten Weg suchen, können Sie den Code, den Sie selbst schreiben, möglicherweise schneller ausführen. Aber wie Sie auch in Ihrem Kommentar sehen können, ist dies nicht trivial, da Sie auch das as.numeric()und as.character()in eine falsche Reihenfolge bringen;) Was Ihr Codeblock tut, ist, den Ebenenindex des Faktors in eine Zeichenmatrix umzuwandeln, also was Sie am und haben werden ist ein Zeichenvektor, der einige Zahlen enthält, die einmal einer bestimmten Ebene Ihres Faktors zugewiesen wurden. Funktionen in diesem Paket sollen diese Verwirrungen verhindern
Mehrad Mahmoudian
23

Hinweis: Diese spezielle Antwort dient nicht zum Konvertieren von numerisch bewerteten Faktoren in numerische Faktoren, sondern zum Konvertieren von kategorialen Faktoren in die entsprechenden Ebenennummern.


Jede Antwort in diesem Beitrag konnte keine Ergebnisse für mich generieren, NAs wurden generiert.

y2<-factor(c("A","B","C","D","A")); 
as.numeric(levels(y2))[y2] 
[1] NA NA NA NA NA Warning message: NAs introduced by coercion

Was für mich funktioniert hat ist das -

as.integer(y2)
# [1] 1 2 3 4 1
Indisch
quelle
Bist du sicher, dass du einen Faktor hattest? Schauen Sie sich dieses Beispiel an. y<-factor(c("5","15","20","2")); unclass(y) %>% as.numericDies ergibt 4,1,3,2, nicht 5,15,20,2. Dies scheint eine falsche Information zu sein.
MrFlick
Ok, das ähnelt dem, was ich heute versucht habe: - y2 <-Faktor (c ("A", "B", "C", "D", "A")); as.numeric (Ebenen (y2)) [y2] [1] NA NA NA NA NA Warnmeldung: NAs durch Zwang eingeführt, während unclass (y2)%>% as.numeric mir die Ergebnisse lieferte, die ich brauchte.
Indi
4
OK, das ist nicht die Frage, die oben gestellt wurde. In dieser Frage sind die Faktorstufen alle "numerisch". In Ihrem Fall as.numeric(y)hätte gut funktionieren sollen, keine Notwendigkeit für die unclass(). Aber genau darum ging es in dieser Frage nicht. Diese Antwort ist hier nicht angemessen.
MrFlick
3
Nun, ich hoffe wirklich, dass es jemandem hilft, der es eilig hat wie ich und nur den Titel liest!
Indi
1
Wenn Sie Zeichen haben, die die ganzen Zahlen als Faktoren darstellen, würde ich diese empfehlen. Dies ist der einzige, der für mich gearbeitet hat.
Ziel
9

Dies ist nur möglich, wenn die Faktorbezeichnungen mit den ursprünglichen Werten übereinstimmen. Ich werde es mit einem Beispiel erklären.

Angenommen, die Daten sind Vektoren x:

x <- c(20, 10, 30, 20, 10, 40, 10, 40)

Jetzt werde ich einen Faktor mit vier Bezeichnungen erstellen:

f <- factor(x, levels = c(10, 20, 30, 40), labels = c("A", "B", "C", "D"))

1) xist mit Typ double, fist mit Typ Integer. Dies ist der erste unvermeidbare Informationsverlust. Faktoren werden immer als ganze Zahlen gespeichert.

> typeof(x)
[1] "double"
> typeof(f)
[1] "integer"

2) Es ist nicht möglich, zu den ursprünglichen Werten (10, 20, 30, 40) zurückzukehren, die nur fverfügbar sind. Wir können sehen, dass fnur ganzzahlige Werte 1, 2, 3, 4 und zwei Attribute enthalten sind - die Liste der Bezeichnungen ("A", "B", "C", "D") und das Klassenattribut "Faktor". Nichts mehr.

> str(f)
 Factor w/ 4 levels "A","B","C","D": 2 1 3 2 1 4 1 4
> attributes(f)
$levels
[1] "A" "B" "C" "D"

$class
[1] "factor"

Um zu den ursprünglichen Werten zurückzukehren, müssen wir die Werte der Ebenen kennen, die beim Erstellen des Faktors verwendet wurden. In diesem Fall c(10, 20, 30, 40). Wenn wir die ursprünglichen Werte kennen (in der richtigen Reihenfolge), können wir zu den ursprünglichen Werten zurückkehren.

> orig_levels <- c(10, 20, 30, 40)
> x1 <- orig_levels[f]
> all.equal(x, x1)
[1] TRUE

Dies funktioniert nur, wenn für alle möglichen Werte in den Originaldaten Beschriftungen definiert wurden.

Wenn Sie also die ursprünglichen Werte benötigen, müssen Sie diese beibehalten. Andernfalls besteht eine hohe Wahrscheinlichkeit, dass es nicht möglich ist, nur von einem Faktor zu ihnen zurückzukehren.

djhurio
quelle
2

Sie können verwenden, hablar::convertwenn Sie einen Datenrahmen haben. Die Syntax ist einfach:

Probe df

library(hablar)
library(dplyr)

df <- dplyr::tibble(a = as.factor(c("7", "3")),
                    b = as.factor(c("1.5", "6.3")))

Lösung

df %>% 
  convert(num(a, b))

gibt Ihnen:

# A tibble: 2 x 2
      a     b
  <dbl> <dbl>
1    7.  1.50
2    3.  6.30

Oder wenn eine Spalte eine Ganzzahl und eine Zahl sein soll:

df %>% 
  convert(int(a),
          num(b))

Ergebnisse in:

# A tibble: 2 x 2
      a     b
  <int> <dbl>
1     7  1.50
2     3  6.30
Davsjob
quelle
0

Sieht aus wie die Lösung als numerisch (Ebenen (f)) [f] funktioniert nicht mehr mit R 4.0.

Alternative Lösung:

factor2number <- function(x){
    data.frame(levels(x), 1:length(levels(x)), row.names = 1)[x, 1]
}

factor2number(yourFactor)
Life_Searching_Steps
quelle
-1

Aus den vielen Antworten, die ich lesen konnte, bestand die einzige Möglichkeit darin, die Anzahl der Variablen entsprechend der Anzahl der Faktoren zu erweitern. Wenn Sie eine Variable "pet" mit den Ebenen "dog" und "cat" haben, erhalten Sie pet_dog und pet_cat.

In meinem Fall wollte ich bei der gleichen Anzahl von Variablen bleiben, indem ich nur die Faktorvariable in eine numerische Variable übersetzte, die auf viele Variablen mit vielen Ebenen angewendet werden kann, so dass beispielsweise cat = 1 und dog = 0 sind.

Nachfolgend finden Sie die entsprechende Lösung:

crime <- data.frame(city = c("SF", "SF", "NYC"),
                    year = c(1990, 2000, 1990),
                    crime = 1:3)

indx <- sapply(crime, is.factor)

crime[indx] <- lapply(crime[indx], function(x){ 
  listOri <- unique(x)
  listMod <- seq_along(listOri)
  res <- factor(x, levels=listOri)
  res <- as.numeric(res)
  return(res)
}
)
Xavier Prudent
quelle
-2

spät , um das Spiel, zufällig fand ich trimws()umwandeln kann factor(3:5)zu c("3","4","5"). Dann können Sie anrufen as.numeric(). Das ist:

as.numeric(trimws(x_factor_var))
Jerry T.
quelle
3
Gibt es einen Grund, warum Sie die Verwendung von trimwsover empfehlen würden, as.characterwie in der akzeptierten Antwort beschrieben? Mir scheint, es sei denn, Sie hatten tatsächlich Leerzeichen, die Sie entfernen mussten, trimwswerden nur eine Menge unnötiger regulärer Ausdrucksarbeiten ausführen, um das gleiche Ergebnis zurückzugeben.
MrFlick
as.numeric (Levels (f)) [f] ist möglicherweise etwas verwirrend und für Anfänger schwer zu merken. trimws schadet nicht.
Jerry T