Ändern Sie die Klasse für viele Spalten in einem Datenrahmen von Faktor zu Numerisch


82

Was ist der schnellste / beste Weg, um eine große Anzahl von Spalten vom Faktor in einen numerischen zu ändern?

Ich habe den folgenden Code verwendet, aber er scheint meine Daten neu geordnet zu haben.

> head(stats[,1:2])
  rk                 team
1  1 Washington Capitals*
2  2     San Jose Sharks*
3  3  Chicago Blackhawks*
4  4     Phoenix Coyotes*
5  5   New Jersey Devils*
6  6   Vancouver Canucks*

for(i in c(1,3:ncol(stats))) {
    stats[,i] <- as.numeric(stats[,i])
}

> head(stats[,1:2])
  rk                 team
1  2 Washington Capitals*
2 13     San Jose Sharks*
3 24  Chicago Blackhawks*
4 26     Phoenix Coyotes*
5 27   New Jersey Devils*
6 28   Vancouver Canucks*

Was ist der beste Weg, ohne jede Spalte wie folgt zu benennen:

df$colname <- as.numeric(ds$colname)

4
Gibt es keine generische Lösung? Einige der hier vorgeschlagenen Lösungen funktionieren nur mit Faktoren, andere immer außer mit Faktoren und so weiter ...
Skan

Antworten:


56

Nach Ramnaths Antwort ist das Verhalten, das Sie erleben, das Ergebnis der as.numeric(x)Rückgabe der internen numerischen Darstellung des Faktors xauf der R-Ebene. Wenn Sie die Zahlen beibehalten möchten, die die Ebenen des Faktors darstellen (und nicht deren interne Darstellung), müssen Sie as.character()gemäß Ramnaths Beispiel zuerst in Zeichen konvertieren .

Ihre forSchleife ist genauso vernünftig wie ein applyAufruf und möglicherweise etwas besser lesbar, was die Absicht des Codes ist. Ändern Sie einfach diese Zeile:

stats[,i] <- as.numeric(stats[,i])

lesen

stats[,i] <- as.numeric(as.character(stats[,i]))

Dies ist FAQ 7.10 in den R FAQ.

HTH


2
Keine Notwendigkeit für irgendeine Art von Schleife. Verwenden Sie einfach die Indizes und heben Sie die Liste auf (). Bearbeiten: Ich habe eine Antwort hinzugefügt, die dies veranschaulicht.
Joris Meys

Dieser Ansatz funktioniert nur in diesem speziellen Fall. Ich habe versucht, damit Spalten zu konvertieren, factorund es hat nicht funktioniert. sapplyoder mutate_ifscheinen allgemein anwendbare Lösungen zu sein.
Leo

@ Leo Care zu erweitern, weil ich sicher weiß, dass dies funktioniert. Es ist genau die gleiche Lösung wie die folgende von Ramnath, außer dass er applydie Schleife ausführt und das OP forexplizit eine Schleife verwendet. Tatsächlich verwenden alle hoch bewerteten Antworten die as.numeric(as.character())Redewendung.
Gavin Simpson

Ja, es funktioniert, um die Klasse mehrerer Spalten in zu ändern numeric, aber es funktioniert nicht umgekehrt (um die Klasse mehrerer Spalten in zu ändern factor). Wenn Sie benötigte Indizes verwenden unlist()und diese auf Spalten mit Zeichen anwenden, werden alle einzelnen Zeichen aufgelistet, sodass sie beim Zurücksetzen der Ausgabe nicht mehr funktionieren stats[,i]. Überprüfen Sie die Antwort hier: stackoverflow.com/questions/45713473/…
Leo

@ Leo natürlich funktioniert es nicht umgekehrt! Was um alles in der Welt hat Ihnen den Eindruck vermittelt, dass es so sein würde? Es wurde nie entworfen und das OP hat nie danach gefragt. Schwer zu beantwortende Fragen, die nicht gestellt werden. Wenn Sie in einen Faktor konvertieren möchten, verwenden Sie as.factor()anstelle von as.numeric(as.character())hier und es wird gut funktionieren. Natürlich, wenn Sie eine Mischung von Spalten haben, müssen Sie iselektiv auswählen , aber das ist auch trivial.
Gavin Simpson

73

Sie müssen vorsichtig sein, wenn Sie Faktoren in numerische ändern. Hier ist eine Codezeile, die eine Reihe von Spalten von Faktor zu Numerisch ändert. Ich gehe hier davon aus, dass die Spalten, die in numerisch geändert werden sollen, 1, 3, 4 bzw. 5 sind. Sie können es entsprechend ändern

cols = c(1, 3, 4, 5);    
df[,cols] = apply(df[,cols], 2, function(x) as.numeric(as.character(x)));

3
Dies funktioniert nicht richtig. Beispiel : x<-as.factor(1:3); df<-data.frame(a=x,y=runif(3),b=x,c=x,d=x). Ich denke nicht, dass applydas für diese Art von Problemen angemessen ist.
Marek

1
anwenden funktioniert in diesen Situationen perfekt. Der Fehler in meinem Code war die Verwendung von margin = 1 anstelle von 2, da die Funktion spaltenweise angewendet werden muss. Ich habe meine Antwort entsprechend bearbeitet.
Ramnath

Jetzt funktioniert es. Aber ich denke, es könnte ohne getan werden apply. Überprüfen Sie meine Bearbeitung.
Marek

2
... oder Joris antwortet mit unlist. Eine as.characterKonvertierung in Ihrer Lösung ist nicht erforderlich, da die applyKonvertierung df[,cols]in characterso apply(df[,cols], 2, function(x) as.numeric(x))auch funktioniert.
Marek

@ Ramnath , warum benutzt du =? Warum nicht <-?
Kittygirl

40

Dies kann in einer Zeile erfolgen, es ist keine Schleife erforderlich, sei es eine for-Schleife oder eine Anwendung. Verwenden Sie stattdessen unlist ():

# testdata
Df <- data.frame(
  x = as.factor(sample(1:5,30,r=TRUE)),
  y = as.factor(sample(1:5,30,r=TRUE)),
  z = as.factor(sample(1:5,30,r=TRUE)),
  w = as.factor(sample(1:5,30,r=TRUE))
)
##

Df[,c("y","w")] <- as.numeric(as.character(unlist(Df[,c("y","w")])))

str(Df)

Bearbeiten: Für Ihren Code wird dies:

id <- c(1,3:ncol(stats))) 
stats[,id] <- as.numeric(as.character(unlist(stats[,id])))

Wenn Sie einen einspaltigen Datenrahmen haben und nicht möchten, dass die automatische Dimensionsreduktion von R ihn in einen Vektor konvertiert, müssen Sie das drop=FALSEArgument hinzufügen .


1
Kleine Verbesserung könnte Einstellung recursiveund use.namesParameter von unlistbeiden sein FALSE.
Marek

@Marek: wahr. Ich liebe dieses Spiel :-)
Joris Meys

Ich werde nur für diejenigen hinzufügen, die in Zukunft nach Antworten suchen. Dies entspricht nicht der Methode von op + gavin, wenn der Datenrahmen nur aus einer Spalte besteht. In diesem Fall wird es in einen Vektor konvertiert, während OPs immer noch ein Datenrahmen sind.
themartinmcfly

1
für diejenigen, die mit tidyverse arbeiten: Interessanterweise scheint dies nicht zu funktionieren, wenn das Objekt auch ein tibble ist: Der Code schlägt nachDf <- tibble::as_tibble(Df)
Tjebo

1
@Tjebo mit den Aktualisierungen von tibble und der Umleitung zwischen tibbles und Datenrahmen ist dieser alte Ansatz in der Tat nicht die beste Option in tidyverse. Nutzen Sie die Tidyselect-Funktionen besser in Kombination mit mutate_if. Oder welcher neue Ansatz auch immer in der nächsten Iteration von dplyr...
Joris Meys

30

Ich weiß, dass diese Frage schon lange gelöst ist, aber ich hatte kürzlich ein ähnliches Problem und denke, ich habe eine etwas elegantere und funktionalere Lösung gefunden, obwohl dafür das magrittr-Paket erforderlich ist.

library(magrittr)
cols = c(1, 3, 4, 5)
df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))

Der %<>%Bediener leitet und weist neu zu, was sehr nützlich ist, um die Datenbereinigung und -transformation einfach zu halten. Jetzt ist die Listenanwendungsfunktion viel einfacher zu lesen, indem nur die Funktion angegeben wird, die Sie anwenden möchten.


2
saubere Lösung. Sie haben eine Klammer vergessen, aber ich kann diese Bearbeitung nicht vornehmen, weil sie zu kurz ist:df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
epo3

1
Ich glaube nicht , dass Sie auch , dass in lappy wickeln müssen df[,cols] %<>% as.numeric(as.character(.))die gleiche funktioniert
Nate

Wenn ich diesen Befehl versuche, erhalte ich den folgenden FehlerError in [.data.table(Results, , cols) : j (the 2nd argument inside [...]) is a single symbol but column name 'cols' is not found. Perhaps you intended DT[,..cols] or DT[,cols,with=FALSE]. This difference to data.frame is deliberate and explained in FAQ 1.1.
Urvah Shabbir

Code ist wie:cols <- c("a","b"); df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))
Urvah Shabbir

Klammer jetzt hinzugefügt.
Joe

9

Hier sind einige dplyrOptionen:

# by column type:
df %>% 
  mutate_if(is.factor, ~as.numeric(as.character(.)))

# by specific columns:
df %>% 
  mutate_at(vars(x, y, z), ~as.numeric(as.character(.))) 

# all columns:
df %>% 
  mutate_all(~as.numeric(as.character(.))) 

6

Ich denke, dass ucfagls herausgefunden hat, warum Ihre Schleife nicht funktioniert.

Falls Sie immer noch keine Schleife verwenden möchten, finden Sie hier eine Lösung mit lapply:

factorToNumeric <- function(f) as.numeric(levels(f))[as.integer(f)] 
cols <- c(1, 3:ncol(stats))
stats[cols] <- lapply(stats[cols], factorToNumeric)

Bearbeiten. Ich habe eine einfachere Lösung gefunden. Es scheint, dass as.matrixin Charakter konvertieren. Damit

stats[cols] <- as.numeric(as.matrix(stats[cols]))

sollte tun was du willst.


5

lapply ist so ziemlich dafür konzipiert

unfactorize<-c("colA","colB")
df[,unfactorize]<-lapply(unfactorize, function(x) as.numeric(as.character(df[,x])))

Hallo @transcom, und willkommen bei stackoverflow. Beachten Sie, dass es bei dieser Frage um die Konvertierung in eine numerische Darstellung von einem Faktor geht, nicht umgekehrt. Siehe Mareks Lösung.
Aaron verließ Stack Overflow

@ Aaron, verstanden. Ich habe diese Antwort aufgrund der Mehrdeutigkeit des OP-Titels veröffentlicht und bin davon ausgegangen, dass andere hier landen und nach einer Möglichkeit suchen, mehrere Spalten unabhängig von der Klasse einfach zu konvertieren. Wie auch immer, ich habe meine Antwort bearbeitet, um die Frage angemessener zu beantworten :)
Transcom

2

Ich habe diese Funktion in einigen anderen doppelten Threads gefunden und eine elegante und allgemeine Möglichkeit gefunden, dieses Problem zu lösen. Dieser Thread wird bei den meisten Suchanfragen zu diesem Thema zuerst angezeigt, daher teile ich ihn hier, um den Leuten Zeit zu sparen. Ich nehme das nicht gut, nur siehe die Originalbeiträge hier und hier für Details.

df <- data.frame(x = 1:10,
                 y = rep(1:2, 5),
                 k = rnorm(10, 5,2),
                 z = rep(c(2010, 2012, 2011, 2010, 1999), 2),
                 j = c(rep(c("a", "b", "c"), 3), "d"))

convert.magic <- function(obj, type){
  FUN1 <- switch(type,
                 character = as.character,
                 numeric = as.numeric,
                 factor = as.factor)
  out <- lapply(obj, FUN1)
  as.data.frame(out)
}

str(df)
str(convert.magic(df, "character"))
str(convert.magic(df, "factor"))
df[, c("x", "y")] <- convert.magic(df[, c("x", "y")], "factor")

1

Ich möchte darauf hinweisen, dass die einfache Verwendung von Indizes nicht funktioniert, wenn Sie NAs in einer Spalte haben. Wenn der Faktor NAs enthält, müssen Sie das von Ramnath bereitgestellte Apply-Skript verwenden.

Z.B

Df <- data.frame(
  x = c(NA,as.factor(sample(1:5,30,r=T))),
  y = c(NA,as.factor(sample(1:5,30,r=T))),
  z = c(NA,as.factor(sample(1:5,30,r=T))),
  w = c(NA,as.factor(sample(1:5,30,r=T)))
)

Df[,c(1:4)] <- as.numeric(as.character(Df[,c(1:4)]))

Gibt Folgendes zurück:

Warning message:
NAs introduced by coercion 

    > head(Df)
       x  y  z  w
    1 NA NA NA NA
    2 NA NA NA NA
    3 NA NA NA NA
    4 NA NA NA NA
    5 NA NA NA NA
    6 NA NA NA NA

Aber:

Df[,c(1:4)]= apply(Df[,c(1:4)], 2, function(x) as.numeric(as.character(x)))

Kehrt zurück:

> head(Df)
   x  y  z  w
1 NA NA NA NA
2  2  3  4  1
3  1  5  3  4
4  2  3  4  1
5  5  3  5  5
6  4  2  4  4

1

Sie können die unfactor()Funktion aus dem "varhandle" -Paketformular CRAN verwenden:

library("varhandle")

my_iris <- data.frame(Sepal.Length = factor(iris$Sepal.Length),
                      sample_id = factor(1:nrow(iris)))

my_iris <- unfactor(my_iris)

1

Ich mag diesen Code, weil er ziemlich praktisch ist:

  data[] <- lapply(data, function(x) type.convert(as.character(x), as.is = TRUE)) #change all vars to their best fitting data type

Es ist nicht genau das, wonach gefragt wurde (in numerisch konvertieren), aber in vielen Fällen sogar noch angemessener.


1

df$colname <- as.numeric(df$colname)

Ich habe auf diese Weise versucht, einen Spaltentyp zu ändern, und ich denke, es ist besser als viele andere Versionen, wenn Sie nicht alle Spaltentypen ändern

df$colname <- as.character(df$colname)

für umgekehrt.


0

Ich hatte Probleme, alle Spalten mit einem apply()Aufruf in numerische zu konvertieren :

apply(data, 2, as.numeric)

Das Problem stellt sich heraus, dass einige der Zeichenfolgen ein Komma enthielten - z. B. "1.024,63" anstelle von "1024,63" - und R diese Art der Formatierung von Zahlen nicht mag. Also habe ich sie entfernt und bin dann gelaufen as.numeric():

data = as.data.frame(apply(data, 2, function(x) {
  y = str_replace_all(x, ",", "") #remove commas
  return(as.numeric(y)) #then convert
}))

Beachten Sie, dass hierfür das stringr-Paket geladen werden muss.


0

Das hat bei mir funktioniert. Die apply()Funktion versucht, df zur Matrix zu zwingen, und gibt NAs zurück.

numeric.df <- as.data.frame(sapply(df, 2, as.numeric))


0

Basierend auf der Antwort von @ SDahm war dies eine "optimale" Lösung für meine tibble:

data %<>% lapply(type.convert) %>% as.data.table()

Dies erfordert dplyrund magrittr.


0

Ich habe ein paar davon bei einem ähnlichen Problem ausprobiert und immer wieder NAs bekommen. Base R hat einige wirklich irritierende Zwangsverhalten, die im Allgemeinen in Tidyverse-Paketen festgelegt sind. Früher habe ich sie vermieden, weil ich keine Abhängigkeiten erstellen wollte, aber sie machen das Leben so viel einfacher, dass ich mich jetzt die meiste Zeit nicht mehr darum kümmere, die Base R-Lösung herauszufinden.

Hier ist die Tidyverse-Lösung, die äußerst einfach und elegant ist:

library(purrr)

mydf <- data.frame(
  x1 = factor(c(3, 5, 4, 2, 1)),
  x2 = factor(c("A", "C", "B", "D", "E")),
  x3 = c(10, 8, 6, 4, 2))

map_df(mydf, as.numeric)

Die meisten Antworten (zumindest alle Top-Antworten) stellen sicher, dass die as.numeric(as.character())Konvertierung durchgeführt wird, um die allzu häufige Konvertierung von Ganzzahlstufen anstelle von Werten in numerische Werte zu vermeiden . Ich würde diese Antwort gerne positiv bewerten, wenn Sie diese Option zeigen.
Gregor Thomas
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.