Konvertieren Sie Spaltenklassen in data.table

118

Ich habe ein Problem bei der Verwendung von data.table: Wie konvertiere ich Spaltenklassen? Hier ein einfaches Beispiel: Mit data.frame habe ich kein Problem beim Konvertieren, mit data.table weiß ich einfach nicht wie:

df <- data.frame(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
#One way: http://stackoverflow.com/questions/2851015/r-convert-data-frame-columns-from-factors-to-characters
df <- data.frame(lapply(df, as.character), stringsAsFactors=FALSE)
#Another way
df[, "value"] <- as.numeric(df[, "value"])

library(data.table)
dt <- data.table(ID=c(rep("A", 5), rep("B",5)), Quarter=c(1:5, 1:5), value=rnorm(10))
dt <- data.table(lapply(dt, as.character), stringsAsFactors=FALSE) 
#Error in rep("", ncol(xi)) : invalid 'times' argument
#Produces error, does data.table not have the option stringsAsFactors?
dt[, "ID", with=FALSE] <- as.character(dt[, "ID", with=FALSE]) 
#Produces error: Error in `[<-.data.table`(`*tmp*`, , "ID", with = FALSE, value = "c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2)") : 
#unused argument(s) (with = FALSE)

Vermisse ich hier etwas Offensichtliches?

Update aufgrund von Matthews Beitrag: Ich habe zuvor eine ältere Version verwendet, aber auch nach dem Update auf 1.6.6 (die Version, die ich jetzt verwende) wird immer noch eine Fehlermeldung angezeigt.

Update 2: Angenommen, ich möchte jede Spalte der Klasse "Faktor" in eine Spalte "Zeichen" konvertieren, weiß aber nicht im Voraus, welche Spalte zu welcher Klasse gehört. Mit einem data.frame kann ich Folgendes tun:

classes <- as.character(sapply(df, class))
colClasses <- which(classes=="factor")
df[, colClasses] <- sapply(df[, colClasses], as.character)

Kann ich mit data.table etwas Ähnliches machen?

Update 3:

sessionInfo () R Version 2.13.1 (08.07.2011) Plattform: x86_64-pc-mingw32 / x64 (64-Bit)

locale:
[1] C

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] data.table_1.6.6

loaded via a namespace (and not attached):
[1] tools_2.13.1
Christoph_J
quelle
Die "[" Operatorargumente in data.tableMethoden unterscheiden sich von denen fürdata.frame
IRTFM
1
Bitte fügen Sie den tatsächlichen Fehler ein und nicht #Produces error. +1 sowieso. Ich bekomme keine Fehlermeldung, welche Version hast du? Es gibt jedoch ein Problem in diesem Bereich, das bereits erwähnt wurde. FR # 1224 und FR # 1493 haben eine hohe Priorität. Andries Antwort ist jedoch der beste Weg.
Matt Dowle
Sorry @MatthewDowle, dass ich das in meiner Frage verpasst habe. Ich habe meinen Beitrag aktualisiert.
Christoph_J
1
@Christoph_J Danke. Sind Sie sich über diesen invalid times argumentFehler sicher ? Arbeite gut für mich. Welche Version hast du?
Matt Dowle
Ich habe meinen Beitrag mit sessionInfo () aktualisiert. Ich habe es heute jedoch auf meiner Arbeitsmaschine überprüft. Gestern ist auf meinem Heimcomputer (Ubuntu) der gleiche Fehler aufgetreten. Ich werde R aktualisieren und sehen, ob das Problem immer noch besteht.
Christoph_J

Antworten:

104

Für eine einzelne Spalte:

dtnew <- dt[, Quarter:=as.character(Quarter)]
str(dtnew)

Classes ‘data.table’ and 'data.frame':  10 obs. of  3 variables:
 $ ID     : Factor w/ 2 levels "A","B": 1 1 1 1 1 2 2 2 2 2
 $ Quarter: chr  "1" "2" "3" "4" ...
 $ value  : num  -0.838 0.146 -1.059 -1.197 0.282 ...

Verwenden von lapplyund as.character:

dtnew <- dt[, lapply(.SD, as.character), by=ID]
str(dtnew)

Classes ‘data.table’ and 'data.frame':  10 obs. of  3 variables:
 $ ID     : Factor w/ 2 levels "A","B": 1 1 1 1 1 2 2 2 2 2
 $ Quarter: chr  "1" "2" "3" "4" ...
 $ value  : chr  "1.487145280568" "-0.827845218358881" "0.028977182770002" "1.35392750102305" ...
Andrie
quelle
2
@Christoph_J Bitte zeigen Sie den Gruppierungsbefehl an, mit dem Sie zu kämpfen haben (das eigentliche Problem). Denken Sie, Sie haben vielleicht etwas Einfaches verpasst. Warum versuchen Sie, Spaltenklassen zu konvertieren?
Matt Dowle
1
@Christoph_J Wenn Sie Schwierigkeiten haben, data.tables zu manipulieren, konvertieren Sie sie einfach vorübergehend in data.frames, bereinigen Sie die Daten und konvertieren Sie sie dann zurück in data.tables.
Andrie
17
Was ist die idiomatische Methode, um dies für eine Teilmenge von Spalten (anstelle von allen) zu tun? Ich habe einen Zeichenvektor convcolsfür Spalten definiert. dt[,lapply(.SD,as.numeric),.SDcols=convcols]ist fast augenblicklich, während dt[,convcols:=lapply(.SD,as.numeric),.SDcols=convcols]R fast einfriert, also schätze ich, dass ich es falsch mache. Danke
Frank
4
@Frank Siehe Matt Dowles Kommentar zu Geneoramas Antwort unten ( stackoverflow.com/questions/7813578/… ); es war hilfreich und idiomatisch genug für mich [Zitat starten] Eine andere und einfachere Möglichkeit ist es, set()zB for (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))[Zitat beenden] zu verwenden
swihart
4
Warum verwenden Sie die Option by = ID?
Skan
48

Versuche dies

DT <- data.table(X1 = c("a", "b"), X2 = c(1,2), X3 = c("hello", "you"))
changeCols <- colnames(DT)[which(as.vector(DT[,lapply(.SD, class)]) == "character")]

DT[,(changeCols):= lapply(.SD, as.factor), .SDcols = changeCols]
Nera
quelle
7
Jetzt können Sie Filterdie Spalten mithilfe der Funktion identifizieren, zum Beispiel: changeCols<- names(Filter(is.character, DT))
David Leal
1
IMO ist dies die bessere Antwort, aus dem Grund, den ich in der gewählten Antwort angegeben habe.
James Hirschorn
oder genauer : changeCols <- names(DT)[sapply(DT, is.character)].
Sindri_baldur
8

Wenn Sie den Kommentar von Matt Dowle zu Geneoramas Antwort ( https://stackoverflow.com/a/20808945/4241780 ) ansprechen , um ihn offensichtlicher zu machen (wie empfohlen ), können Sie ihn verwenden for(...)set(...).


library(data.table)

DT = data.table(a = LETTERS[c(3L,1:3)], b = 4:7, c = letters[1:4])
DT1 <- copy(DT)
names_factors <- c("a", "c")

for(col in names_factors)
  set(DT, j = col, value = as.factor(DT[[col]]))

sapply(DT, class)
#>         a         b         c 
#>  "factor" "integer"  "factor"

Erstellt am 12.02.2020 durch das reprex-Paket (v0.3.0)

Weitere Informationen finden Sie in einem weiteren Kommentar von Matt unter https://stackoverflow.com/a/33000778/4241780 .

Bearbeiten.

Wie von Espen und in angegeben help(set), jkann dies "Spaltenname (n) (Zeichen) oder Nummer (n) (Ganzzahl) sein, denen ein Wert zugewiesen werden soll, wenn Spalte (n) bereits vorhanden sind". So names_factors <- c(1L, 3L)wird es auch funktionieren.

JWilliman
quelle
Vielleicht möchten Sie hinzufügen, was names_factorshier ist. Ich denke, es stammt von stackoverflow.com/a/20808945/1666063, also names_factors = c('fac1', 'fac2')in diesem Fall - das sind Spaltennamen. Aber es könnten auch Spaltennummern sein, zum Beispiel 1; ncol (dt), die alle Spalten konvertieren würden
Espen Riskedal
@EspenRiskedal Vielen Dank, ich habe den Beitrag bearbeitet, um ihn offensichtlicher zu machen.
JWilliman
2

Dies ist ein schlechter Weg, um es zu tun! Ich lasse diese Antwort nur für den Fall, dass sie andere seltsame Probleme löst. Diese besseren Methoden sind wahrscheinlich teilweise das Ergebnis neuerer data.table-Versionen. Es lohnt sich also, diesen harten Weg zu dokumentieren. Außerdem ist dies ein schönes Syntaxbeispiel für die eval substituteSyntax.

library(data.table)
dt <- data.table(ID = c(rep("A", 5), rep("B",5)), 
                 fac1 = c(1:5, 1:5), 
                 fac2 = c(1:5, 1:5) * 2, 
                 val1 = rnorm(10),
                 val2 = rnorm(10))

names_factors = c('fac1', 'fac2')
names_values = c('val1', 'val2')

for (col in names_factors){
  e = substitute(X := as.factor(X), list(X = as.symbol(col)))
  dt[ , eval(e)]
}
for (col in names_values){
  e = substitute(X := as.numeric(X), list(X = as.symbol(col)))
  dt[ , eval(e)]
}

str(dt)

was gibt dir

Classes ‘data.table’ and 'data.frame':  10 obs. of  5 variables:
 $ ID  : chr  "A" "A" "A" "A" ...
 $ fac1: Factor w/ 5 levels "1","2","3","4",..: 1 2 3 4 5 1 2 3 4 5
 $ fac2: Factor w/ 5 levels "2","4","6","8",..: 1 2 3 4 5 1 2 3 4 5
 $ val1: num  0.0459 2.0113 0.5186 -0.8348 -0.2185 ...
 $ val2: num  -0.0688 0.6544 0.267 -0.1322 -0.4893 ...
 - attr(*, ".internal.selfref")=<externalptr> 
Genorama
quelle
42
Eine andere und einfachere Möglichkeit ist die Verwendung von set()zBfor (col in names_factors) set(dt, j=col, value=as.factor(dt[[col]]))
Matt Dowle
1
Ich denke, meine Antwort erreicht dies in einer Zeile für alle Versionen. Ich bin mir nicht sicher, ob dies setangemessener ist.
Ben Rollert
1
Weitere Informationen for(...)set(...)hier: stackoverflow.com/a/33000778/403310
Matt Dowle
1
@skan Gute Frage. Wenn Sie es vorher nicht finden können, stellen Sie bitte eine neue Frage. Hilft anderen in Zukunft.
Matt Dowle
1
@skan so habe ich es gemacht: github.com/geneorama/geneorama/blob/master/R/…
geneorama
0

Ich habe verschiedene Ansätze ausprobiert.

# BY {dplyr}
data.table(ID      = c(rep("A", 5), rep("B",5)), 
           Quarter = c(1:5, 1:5), 
           value   = rnorm(10)) -> df1
df1 %<>% dplyr::mutate(ID      = as.factor(ID),
                       Quarter = as.character(Quarter))
# check classes
dplyr::glimpse(df1)
# Observations: 10
# Variables: 3
# $ ID      (fctr) A, A, A, A, A, B, B, B, B, B
# $ Quarter (chr) "1", "2", "3", "4", "5", "1", "2", "3", "4", "5"
# $ value   (dbl) -0.07676732, 0.25376110, 2.47192852, 0.84929175, -0.13567312,  -0.94224435, 0.80213218, -0.89652819...

, oder andernfalls

# from list to data.table using data.table::setDT
list(ID      = as.factor(c(rep("A", 5), rep("B",5))), 
     Quarter = as.character(c(1:5, 1:5)), 
     value   = rnorm(10)) %>% setDT(list.df) -> df2
class(df2)
# [1] "data.table" "data.frame"
Uribo
quelle
0

Ich biete eine allgemeinere und sicherere Möglichkeit, dieses Zeug zu machen,

".." <- function (x) 
{
  stopifnot(inherits(x, "character"))
  stopifnot(length(x) == 1)
  get(x, parent.frame(4))
}


set_colclass <- function(x, class){
  stopifnot(all(class %in% c("integer", "numeric", "double","factor","character")))
  for(i in intersect(names(class), names(x))){
    f <- get(paste0("as.", class[i]))
    x[, (..("i")):=..("f")(get(..("i")))]
  }
  invisible(x)
}

Die Funktion ..stellt sicher, dass wir eine Variable aus dem Bereich von data.table herausholen. set_colclass legt die Klassen Ihrer Spalten fest. Sie können es so verwenden:

dt <- data.table(i=1:3,f=3:1)
set_colclass(dt, c(i="character"))
class(dt$i)
liqg3
quelle
-1

Wenn Sie eine Liste mit Spaltennamen in data.table haben, möchten Sie die Klasse do ändern:

convert_to_character <- c("Quarter", "value")

dt[, convert_to_character] <- dt[, lapply(.SD, as.character), .SDcols = convert_to_character]
Emil Lykke Jensen
quelle
Diese Antwort ist im Wesentlichen eine schlechte Version von @ Neras Antwort unten. dt[, c(convert_to_character) := lapply(.SD, as.character), .SDcols=convert_to_character]Führen Sie dies einfach durch Referenz zu, anstatt die langsamere Zuweisung von data.frame zu verwenden.
Altabq
-3

Versuchen:

dt <- data.table(A = c(1:5), 
                 B= c(11:15))

x <- ncol(dt)

for(i in 1:x) 
{
     dt[[i]] <- as.character(dt[[i]])
}
user151444
quelle