2013-04-24 16 views
15

Original-Datenrahmen: Recode kategorische Faktor mit N Kategorien in N binären Spalten

v1 = sample(letters[1:3], 10, replace=TRUE) 
v2 = sample(letters[1:3], 10, replace=TRUE) 
df = data.frame(v1,v2) 
df 
 
    v1 v2 
1 b c 
2 a a 
3 c c 
4 b a 
5 c c 
6 c b 
7 a a 
8 a b 
9 a c 
10 a b 

Neue Datenrahmen:

new_df = data.frame(row.names=rownames(df)) 
for (i in colnames(df)) { 
    for (x in letters[1:3]) { 
     #new_df[x] = as.numeric(df[i] == x) 
     new_df[paste0(i, "_", x)] = as.numeric(df[i] == x) 
    } 
} 
 
    v1_a v1_b v1_c v2_a v2_b v2_c 
1  0 1 0 0 0 1 
2  1 0 0 1 0 0 
3  0 0 1 0 0 1 
4  0 1 0 1 0 0 
5  0 0 1 0 0 1 
6  0 0 1 0 1 0 
7  1 0 0 1 0 0 
8  1 0 0 0 1 0 
9  1 0 0 0 0 1 
10 1 0 0 0 1 0 

Für kleine Datensätze dieser ist in Ordnung, aber es wird sl für viel größere Datensätze.

Wer weiß von einer Möglichkeit, dies ohne Schleifen zu tun?

+1

Ihr erster Datenrahmen hatte zwei Variablen, aber es sieht so aus, als hätten Sie nur den zweiten konvertiert. Kannst du das ein bisschen klären? – joran

+0

Sie überschreiben Ihre Daten. Es sollte 6 Spalten in der Ausgabe haben. – Arun

+0

Entschuldigung, das war ein Fehler von mir - ich habe es im obigen Code behoben. Im obigen Beispiel sollten für jede ursprüngliche Spalte drei neue Spalten vorhanden sein. Danke, dass du das erwischt hast! –

Antwort

21

Noch besser mit Hilfe von @ AnandaMahto Suchfähigkeiten,

model.matrix(~ . + 0, data=df, contrasts.arg = lapply(df, contrasts, contrasts=FALSE)) 
# v1a v1b v1c v2a v2b v2c 
# 1 0 1 0 0 0 1 
# 2 1 0 0 1 0 0 
# 3 0 0 1 0 0 1 
# 4 0 1 0 1 0 0 
# 5 0 0 1 0 0 1 
# 6 0 0 1 0 1 0 
# 7 1 0 0 1 0 0 
# 8 1 0 0 0 1 0 
# 9 1 0 0 0 0 1 
# 10 1 0 0 0 1 0 

Ich denke, das ist das, was Sie suchen. Ich würde gerne löschen, wenn es nicht so ist. Danke an @ G.Grothiedeck (noch einmal) für die excellent usage von model.matrix!

cbind(with(df, model.matrix(~ v1 + 0)), with(df, model.matrix(~ v2 + 0))) 
# v1a v1b v1c v2a v2b v2c 
# 1 0 1 0 0 0 1 
# 2 1 0 0 1 0 0 
# 3 0 0 1 0 0 1 
# 4 0 1 0 1 0 0 
# 5 0 0 1 0 0 1 
# 6 0 0 1 0 1 0 
# 7 1 0 0 1 0 0 
# 8 1 0 0 0 1 0 
# 9 1 0 0 0 0 1 
# 10 1 0 0 0 1 0 

Hinweis: Die Ausgabe ist nur:

with(df, model.matrix(~ v2 + 0)) 

Anmerkung 2: Daraus ergibt sich eine matrix. Ziemlich offensichtlich, aber immer noch, wickeln Sie es mit as.data.frame(.), wenn Sie eine data.frame möchten.

0

Hier ist eine Lösung für die allgemeineren Fall, wenn die Menge der Buchstaben ist nicht a priori festgelegt:

convertABC <- function(x) { 

    hold <- rep(0,max(match(as.matrix(df),letters))) # pre-format output 

    codify <- function(x) {       # define function for single char 

     output <- hold        # take empty vector 
     output[match(x,letters)] <- 1    # place 1 according to letter pos 
     return(output) 
    } 

    to.return <- t(sapply(as.character(x),codify)) # apply it to whole vector 
    rownames(to.return) <- 1:nrow(to.return)   # nice rownames 
    colnames(to.return) <- do.call(c,list(letters[1:max(match(as.matrix(df),letters))])) # nice columnnames 
    return(to.return) 
} 

Diese Funktion einen Vektor von Zeichen nimmt und rekodiert es in binäre Werte. Um alle Variablen in df zu verarbeiten:

do.call(cbind,lapply(df,convertABC)) 
3

Ein ziemlich direkter Ansatz ist die Verwendung nur table auf jeder Spalte, durch die Anzahl der Zeilen, die Werte in der Spalte Tabelliermaschinen im data.frame:

allLevels <- levels(factor(unlist(df))) 
do.call(cbind, 
     lapply(df, function(x) table(sequence(nrow(df)), 
            factor(x, levels = allLevels)))) 
# a b c a b c 
# 1 0 1 0 0 0 1 
# 2 1 0 0 1 0 0 
# 3 0 0 1 0 0 1 
# 4 0 1 0 1 0 0 
# 5 0 0 1 0 0 1 
# 6 0 0 1 0 1 0 
# 7 1 0 0 1 0 0 
# 8 1 0 0 0 1 0 
# 9 1 0 0 0 0 1 
# 10 1 0 0 0 1 0 

I habe factor auf "x" verwendet, um sicherzustellen, dass selbst in Fällen, in denen beispielsweise keine "c" -Werte in einer Spalte vorhanden sind, in der Ausgabe immer noch eine "c" -Spalte mit Nullen gefüllt ist.

8

Es gibt eine Funktion in Caret-Paket, das tut, was Sie tun, dummyVars. Hier ist das Beispiel dafür Nutzung der Autoren Dokumentation genommen hat: http://topepo.github.io/caret/preprocess.html

library(earth) 
data(etitanic) 

dummies <- caret::dummyVars(survived ~ ., data = etitanic) 
head(predict(dummies, newdata = etitanic)) 

    pclass.1st pclass.2nd pclass.3rd sex.female sex.male  age sibsp parch 
1   1   0   0   1  0 29.0000  0  0 
2   1   0   0   0  1 0.9167  1  2 
3   1   0   0   1  0 2.0000  1  2 
4   1   0   0   0  1 30.0000  1  2 
5   1   0   0   1  0 25.0000  1  2 
6   1   0   0   0  1 48.0000  0  0 

Die model.matrix Optionen für den Fall nützlich sein könnten Sie spärliche Daten hatte und wollte verwenden Matrix::sparse.model.matrix

2

ich vor kurzem über kam ein anderer Weg.Ich habe festgestellt, dass, wenn Sie eine der Kontrastfunktionen mit contrasts auf FALSE eingestellt ausführen, gibt es Ihnen eine Hot-Codierung. Zum Beispiel contr.sum(5, contrasts = FALSE) gibt

1 2 3 4 5 
1 1 0 0 0 0 
2 0 1 0 0 0 
3 0 0 1 0 0 
4 0 0 0 1 0 
5 0 0 0 0 1 

Um dieses Verhalten für alle Ihre Faktoren zu erhalten, können Sie eine neue Kontrastfunktion erstellen und als Standard festgelegt. Zum Beispiel

contr.onehot = function (n, contrasts, sparse = FALSE) { 
    contr.sum(n = n, contrasts = FALSE, sparse = sparse) 
} 

options(contrasts = c("contr.onehot", "contr.onehot")) 
model.matrix(~ . - 1, data = df) 

Dies führt zu

v1a v1b v1c v2a v2b v2c 
1 0 0 1 0 0 1 
2 0 1 0 1 0 0 
3 0 0 1 0 1 0 
4 1 0 0 0 1 0 
5 0 1 0 0 1 0 
6 0 1 0 0 0 1 
7 1 0 0 0 1 0 
8 0 1 0 0 1 0 
9 0 1 0 1 0 0 
10 0 0 1 0 0 1 
1

Nur eine geschlossene Frage hier gerichtet gesehen, und niemand hat mit dem dummies Paket noch erwähnt:

Sie Ihre Variablen umkodieren kann die Verwendung von dummy.data.frame() Funktion, die auf model.matrix() gebaut wird, hat aber einfachere Syntax, einige gute Optionen und gibt einen Datenrahmen zurück:

> dummy.data.frame(df, sep="_") 
    v1_a v1_b v1_c v2_a v2_b v2_c 
1  0 1 0 0 0 1 
2  1 0 0 1 0 0 
3  0 0 1 0 0 1 
4  0 1 0 1 0 0 
5  0 0 1 0 0 1 
6  0 0 1 0 1 0 
7  1 0 0 1 0 0 
8  1 0 0 0 1 0 
9  1 0 0 0 0 1 
10 1 0 0 0 1 0 

einige schöne Aspekte dieser Funktion ist, dass Sie einfach sollte delimeter für die neuen Namen (sep=), lassen Sie nicht-codierte Variablen (all=F) und kommt mit seiner eigenen Wahl dummy.classes das Ihnen erlaubt, zu spezifizieren, welche Klassen von Spalte angeben codiert sein.

Sie können auch einfach die Funktion dummy() verwenden, um dies auf nur eine Spalte anzuwenden.