2016-10-06 1 views
5

Für diejenigen, die nicht vertraut sind, bezieht sich die Ein-Hot-Codierung einfach auf das Konvertieren einer Spalte von Kategorien (d. H. Eines Faktors) in mehrere Spalten binärer Indikatorvariablen, wobei jede neue Spalte einer der Klassen der ursprünglichen Spalte entspricht. In diesem Beispiel wird es besser erklären:Wie kann man die Faktorvariablen mit data.table kodieren?

dt <- data.table(
    ID=1:5, 
    Color=factor(c("green", "red", "red", "blue", "green"), levels=c("blue", "green", "red", "purple")), 
    Shape=factor(c("square", "triangle", "square", "triangle", "cirlce")) 
) 

dt 
    ID Color Shape 
1: 1 green square 
2: 2 red triangle 
3: 3 red square 
4: 4 blue triangle 
5: 5 green cirlce 

# one hot encode the colors 
color.binarized <- dcast(dt[, list(V1=1, ID, Color)], ID ~ Color, fun=sum, value.var="V1", drop=c(TRUE, FALSE)) 

# Prepend Color_ in front of each one-hot-encoded feature 
setnames(color.binarized, setdiff(colnames(color.binarized), "ID"), paste0("Color_", setdiff(colnames(color.binarized), "ID"))) 

# one hot encode the shapes 
shape.binarized <- dcast(dt[, list(V1=1, ID, Shape)], ID ~ Shape, fun=sum, value.var="V1", drop=c(TRUE, FALSE)) 

# Prepend Shape_ in front of each one-hot-encoded feature 
setnames(shape.binarized, setdiff(colnames(shape.binarized), "ID"), paste0("Shape_", setdiff(colnames(shape.binarized), "ID"))) 

# Join one-hot tables with original dataset 
dt <- dt[color.binarized, on="ID"] 
dt <- dt[shape.binarized, on="ID"] 

dt 
    ID Color Shape Color_blue Color_green Color_red Color_purple Shape_cirlce Shape_square Shape_triangle 
1: 1 green square   0   1   0   0   0   1    0 
2: 2 red triangle   0   0   1   0   0   0    1 
3: 3 red square   0   0   1   0   0   1    0 
4: 4 blue triangle   1   0   0   0   0   0    1 
5: 5 green cirlce   0   1   0   0   1   0    0 

Das ist etwas, das ich viel zu tun, und wie Sie es ist ziemlich langweilig sehen (vor allem, wenn meine Daten viele Faktor Spalten). Gibt es einen einfacheren Weg, dies mit data.table zu tun? Insbesondere nahm ich dcast mich zu einem Hot-kodieren mehrere Spalten auf einmal erlauben würde, wenn ich so etwas wie

dcast(dt[, list(V1=1, ID, Color, Shape)], ID ~ Color + Shape, fun=sum, value.var="V1", drop=c(TRUE, FALSE)) 

Versuchen Sie, ich Spalte Kombinationen

ID blue_cirlce blue_square blue_triangle green_cirlce green_square green_triangle red_cirlce red_square red_triangle purple_cirlce purple_square purple_triangle 
1: 1   0   0    0   0   1    0   0   0   0    0    0    0 
2: 2   0   0    0   0   0    0   0   0   1    0    0    0 
3: 3   0   0    0   0   0    0   0   1   0    0    0    0 
4: 4   0   0    1   0   0    0   0   0   0    0    0    0 
5: 5   0   0    0   1   0    0   0   0   0    0    0    0 
+0

Für OHE ist es besser, mit Sparse-Matrizen zu arbeiten. –

+0

@DavidArenburg danke für die schnelle Antwort. In Produktionsmodellen tue ich das normalerweise, aber wenn ich an neuen Ideen bastle und kleine Datensätze teste, mag ich die Verwendung von data.table, weil es einfacher ist, zu betrachten/plotten/subset – Ben

+1

Ok, dann würde ich einfach 'dcast (schmelzen (dt , 1), ID ~ Wert, Länge) '. Es gibt wahrscheinlich einen Betrüger dafür irgendwo –

Antwort

5

Hier gehen Sie:

dcast(melt(dt, id.vars='ID'), ID ~ variable + value, fun = length) 
# ID Color_blue Color_green Color_red Shape_cirlce Shape_square Shape_triangle 
#1: 1   0   1   0   0   1    0 
#2: 2   0   0   1   0   0    1 
#3: 3   0   0   1   0   1    0 
#4: 4   1   0   0   0   0    1 
#5: 5   0   1   0   1   0    0 

Um die fehlenden Faktoren erhalten Sie das tun können, folgende:

res = dcast(melt(dt, id = 'ID', value.factor = T), ID ~ value, drop = F, fun = length) 
setnames(res, c("ID", unlist(lapply(2:ncol(dt), 
          function(i) paste(names(dt)[i], levels(dt[[i]]), sep = "_"))))) 
res 
# ID Color_blue Color_green Color_red Color_purple Shape_cirlce Shape_square Shape_triangle 
#1: 1   0   1   0   0   0   1    0 
#2: 2   0   0   1   0   0   0    1 
#3: 3   0   0   1   0   0   1    0 
#4: 4   1   0   0   0   0   0    1 
#5: 5   0   1   0   0   1   0    0 
+0

Ah, das sieht so elegant aus, aber leider fehlt Color_purple (eine ungenutzte Farbstufe). – Ben

+0

@Ben siehe bearbeiten für eine Möglichkeit, das hinzufügen – eddi

+0

Sieht aus wie ich die Waffe sprang. Leider funktioniert das nur, wenn die Ebenen jeder Faktorspalte völlig verschieden sind. Ziemlich sicher, ich kann es aber reparieren. Du hast mich 90% des Weges dorthin geschafft. – Ben

1

bekommen Wenn postet niemand eine saubere Weg, dies jedes Mal von Hand zu schreiben, können Sie immer eine Funktion/Makro machen:

OHE <- function(dt, grp, encodeCols) { 
     grpSymb = as.symbol(grp) 
     for (col in encodeCols) { 
       colSymb = as.symbol(col) 
       eval(bquote(
          dt[, .SD 
           ][, V1 := 1 
           ][, dcast(.SD, .(grpSymb) ~ .(colSymb), fun=sum, value.var='V1') 
           ][, setnames(.SD, setdiff(colnames(.SD), grp), sprintf("%s_%s", col, setdiff(colnames(.SD), grp))) 
           ][, dt <<- dt[.SD, on=grp] 
           ] 
          )) 
     } 
     dt 
} 

dtOHE = OHE(dt, 'ID', c('Color', 'Shape')) 
dtOHE 

    ID Color Shape Color_blue Color_green Color_red Shape_cirlce Shape_square Shape_triangle 
1: 1 green square   0   1   0   0   1    0 
2: 2 red triangle   0   0   1   0   0    1 
3: 3 red square   0   0   1   0   1    0 
4: 4 blue triangle   1   0   0   0   0    1 
5: 5 green cirlce   0   1   0   1   0    0 
5

model.matrix Verwendung:

> cbind(dt[, .(ID)], model.matrix(~ Color + Shape, dt)) 
    ID (Intercept) Colorgreen Colorred Colorpurple Shapesquare Shapetriangle 
1: 1   1   1  0   0   1    0 
2: 2   1   0  1   0   0    1 
3: 3   1   0  1   0   1    0 
4: 4   1   0  0   0   0    1 
5: 5   1   1  0   0   0    0 

Dies ist am sinnvollsten, wenn Sie modellieren.

Wenn Sie den Schnittpunkt (und Wiederherstellen der aliased Spalte für die erste Variable) zu unterdrücken:

> cbind(dt[, .(ID)], model.matrix(~ Color + Shape - 1, dt)) 
    ID Colorblue Colorgreen Colorred Colorpurple Shapesquare Shapetriangle 
1: 1   0   1  0   0   1    0 
2: 2   0   0  1   0   0    1 
3: 3   0   0  1   0   1    0 
4: 4   1   0  0   0   0    1 
5: 5   0   1  0   0   0    0 
+0

' Matrix :: sparse.model.matrix' wäre besser. –

+1

'Shapecirlce' fehlt ...? – eddi

+2

@eddi 'Shapecircle' kann aus den Werten von' Shapesquare' und 'Shapetriangle' abgeleitet werden. Die Darstellung von n Ebenen erfordert generell n-1 Spalten. –

2

Hier ist eine verallgemeinerte Version von eddi Lösung:

one_hot <- function(dt, cols="auto", dropCols=TRUE, dropUnusedLevels=FALSE){ 
    # One-Hot-Encode unordered factors in a data.table 
    # If cols = "auto", each unordered factor column in dt will be encoded. (Or specifcy a vector of column names to encode) 
    # If dropCols=TRUE, the original factor columns are dropped 
    # If dropUnusedLevels = TRUE, unused factor levels are dropped 

    # Automatically get the unordered factor columns 
    if(cols[1] == "auto") cols <- colnames(dt)[which(sapply(dt, function(x) is.factor(x) & !is.ordered(x)))] 

    # Build tempDT containing and ID column and 'cols' columns 
    tempDT <- dt[, cols, with=FALSE] 
    tempDT[, ID := .I] 
    setcolorder(tempDT, unique(c("ID", colnames(tempDT)))) 
    for(col in cols) set(tempDT, j=col, value=factor(paste(col, tempDT[[col]], sep="_"), levels=paste(col, levels(tempDT[[col]]), sep="_"))) 

    # One-hot-encode 
    if(dropUnusedLevels == TRUE){ 
    newCols <- dcast(melt(tempDT, id = 'ID', value.factor = T), ID ~ value, drop = T, fun = length) 
    } else{ 
    newCols <- dcast(melt(tempDT, id = 'ID', value.factor = T), ID ~ value, drop = F, fun = length) 
    } 

    # Combine binarized columns with the original dataset 
    result <- cbind(dt, newCols[, !"ID"]) 

    # If dropCols = TRUE, remove the original factor columns 
    if(dropCols == TRUE){ 
    result <- result[, !cols, with=FALSE] 
    } 

    return(result) 
} 

Beachten Sie, dass für große Datensätze wahrscheinlich ist es besser Matrix::sparse.model.matrix

Update zu verwenden (2017)

Dies ist nun im Paket mltools.

+0

Ihre Funktion funktioniert gut für mich, aber da es n Faktor Ebenen in n Spalten dreht, ist es nicht nützlich für die Erstellung von Modellen, die empfindlich für Multikollinearität sind. Gibt es eine angepasste Version Ihrer Funktion, die n-1 Pseudospalten pro Faktorspalte erzeugt? – Constantin

+0

@Constantin Nein, aber Sie könnten einfach eine der Spalten nach der Codierung fallen lassen. – Ben

Verwandte Themen