Tự động mở rộng nhân tố R thành tập hợp 1/0 biến chỉ số cho mọi cấp nhân tố


108

Tôi có một khung dữ liệu R chứa một yếu tố mà tôi muốn "mở rộng" để đối với mỗi cấp yếu tố, có một cột được liên kết trong khung dữ liệu mới, chứa chỉ số 1/0. Ví dụ: giả sử tôi có:

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

Tôi muốn:

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

Bởi vì đối với một số phân tích nhất định mà bạn cần có khung dữ liệu hoàn toàn bằng số (ví dụ: phân tích thành phần chính), tôi nghĩ rằng tính năng này có thể được tích hợp sẵn. Viết một hàm để thực hiện việc này không quá khó, nhưng tôi có thể thấy trước một số những thách thức liên quan đến tên cột và nếu cái gì đó đã tồn tại, tôi muốn sử dụng cái đó.

Câu trả lời:


131

Sử dụng model.matrixchức năng:

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

1
Tôi có thể chỉ thêm rằng phương pháp này nhanh hơn rất nhiều so với sử dụng castcho tôi.
Matt Weller

3
@GregSnow Tôi đã xem lại đoạn 2 của ?formulacũng như ?model.matrix, nhưng nó không rõ ràng (có thể là do tôi thiếu kiến ​​thức chuyên sâu về đại số ma trận và xây dựng mô hình). Sau khi tìm hiểu thêm, tôi đã có thể thu thập rằng -1 chỉ là chỉ định không bao gồm cột "chặn". Nếu bạn bỏ đi -1, bạn sẽ thấy một cột chặn của 1 trong đầu ra với một cột nhị phân bị bỏ đi. Bạn có thể xem giá trị nào mà cột bị bỏ qua là 1 dựa trên các hàng mà giá trị của các cột khác là 0. Tài liệu có vẻ khó hiểu - có một nguồn tài liệu tốt khác không?
Ryan Chase

1
@RyanChase, có rất nhiều sách và hướng dẫn trực tuyến về R / S (một số hướng dẫn có mô tả ngắn gọn trên trang web r-project.org). Việc học của tôi về S và R khá là chiết trung (và dài dòng), vì vậy tôi không phải là người tốt nhất để đưa ra ý kiến ​​về cách các cuốn sách / hướng dẫn hiện tại thu hút người mới bắt đầu. Tuy nhiên, tôi là một người thích thử nghiệm. Thử một thứ gì đó trong một phiên R mới có thể rất thú vị và không nguy hiểm (điều tồi tệ nhất đã xảy ra với tôi là làm rơi R, và điều đó hiếm khi dẫn đến việc cải thiện R). Stackoverflow sau đó là một nguồn tốt để hiểu những gì đã xảy ra.
Greg Snow

7
Và nếu bạn muốn chuyển đổi tất cả các cột yếu tố, bạn có thể sử dụng:model.matrix(~., data=iris)[,-1]
user890739

1
@colin, Không hoàn toàn tự động, nhưng bạn có thể sử dụng naresidđể đặt lại các giá trị bị thiếu sau khi sử dụng na.exclude. Một ví dụ nhanh: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

Nếu khung dữ liệu của bạn chỉ được tạo bởi các yếu tố (hoặc bạn đang làm việc trên một tập hợp con của các biến là tất cả các yếu tố), bạn cũng có thể sử dụng acm.disjonctifhàm từ ade4gói:

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

Không chính xác trường hợp bạn đang mô tả, nhưng nó cũng có thể hữu ích ...


Cảm ơn, điều này đã giúp tôi rất nhiều vì nó sử dụng ít bộ nhớ hơn model.matrix!
Serhiy

Tôi thích cách các biến được đặt tên; Tôi không thích việc chúng được trả về dưới dạng số đói với bộ nhớ trong khi chúng phải (IMHO) chỉ là logic.
dsz

9

Một cách nhanh chóng bằng cách sử dụng reshape2gói:

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

Lưu ý rằng điều này tạo ra chính xác tên cột mà bạn muốn.


Tốt. Nhưng hãy cẩn thận với bản sao của giăm bông. nói, d <- data.frame (egg = c ("foo", "bar", "foo"), ham = c (1,2,1)); dcast (d, ham ~ trứng, chiều dài) làm cho foo = 2.
kohske

@Kohske, đúng, nhưng tôi đã giả định hamlà một id hàng duy nhất. Nếu hamkhông phải là một id duy nhất thì người ta phải sử dụng một số id duy nhất khác (hoặc tạo một id giả) và sử dụng nó thay cho ham. Việc chuyển đổi một nhãn phân loại thành một chỉ báo nhị phân sẽ chỉ có ý nghĩa đối với các id duy nhất.
Prasad Chalasani

6

có thể biến giả tương tự như những gì bạn muốn. Sau đó, model.matrix rất hữu ích:

> 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

Mục nhập muộn class.indtừ nnetgói hàng

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

Chỉ cần xem qua chuỗi cũ này và nghĩ rằng tôi sẽ thêm một hàm sử dụng ade4 để lấy khung dữ liệu bao gồm các yếu tố và / hoặc dữ liệu số và trả về một khung dữ liệu với các yếu tố là mã giả.

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

Hãy thử nó.

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

Đây là một cách rõ ràng hơn để làm điều đó. Tôi sử dụng model.matrix để tạo các biến boolean giả và sau đó hợp nhất nó trở lại khung dữ liệu ban đầu.

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

Tôi cần một hàm để 'bùng nổ' các yếu tố linh hoạt hơn một chút và tạo một hàm dựa trên hàm acm.disjonctif từ gói ade4. Điều này cho phép bạn chọn các giá trị bùng nổ, là 0 và 1 trong acm.disjonctif. Nó chỉ làm bùng nổ các yếu tố có mức 'vài'. Các cột số được giữ nguyên.

# 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)
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.