2014-10-03 32 views
8

Mục tiêu là chuyển đổi danh sách lồng nhau đôi khi chứa các bản ghi bị thiếu vào một khung dữ liệu. Một ví dụ về cấu trúc khi có hồ sơ thiếu là:Chuyển danh sách lồng vào dataframe

str(mylist) 

List of 3 
$ :List of 7 
    ..$ Hit : chr "True" 
    ..$ Project: chr "Blue" 
    ..$ Year : chr "2011" 
    ..$ Rating : chr "4" 
    ..$ Launch : chr "26 Jan 2012" 
    ..$ ID  : chr "19" 
    ..$ Dept : chr "1, 2, 4" 
$ :List of 2 
    ..$ Hit : chr "False" 
    ..$ Error: chr "Record not found" 
$ :List of 7 
    ..$ Hit : chr "True" 
    ..$ Project: chr "Green" 
    ..$ Year : chr "2004" 
    ..$ Rating : chr "8" 
    ..$ Launch : chr "29 Feb 2004" 
    ..$ ID  : chr "183" 
    ..$ Dept : chr "6, 8" 

Khi có được không thiếu ghi danh sách có thể được chuyển đổi thành một khung dữ liệu sử dụng data.frame(do.call(rbind.data.frame, mylist)). Tuy nhiên, khi các bản ghi bị thiếu kết quả này trong một cột không phù hợp. Tôi biết có các chức năng để hợp nhất các khung dữ liệu của các cột không phù hợp nhưng tôi vẫn chưa tìm thấy một khung có thể được áp dụng cho các danh sách. Kết quả lý tưởng sẽ giữ kỷ lục 2 với NA cho tất cả các biến. Hy vọng cho một số giúp đỡ.

Chỉnh sửa để thêm dput(mylist):

list(structure(list(Hit = "True", Project = "Blue", Year = "2011", 
Rating = "4", Launch = "26 Jan 2012", ID = "19", Dept = "1, 2, 4"), .Names = c("Hit", 
"Project", "Year", "Rating", "Launch", "ID", "Dept")), structure(list(
Hit = "False", Error = "Record not found"), .Names = c("Hit", 
"Error")), structure(list(Hit = "True", Project = "Green", Year = "2004", 
Rating = "8", Launch = "29 Feb 2004", ID = "183", Dept = "6, 8"), .Names = c("Hit", 
"Project", "Year", "Rating", "Launch", "ID", "Dept"))) 

Trả lời

17

Bạn cũng có thể sử dụng (ít nhất v1.9.3) của rbindlist trong data.table gói:

library(data.table) 

rbindlist(mylist, fill=TRUE) 

##  Hit Project Year Rating  Launch ID Dept   Error 
## 1: True Blue 2011  4 26 Jan 2012 19 1, 2, 4    NA 
## 2: False  NA NA  NA   NA NA  NA Record not found 
## 3: True Green 2004  8 29 Feb 2004 183 6, 8    NA 
+1

[1.9.4 hiện khả dụng trên CRAN] (http://cran.r-project.org/web/packages/data.table/index.html) (mặc dù có thể mất thêm một ngày để các tệp nhị phân còn lại có sẵn). – Arun

7

Bạn có thể tạo một danh sách các data.frames:

dfs <- lapply(mylist, data.frame, stringsAsFactors = FALSE) 

Sau đó sử dụng một trong các:

library(plyr) 
rbind.fill(dfs) 

hoặc nhanh hơn

library(dplyr) 
rbind_all(dfs) 

Trong trường hợp dplyr::rbind_all, tôi rất ngạc nhiên khi chọn sử dụng "" thay vì NA cho dữ liệu bị thiếu. Nếu bạn xóa stringsAsFactors = FALSE, bạn sẽ nhận được NA nhưng với chi phí cảnh báo ... Vì vậy, suppressWarnings(rbind_all(lapply(mylist, data.frame))) sẽ là một giải pháp xấu xí nhưng nhanh chóng.

+2

'rbind_all()' không được chấp nhận. Thay vào đó, hãy sử dụng 'bind_rows()'. – psychonomics

5

Tôi chỉ phát triển một giải pháp cho this question đó là áp dụng ở đây, vì vậy tôi sẽ cung cấp cho nó ở đây là tốt :

tl <- function(e) { if (is.null(e)) return(NULL); ret <- typeof(e); if (ret == 'list' && !is.null(names(e))) ret <- list(type='namedlist') else ret <- list(type=ret,len=length(e)); ret; }; 
mkcsv <- function(v) paste0(collapse=',',v); 
keyListToStr <- function(keyList) paste0(collapse='','/',sapply(keyList,function(key) if (is.null(key)) '*' else paste0(collapse=',',key))); 

extractLevelColumns <- function(
    nodes, ## current level node selection 
    ..., ## additional arguments to data.frame() 
    keyList=list(), ## current key path under main list 
    sep=NULL, ## optional string separator on which to join multi-element vectors; if NULL, will leave as separate columns 
    mkname=function(keyList,maxLen) paste0(collapse='.',if (is.null(sep) && maxLen == 1L) keyList[-length(keyList)] else keyList) ## name builder from current keyList and character vector max length across node level; default to dot-separated keys, and remove last index component for scalars 
) { 
    cat(sprintf('extractLevelColumns(): %s\n',keyListToStr(keyList))); 
    if (length(nodes) == 0L) return(list()); ## handle corner case of empty main list 
    tlList <- lapply(nodes,tl); 
    typeList <- do.call(c,lapply(tlList,`[[`,'type')); 
    if (length(unique(typeList)) != 1L) stop(sprintf('error: inconsistent types (%s) at %s.',mkcsv(typeList),keyListToStr(keyList))); 
    type <- typeList[1L]; 
    if (type == 'namedlist') { ## hash; recurse 
     allKeys <- unique(do.call(c,lapply(nodes,names))); 
     ret <- do.call(c,lapply(allKeys,function(key) extractLevelColumns(lapply(nodes,`[[`,key),...,keyList=c(keyList,key),sep=sep,mkname=mkname))); 
    } else if (type == 'list') { ## array; recurse 
     lenList <- do.call(c,lapply(tlList,`[[`,'len')); 
     maxLen <- max(lenList,na.rm=T); 
     allIndexes <- seq_len(maxLen); 
     ret <- do.call(c,lapply(allIndexes,function(index) extractLevelColumns(lapply(nodes,function(node) if (length(node) < index) NULL else node[[index]]),...,keyList=c(keyList,index),sep=sep,mkname=mkname))); ## must be careful to translate out-of-bounds to NULL; happens automatically with string keys, but not with integer indexes 
    } else if (type%in%c('raw','logical','integer','double','complex','character')) { ## atomic leaf node; build column 
     lenList <- do.call(c,lapply(tlList,`[[`,'len')); 
     maxLen <- max(lenList,na.rm=T); 
     if (is.null(sep)) { 
      ret <- lapply(seq_len(maxLen),function(i) setNames(data.frame(sapply(nodes,function(node) if (length(node) < i) NA else node[[i]]),...),mkname(c(keyList,i),maxLen))); 
     } else { 
      ## keep original type if maxLen is 1, IOW don't stringify 
      ret <- list(setNames(data.frame(sapply(nodes,function(node) if (length(node) == 0L) NA else if (maxLen == 1L) node else paste(collapse=sep,node)),...),mkname(keyList,maxLen))); 
     }; ## end if 
    } else stop(sprintf('error: unsupported type %s at %s.',type,keyListToStr(keyList))); 
    if (is.null(ret)) ret <- list(); ## handle corner case of exclusively empty sublists 
    ret; 
}; ## end extractLevelColumns() 
## simple interface function 
flattenList <- function(mainList,...) do.call(cbind,extractLevelColumns(mainList,...)); 

Thực hiện:

## define data 
mylist <- list(structure(list(Hit='True',Project='Blue',Year='2011',Rating='4',Launch='26 Jan 2012',ID='19',Dept='1, 2, 4'),.Names=c('Hit','Project','Year','Rating','Launch','ID','Dept')),structure(list(Hit='False',Error='Record not found'),.Names=c('Hit','Error')),structure(list(Hit='True',Project='Green',Year='2004',Rating='8',Launch='29 Feb 2004',ID='183',Dept='6, 8'),.Names=c('Hit','Project','Year','Rating','Launch','ID','Dept'))); 

## run it 
df <- flattenList(mylist); 
## extractLevelColumns(): 
## extractLevelColumns(): Hit 
## extractLevelColumns(): Project 
## extractLevelColumns(): Year 
## extractLevelColumns(): Rating 
## extractLevelColumns(): Launch 
## extractLevelColumns(): ID 
## extractLevelColumns(): Dept 
## extractLevelColumns(): Error 

df; 
##  Hit Project Year Rating  Launch ID Dept   Error 
## 1 True Blue 2011  4 26 Jan 2012 19 1, 2, 4    <NA> 
## 2 False <NA> <NA> <NA>  <NA> <NA> <NA> Record not found 
## 3 True Green 2004  8 29 Feb 2004 183 6, 8    <NA> 

Chức năng của tôi mạnh hơn data.table::rbindlist() kể từ 1.9.6, ở chỗ nó có thể xử lý bất kỳ số lượng mức lồng nhau và độ dài vector khác nhau trên các nhánh. Trong câu hỏi được liên kết, hàm của tôi sẽ làm phẳng danh sách OP thành một tệp dữ liệu chính xác, nhưng data.table::rbindlist() không thành công với "Error in rbindlist(jsonRList, fill = T) : Column 4 of item 16 is length 2, inconsistent with first column of that item which is length 1. rbind/rbindlist doesn't recycle as it already expects each item to be a uniform list, data.frame or data.table".

+0

Wow, cuối cùng tôi đã tìm thấy một giải pháp để san bằng loại danh sách mà tôi đang gặp phải. Cảm ơn bạn. – jcarlos

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