2013-04-24 33 views
15

khung dữ liệu gốc: Recode yếu tố phân loại với N loại vào N cột nhị phân

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 

New khung dữ liệu:

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 

Đối với các tập dữ liệu nhỏ này là tốt, nhưng nó trở thành sl ow cho bộ dữ liệu lớn hơn nhiều.

Bất kỳ ai biết cách thực hiện điều này mà không sử dụng tính năng lặp?

+1

Khung dữ liệu đầu tiên của bạn có hai biến, nhưng có vẻ như bạn chỉ chuyển đổi biến thứ hai. Bạn có thể làm rõ một chút? – joran

+0

bạn đang ghi đè dữ liệu của mình. Nó phải có 6 cột trong đầu ra. – Arun

+0

Xin lỗi, đó là một sai lầm về phía tôi - tôi đã sửa nó trong đoạn mã trên. Nên có ba cột mới cho mỗi cột ban đầu trong ví dụ trên. Cảm ơn vì đã bắt được điều đó! –

Trả lời

21

Thậm chí tốt hơn với sự giúp đỡ của khả năng tìm kiếm @ AnandaMahto của,

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 

Tôi nghĩ rằng đây là những gì bạn đang tìm kiếm. Tôi rất sẵn lòng xóa nếu không phải vậy. Nhờ @ G.Gothendieck (một lần nữa) cho số excellent usage của 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 

Lưu ý: Đầu ra của bạn chỉ là:

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

Note 2: Điều này cho phép một matrix. Khá rõ ràng, nhưng vẫn còn, bọc nó với as.data.frame(.) nếu bạn muốn có một data.frame.

0

Đây là một giải pháp cho trường hợp tổng quát hơn, khi lượng chữ không được xác định apriori:

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

chức năng này có một vector của các nhân vật, và recodes nó vào giá trị nhị phân. Để xử lý tất cả các biến trong df:

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

Một cách tiếp cận khá trực tiếp là chỉ cần sử dụng table trên mỗi cột, lập bảng các giá trị trong cột bằng số lượng hàng trong 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 

tôi đã sử dụng factor trên "x" để đảm bảo rằng ngay cả trong trường hợp có, nói rằng, không có giá trị "c" trong cột, vẫn sẽ có cột "c" ở đầu ra, được lấp đầy bằng số không.

8

Có chức năng trong gói của người chăm sóc làm những gì bạn yêu cầu, dummyVars. Dưới đây là ví dụ của nó sử dụng lấy từ các tài liệu tác giả: 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 

Các tùy chọn model.matrix có thể hữu ích trong trường hợp bạn đã có dữ liệu thưa thớt và muốn sử dụng Matrix::sparse.model.matrix

2

Gần đây tôi đã đi qua cách khác.Tôi nhận thấy rằng khi bạn chạy bất kỳ chức năng tương phản nào với contrasts được đặt thành FALSE, nó sẽ cung cấp cho bạn một mã hóa nóng. Ví dụ, contr.sum(5, contrasts = FALSE) cho

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 

Để có được hành vi này đối với tất cả các yếu tố, bạn có thể tạo một hàm tương phản mới và đặt nó làm mặc định. Ví dụ,

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) 

Điều này dẫn đến

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

Chỉ cần nhìn thấy một câu hỏi khép kín hướng đến ở đây, và chưa ai đề cập bằng các gói dummies nào:

Bạn có thể mã hóa lại các biến của bạn bằng cách sử dụng Chức năng dummy.data.frame() được xây dựng trên đầu trang của model.matrix() nhưng có cú pháp dễ dàng hơn, một số tùy chọn tốt và sẽ trả về một khung dữ liệu:

> 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 

Một số khía cạnh tốt đẹp của chức năng này là bạn có thể dễ dàng xác định delimeter cho tên mới (sep=), bỏ qua các đại lượng không mã hóa (all=F) và đi kèm với tùy chọn riêng của mình dummy.classes cho phép bạn xác định các lớp học của cột nên được mã hóa.

Bạn cũng có thể chỉ cần sử dụng hàm dummy() để áp dụng điều này cho một cột.

Các vấn đề liên quan