Her faktör seviyesi için bir R faktörünü otomatik olarak 1/0 gösterge değişkenleri koleksiyonuna genişletme


108

"Genişletmek" istediğim bir faktör içeren bir R veri çerçevem ​​var, böylece her faktör seviyesi için, yeni bir veri çerçevesinde 1/0 göstergesi içeren ilişkili bir sütun var. Örneğin:

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

İstiyorum:

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

Çünkü tamamen sayısal bir veri çerçevesine sahip olmanız gereken bazı analizler için (örneğin, temel bileşen analizi), bu özelliğin yerleşik olabileceğini düşündüm. Bunu yapmak için bir işlev yazmak çok zor olmamalı, ancak bazılarını öngörebilirim sütun adlarıyla ilgili zorluklar ve zaten bir şey varsa, bunu kullanmayı tercih ederim.

Yanıtlar:


131

Şu model.matrixişlevi kullanın :

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

1
Bu yöntemin castbenim için kullanmaktan çok daha hızlı olduğunu ekleyebilir miyim ?
Matt Weller

3
Ben 2. paragrafı gözden @GregSnow ?formulayanı sıra ?model.matrix, ancak (sadece matris cebir ve modeli formülasyonunda bilgisinin derinliği benim eksikliği olabilir) belli değildi. Daha fazla kazdıktan sonra, -1'in "kesişme" sütununu dahil etmemeyi belirttiğini anladım. -1'i dışarıda bırakırsanız, çıktıda 1'lerden oluşan bir kesişme sütunu görürsünüz ve bir ikili sütun dışarıda kalır. Diğer sütunların değerlerinin 0 olduğu satırlara dayalı olarak, atlanan sütunun hangi değerleri 1'ler olduğunu görebilirsiniz. Belgeler şifreli görünüyor - başka iyi bir kaynak var mı?
Ryan Chase

1
@RyanChase, R / S hakkında birçok çevrimiçi öğretici ve kitap var (bazıları r-project.org web sayfasında kısa açıklamalar içeren). Kendi S ve R öğrenimim oldukça eklektik (ve uzun) oldu, bu yüzden mevcut kitapların / öğreticilerin yeni başlayanlara nasıl hitap ettiği konusunda bir fikir verecek en iyi kişi ben değilim. Ancak ben bir deney hayranıyım. Yeni bir R seansında bir şeyi denemek çok aydınlatıcı olabilir ve tehlikeli olmayabilir (başıma gelen en kötü şey R'nin çökmesidir ve bu nadiren R'de iyileşmelere yol açar). Stackoverflow, ne olduğunu anlamak için iyi bir kaynaktır.
Greg Snow

7
Tüm faktör sütunlarını dönüştürmek istiyorsanız, şunları kullanabilirsiniz:model.matrix(~., data=iris)[,-1]
user890739

1
@colin, Tam otomatik değil, ancak kullandıktan naresidsonra eksik değerleri geri koymak için kullanabilirsiniz na.exclude. Hızlı bir örnek: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

Veri çerçeveniz yalnızca faktörlerden oluşuyorsa (veya tümü faktör olan bir değişkenler alt kümesi üzerinde çalışıyorsanız), paketteki acm.disjonctifişlevi de kullanabilirsiniz ade4:

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

Tam olarak tarif ettiğiniz durum değil, ama faydalı da olabilir ...


Teşekkürler, bu model.matrix'ten daha az bellek kullandığından bana çok yardımcı oldu!
Serhiy

Değişkenlerin isimlendirilme şeklini seviyorum; Gerektiği zaman (IMHO) sadece mantıksal olmaları gerektiğinde depolamaya aç sayısal olarak döndürülmelerini sevmiyorum.
dsz

9

reshape2Paketi kullanmanın hızlı bir yolu :

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

Bunun tam olarak istediğiniz sütun adlarını oluşturduğuna dikkat edin.


İyi. Ama jambonun kopyasına dikkat edin. d <- data.frame (yumurta = c ("foo", "bar", "foo"), jambon = c (1,2,1)); dcast (d, jambon ~ yumurta, uzunluk) foo = 2 yapar.
kohske

@Kohske, doğru, ancak hambenzersiz bir satır kimliği olduğunu varsayıyordum . hamBenzersiz bir kimlik değilse , başka bir benzersiz kimlik kullanmalı (veya sahte bir kimlik oluşturmalı) ve bunun yerine bunu kullanmalıdır ham. Kategorik bir etiketi ikili göstergeye dönüştürmek, yalnızca benzersiz kimlikler için mantıklı olacaktır.
Prasad Chalasani

6

muhtemelen kukla değişken istediğiniz şeye benzer. Model.matrix kullanışlıdır:

> 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

Geç giriş class.inddan nnetpaketin

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

Bu eski iş parçacığına henüz rastladım ve faktörlerden ve / veya sayısal verilerden oluşan bir veri çerçevesi almak için ade4'ü kullanan ve kukla kodlar olarak faktörlerle bir veri çerçevesi döndüren bir işlev ekleyeceğimi düşündüm.

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)
} 

Hadi deneyelim.

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

İşte bunu yapmanın daha net bir yolu. Model.matrix'i kukla boolean değişkenleri oluşturmak için kullanıyorum ve ardından orijinal veri çerçevesine geri birleştiriyorum.

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

Etkenleri 'patlatmak' için biraz daha esnek bir işleve ihtiyacım vardı ve ade4 paketindeki acm.disjonctif işlevini temel alarak bir işlev oluşturdum. Bu, acm.disjonctif içinde 0 ve 1 olan patlatılmış değerleri seçmenize olanak sağlar. Yalnızca 'az' seviyeye sahip faktörleri patlatır. Sayısal sütunlar korunur.

# 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)
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.