Automatisches Erweitern eines R-Faktors in eine Sammlung von 1/0 Indikatorvariablen für jede Faktorstufe


108

Ich habe einen R-Datenrahmen, der einen Faktor enthält, den ich "erweitern" möchte, sodass für jede Faktorstufe eine Spalte in einem neuen Datenrahmen zugeordnet ist, die einen 1/0 Indikator enthält. Angenommen, ich habe:

df.original <-data.frame(eggs = c("foo", "foo", "bar", "bar"), ham = c(1,2,3,4))

Ich will:

df.desired  <- data.frame(foo = c(1,1,0,0), bar=c(0,0,1,1), ham=c(1,2,3,4))

Da für bestimmte Analysen, für die Sie einen vollständig numerischen Datenrahmen benötigen (z. B. Hauptkomponentenanalyse), ich dachte, dass diese Funktion integriert sein könnte. Das Schreiben einer Funktion, um dies zu tun, sollte nicht zu schwierig sein, aber ich kann einige vorhersehen Herausforderungen in Bezug auf Spaltennamen und wenn bereits etwas vorhanden ist, würde ich das lieber nutzen.

Antworten:


131

Verwenden Sie die model.matrixFunktion:

model.matrix( ~ Species - 1, data=iris )

1
Kann ich nur hinzufügen, dass diese Methode so viel schneller war als castfür mich.
Matt Weller

3
@ GregSnow Ich habe den 2. Absatz von ?formulasowie überprüft ?model.matrix, aber es war unklar (könnte nur mein Mangel an Wissenstiefe in Matrixalgebra und Modellformulierung sein). Nachdem ich mehr gegraben hatte, konnte ich feststellen, dass -1 nur angibt, dass die Spalte "Intercept" nicht enthalten sein soll. Wenn Sie -1 weglassen, wird in der Ausgabe eine Intercept-Spalte mit Einsen angezeigt, wobei eine Binärspalte weggelassen wird. Sie können anhand von Zeilen sehen, welche Werte in der ausgelassenen Spalte 1 sind, wobei die Werte der anderen Spalten 0 sind. Die Dokumentation scheint kryptisch - gibt es noch eine gute Ressource?
Ryan Chase

1
@ RyanChase, es gibt viele Online-Tutorials und Bücher über R / S (einige mit kurzen Beschreibungen auf der Webseite von r-project.org). Mein eigenes Lernen von S und R war ziemlich vielseitig (und lang), daher bin ich nicht der Beste, um eine Meinung darüber abzugeben, wie aktuelle Bücher / Tutorials Anfänger ansprechen. Ich bin jedoch ein Fan von Experimenten. Das Ausprobieren in einer neuen R-Sitzung kann sehr aufschlussreich und nicht gefährlich sein (das Schlimmste, das mir je passiert ist, ist das Abstürzen von R, und das selten, was zu Verbesserungen von R führt). Stackoverflow ist dann eine gute Ressource, um zu verstehen, was passiert ist.
Greg Snow

7
Und wenn Sie alle model.matrix(~., data=iris)[,-1]
Faktorspalten

1
@colin, Nicht vollautomatisch, aber Sie können naresiddie fehlenden Werte nach der Verwendung wieder eingeben na.exclude. Ein kurzes Beispiel:tmp <- data.frame(x=factor(c('a','b','c',NA,'a'))); tmp2 <- na.exclude(tmp); tmp3 <- model.matrix( ~x-1, tmp2); tmp4 <- naresid(attr(tmp2,'na.action'), tmp3)
Greg Snow

17

Wenn Ihr Datenrahmen nur aus Faktoren besteht (oder Sie an einer Teilmenge von Variablen arbeiten, die alle Faktoren sind), können Sie auch die acm.disjonctifFunktion aus dem ade4Paket verwenden:

R> library(ade4)
R> df <-data.frame(eggs = c("foo", "foo", "bar", "bar"), ham = c("red","blue","green","red"))
R> acm.disjonctif(df)
  eggs.bar eggs.foo ham.blue ham.green ham.red
1        0        1        0         0       1
2        0        1        1         0       0
3        1        0        0         1       0
4        1        0        0         0       1

Nicht genau der Fall, den Sie beschreiben, aber es kann auch nützlich sein ...


Vielen Dank, das hat mir sehr geholfen, da es weniger Speicher benötigt als model.matrix!
Serhiy

Mir gefällt die Art und Weise, wie die Variablen benannt werden. Ich mag nicht , dass sie als speicherhungrige numerische zurückgegeben werden , wenn sie sollten (IMHO) nur Logicals sein.
DSZ

9

Ein schneller Weg mit dem reshape2Paket:

require(reshape2)

> dcast(df.original, ham ~ eggs, length)

Using ham as value column: use value_var to override.
  ham bar foo
1   1   0   1
2   2   0   1
3   3   1   0
4   4   1   0

Beachten Sie, dass dies genau die gewünschten Spaltennamen erzeugt.


Gut. Aber achten Sie auf das Duplikat von Schinken. sagen wir, d <- data.frame (Eier = c ("foo", "bar", "foo"), Schinken = c (1,2,1)); dcast (d, Schinken ~ Eier, Länge) macht foo = 2.
kohske

@Kohske, stimmt, aber ich habe angenommen, dass hames sich um eine eindeutige Zeilen-ID handelt. Wenn hames sich nicht um eine eindeutige ID handelt, muss eine andere eindeutige ID verwendet werden (oder eine Dummy-ID erstellt werden) und diese anstelle von verwendet werden ham. Das Konvertieren einer kategorialen Bezeichnung in einen binären Indikator wäre nur für eindeutige IDs sinnvoll.
Prasad Chalasani

6

Wahrscheinlich ähnelt die Dummy-Variable dem, was Sie wollen. Dann ist model.matrix nützlich:

> with(df.original, data.frame(model.matrix(~eggs+0), ham))
  eggsbar eggsfoo ham
1       0       1   1
2       0       1   2
3       1       0   3
4       1       0   4

6

Ein verspäteter Eintrag class.indaus dem nnetPaket

library(nnet)
 with(df.original, data.frame(class.ind(eggs), ham))
  bar foo ham
1   0   1   1
2   0   1   2
3   1   0   3
4   1   0   4

4

Ich bin gerade auf diesen alten Thread gestoßen und dachte, ich würde eine Funktion hinzufügen, die ade4 verwendet, um einen Datenrahmen zu verwenden, der aus Faktoren und / oder numerischen Daten besteht, und einen Datenrahmen mit Faktoren als Dummy-Codes zurückgibt.

dummy <- function(df) {  

    NUM <- function(dataframe)dataframe[,sapply(dataframe,is.numeric)]
    FAC <- function(dataframe)dataframe[,sapply(dataframe,is.factor)]

    require(ade4)
    if (is.null(ncol(NUM(df)))) {
        DF <- data.frame(NUM(df), acm.disjonctif(FAC(df)))
        names(DF)[1] <- colnames(df)[which(sapply(df, is.numeric))]
    } else {
        DF <- data.frame(NUM(df), acm.disjonctif(FAC(df)))
    }
    return(DF)
} 

Lass es uns versuchen.

df <-data.frame(eggs = c("foo", "foo", "bar", "bar"), 
            ham = c("red","blue","green","red"), x=rnorm(4))     
dummy(df)

df2 <-data.frame(eggs = c("foo", "foo", "bar", "bar"), 
            ham = c("red","blue","green","red"))  
dummy(df2)

3

Hier ist ein klarerer Weg, dies zu tun. Ich verwende model.matrix, um die booleschen Dummy-Variablen zu erstellen und sie dann wieder in den ursprünglichen Datenrahmen einzufügen.

df.original <-data.frame(eggs = c("foo", "foo", "bar", "bar"), ham = c(1,2,3,4))
df.original
#   eggs ham
# 1  foo   1
# 2  foo   2
# 3  bar   3
# 4  bar   4

# Create the dummy boolean variables using the model.matrix() function.
> mm <- model.matrix(~eggs-1, df.original)
> mm
#   eggsbar eggsfoo
# 1       0       1
# 2       0       1
# 3       1       0
# 4       1       0
# attr(,"assign")
# [1] 1 1
# attr(,"contrasts")
# attr(,"contrasts")$eggs
# [1] "contr.treatment"

# Remove the "eggs" prefix from the column names as the OP desired.
colnames(mm) <- gsub("eggs","",colnames(mm))
mm
#   bar foo
# 1   0   1
# 2   0   1
# 3   1   0
# 4   1   0
# attr(,"assign")
# [1] 1 1
# attr(,"contrasts")
# attr(,"contrasts")$eggs
# [1] "contr.treatment"

# Combine the matrix back with the original dataframe.
result <- cbind(df.original, mm)
result
#   eggs ham bar foo
# 1  foo   1   0   1
# 2  foo   2   0   1
# 3  bar   3   1   0
# 4  bar   4   1   0

# At this point, you can select out the columns that you want.

0

Ich brauchte eine Funktion, um Faktoren zu explodieren, die etwas flexibler ist, und habe eine basierend auf der Funktion acm.disjonctif aus dem ade4-Paket erstellt. Auf diese Weise können Sie die Explosionswerte auswählen, die in acm.disjonctif 0 und 1 sind. Es explodiert nur Faktoren, die "wenige" Ebenen haben. Numerische Spalten bleiben erhalten.

# Function to explode factors that are considered to be categorical,
# i.e., they do not have too many levels.
# - data: The data.frame in which categorical variables will be exploded.
# - values: The exploded values for the value being unequal and equal to a level.
# - max_factor_level_fraction: Maximum number of levels as a fraction of column length. Set to 1 to explode all factors.
# Inspired by the acm.disjonctif function in the ade4 package.
explode_factors <- function(data, values = c(-0.8, 0.8), max_factor_level_fraction = 0.2) {
  exploders <- colnames(data)[sapply(data, function(col){
      is.factor(col) && nlevels(col) <= max_factor_level_fraction * length(col)
    })]
  if (length(exploders) > 0) {
    exploded <- lapply(exploders, function(exp){
        col <- data[, exp]
        n <- length(col)
        dummies <- matrix(values[1], n, length(levels(col)))
        dummies[(1:n) + n * (unclass(col) - 1)] <- values[2]
        colnames(dummies) <- paste(exp, levels(col), sep = '_')
        dummies
      })
    # Only keep numeric data.
    data <- data[sapply(data, is.numeric)]
    # Add exploded values.
    data <- cbind(data, exploded)
  }
  return(data)
}
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.