2013-06-01 45 views
7

Tôi có một khung dữ liệu với một cột mà là một danh sách, như vậy:Chuyển đổi một cột kiểu 'danh sách' để nhiều cột trong một khung dữ liệu

>head(movies$genre_list) 
[[1]] 
[1] "drama" "action" "romance" 
[[2]] 
[1] "crime" "drama" 
[[3]] 
[1] "crime" "drama" "mystery" 
[[4]] 
[1] "thriller" "indie" 
[[5]] 
[1] "thriller" 
[[6]] 
[1] "drama" "family" 

Tôi muốn chuyển đổi một cột này để nhiều cột, một cho mỗi phần tử duy nhất trên các danh sách (trong trường hợp này là thể loại) và có chúng dưới dạng cột nhị phân. Tôi đang tìm một giải pháp thanh lịch, không liên quan đến việc tìm ra bao nhiêu thể loại, và sau đó tạo một cột cho mỗi nhóm, sau đó kiểm tra từng phần tử danh sách để sau đó điền các cột thể loại. Tôi đã thử không công khai, nhưng nó không hoạt động với một vectơ danh sách theo cách tôi muốn.

Cảm ơn!

+0

Mỗi mục trong danh sách luôn có thể loại duy nhất không? Nói cách khác, một kỷ lục có thể là "kịch, hành động, lãng mạn, hành động"? – A5C1D2H2I1M1N2O1R2T1

Trả lời

4

Dưới đây là một vài phương pháp:

movies <- data.frame(genre_list = I(list(
    c("drama", "action", "romance"), 
    c("crime", "drama"), 
    c("crime", "drama", "mystery"), 
    c("thriller", "indie"), 
    c("thriller"), 
    c("drama", "family")))) 

Update, năm sau ....

Bạn có thể sử dụng chức năng mtabulate từ "qdapTools" hoặc unexported charMat chức năng từ của tôi " gói "splitstackshape".

Cú pháp sẽ là:

library(qdapTools) 
mtabulate(movies$genre_list) 
# action crime drama family indie mystery romance thriller 
# 1  1  0  1  0  0  0  1  0 
# 2  0  1  1  0  0  0  0  0 
# 3  0  1  1  0  0  1  0  0 
# 4  0  0  0  0  1  0  0  1 
# 5  0  0  0  0  0  0  0  1 
# 6  0  0  1  1  0  0  0  0 

hoặc

splitstackshape:::charMat(movies$genre_list, fill = 0) 
#  action crime drama family indie mystery romance thriller 
# [1,]  1  0  1  0  0  0  1  0 
# [2,]  0  1  1  0  0  0  0  0 
# [3,]  0  1  1  0  0  1  0  0 
# [4,]  0  0  0  0  1  0  0  1 
# [5,]  0  0  0  0  0  0  0  1 
# [6,]  0  0  1  1  0  0  0  0 

Cập nhật: Một vài phương pháp tiếp cận trực tiếp hơn

Cải thiện lựa chọn 1: Sử dụng table hơi trực tiếp:

table(rep(1:nrow(movies), sapply(movies$genre_list, length)), 
     unlist(movies$genre_list, use.names=FALSE)) 

Tùy chọn được cải tiến 2: Sử dụng vòng lặp for.

x <- unique(unlist(movies$genre_list, use.names=FALSE)) 
m <- matrix(0, ncol = length(x), nrow = nrow(movies), dimnames = list(NULL, x)) 
for (i in 1:nrow(m)) { 
    m[i, movies$genre_list[[i]]] <- 1 
} 
m 

Dưới đây là câu trả lời OLD

Chuyển đổi danh sách để một danh sách các table s (lần lượt chuyển đổi sang data.frame s):

tables <- lapply(seq_along(movies$genre_list), function(x) { 
    temp <- as.data.frame.table(table(movies$genre_list[[x]])) 
    names(temp) <- c("Genre", paste("Record", x, sep = "_")) 
    temp 
}) 

Sử dụng Reduce đến merge danh sách kết quả. Nếu tôi hiểu mục tiêu cuối cùng của bạn một cách chính xác, kết quả này theo hình thức hoán của kết quả mà bạn đang quan tâm.

merged_tables <- Reduce(function(x, y) merge(x, y, all = TRUE), tables) 
merged_tables 
#  Genre Record_1 Record_2 Record_3 Record_4 Record_5 Record_6 
# 1 action  1  NA  NA  NA  NA  NA 
# 2 drama  1  1  1  NA  NA  1 
# 3 romance  1  NA  NA  NA  NA  NA 
# 4 crime  NA  1  1  NA  NA  NA 
# 5 mystery  NA  NA  1  NA  NA  NA 
# 6 indie  NA  NA  NA  1  NA  NA 
# 7 thriller  NA  NA  NA  1  1  NA 
# 8 family  NA  NA  NA  NA  NA  1 

transposing và chuyển đổi NA-0 là khá đơn giản.Chỉ cần thả cột đầu tiên và tái sử dụng nó như cột names cho cái mới data.frame

movie_genres <- setNames(data.frame(t(merged_tables[-1])), merged_tables[[1]]) 
movie_genres[is.na(movie_genres)] <- 0 
movie_genres 
3

Sử dụng các đầu vào tương tự như trong các câu trả lời khác đây là một số lựa chọn thay thế:

1) yếu tố/table/rbind

> levs <- levels(factor(unlist(movies[[1]]))) 
> as.data.frame(do.call(rbind, lapply(lapply(movies[[1]], factor, levs), table))) 
    action crime drama family indie mystery romance thriller 
1  1  0  1  0  0  0  1  0 
2  0  1  1  0  0  0  0  0 
3  0  1  1  0  0  1  0  0 
4  0  0  0  0  1  0  0  1 
5  0  0  0  0  0  0  0  1 
6  0  0  1  1  0  0  0  0 

2) make.groups/xtabs

> library(lattice) 
> m <- do.call(make.groups, movies[[1]]) 
> as.data.frame.matrix(xtabs(~ which + data, m)) 
           action crime drama family indie mystery romance thriller 
c("drama", "action", "romance")  1  0  1  0  0  0  1  0 
c("crime", "drama")     0  1  1  0  0  0  0  0 
c("crime", "drama", "mystery")  0  1  1  0  0  1  0  0 
c("thriller", "indie")    0  0  0  0  1  0  0  1 
thriller        0  0  0  0  0  0  0  1 
c("drama", "family")     0  0  1  1  0  0  0  0 

2a) make.groups/dcast một Đây là một biến thể của thay thế 2 sử dụng dcast từ reshape2 ở vị trí của as.data.frame.matrixxtabs. khung dữ liệu tan chảy m là từ thay thế 2.

library(reshape2) 
dcast(m, which ~ data, fun.aggregate = length, value.var = "which") 

UPDATE: Thêm lựa chọn 2.

UPDATE 2: Thêm 2a thay thế.

+0

Cảm ơn! Tôi thích giải pháp 1 nhất! Tôi chỉ không được sử dụng để 'lưới 'đủ để grok giải pháp 2. –

+0

@New, Đã chia # 2 thành hai dòng để các dữ liệu nóng chảy khung' m' có thể được kiểm tra. Điều đó có thể cải thiện khả năng hiểu. –

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