2012-07-19 26 views
10

Câu hỏi này là về cơ chế chung để chuyển đổi bất kỳ tập hợp các cấu trúc dữ liệu không đồng nhất hoặc không đồng nhất vào một khung dữ liệu. Điều này có thể đặc biệt hữu ích khi xử lý việc nhập nhiều tài liệu JSON hoặc với một tài liệu JSON lớn, là một mảng từ điển.R: Làm phẳng JSON chung thành dữ liệu.frame

Có một số câu hỏi SO xử lý thao tác cấu trúc JSON lồng nhau sâu và chuyển chúng thành các khung dữ liệu sử dụng chức năng như plyr, lapply, v.v. cách tiếp cận để xử lý các tập hợp các cấu trúc dữ liệu JSON phức tạp. Trong Python và Ruby, tôi đã được phục vụ tốt bằng cách triển khai một tiện ích phẳng cấu trúc dữ liệu chung sử dụng đường dẫn đến nút lá trong cấu trúc dữ liệu làm tên của giá trị tại nút đó trong cấu trúc dữ liệu phẳng. Ví dụ: giá trị my_data[['x']][[2]][['y']] sẽ xuất hiện dưới dạng result[['x.2.y']].

Nếu một bộ sưu tập các cấu trúc dữ liệu này có thể không hoàn toàn đồng nhất, thì để khám phá tên của tất cả các cột dataframe có thể, ví dụ bằng cách lấy tất cả các khóa/tên của các giá trị trong các cấu trúc dữ liệu được làm phẳng riêng lẻ.

Điều này có vẻ giống như một mô hình phổ biến và vì vậy tôi tự hỏi liệu có ai đó đã xây dựng nó cho R. Nếu không, tôi sẽ xây dựng nó, nhưng với cấu trúc dữ liệu dựa trên lời hứa duy nhất của R, tôi đánh giá cao lời khuyên một cách tiếp cận thực hiện để giảm thiểu sự heap thrashing.

+0

Huh? Quá nhiều tiếng Anh cho tôi (dù sao) để hiểu. Đề nghị cung cấp một số đầu vào có thể tái sản xuất với một số (có lẽ) mã chậm tạo ra kết quả bạn muốn và đi từ đó. Có lẽ chỉ là tôi không biết JSON. Bạn có thể cung cấp thứ gì đó có thể dán vào một phiên R mới để tải xuống một số dữ liệu JSON từ đâu đó để chứng minh câu hỏi của bạn không? [Làm thế nào để tạo ra một ví dụ tái sản xuất tốt] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) –

Trả lời

7

Hi @Sim tôi đã gây ra cho suy nghĩ về vấn đề của bạn ngày hôm qua xác định:

flatten<-function(x) { 
    dumnames<-unlist(getnames(x,T)) 
    dumnames<-gsub("(*.)\\.1","\\1",dumnames) 
    repeat { 
     x <- do.call(.Primitive("c"), x) 
     if(!any(vapply(x, is.list, logical(1)))){ 
      names(x)<-dumnames 
      return(x) 
     } 
    } 
} 
getnames<-function(x,recursive){ 

    nametree <- function(x, parent_name, depth) { 
     if (length(x) == 0) 
      return(character(0)) 
     x_names <- names(x) 
     if (is.null(x_names)){ 
      x_names <- seq_along(x) 
      x_names <- paste(parent_name, x_names, sep = "") 
     }else{ 
      x_names[x_names==""] <- seq_along(x)[x_names==""] 
      x_names <- paste(parent_name, x_names, sep = "") 
     } 
     if (!is.list(x) || (!recursive && depth >= 1L)) 
      return(x_names) 
     x_names <- paste(x_names, ".", sep = "") 
     lapply(seq_len(length(x)), function(i) nametree(x[[i]], 
      x_names[i], depth + 1L)) 
    } 
    nametree(x, "", 0L) 
} 

(getnames được chuyển thể từ AnnotationDbi ::: make.name.tree)

(flatten được điều chỉnh từ thảo luận tại đây How to flatten a list to a list without coercion?)

như một ví dụ đơn giản

my_data<-list(x=list(1,list(1,2,y='e'),3)) 

> my_data[['x']][[2]][['y']] 
[1] "e" 

> out<-flatten(my_data) 
> out 
$x.1 
[1] 1 

$x.2.1 
[1] 1 

$x.2.2 
[1] 2 

$x.2.y 
[1] "e" 

$x.3 
[1] 3 

> out[['x.2.y']] 
[1] "e" 

nên kết quả là một danh sách phẳng với xấp xỉ cấu trúc đặt tên bạn đề nghị. Coercion là tránh cũng là một cộng.

Một ví dụ phức tạp hơn

library(RJSONIO) 
library(RCurl) 
json.data<-getURL("http://www.reddit.com/r/leagueoflegends/.json") 
dumdata<-fromJSON(json.data) 
out<-flatten(dumdata) 

CẬP NHẬT

cách ngây thơ để loại bỏ đuôi .1

my_data<-list(x=list(1,list(1,2,y='e'),3)) 
gsub("(*.)\\.1","\\1",unlist(getnames(my_data,T))) 

> gsub("(*.)\\.1","\\1",unlist(getnames(my_data,T))) 
[1] "x.1" "x.2.1" "x.2.2" "x.2.y" "x.3" 
+0

Có vẻ đầy hứa hẹn. Làm thế nào bạn sẽ đề nghị chúng tôi thoát khỏi dấu '.1'? – Sim

+0

Bạn có thể gán lại tên (flattened_structure) ', phải không? – Sim

+0

Tôi đồng ý. Cleaner bây giờ. Câu hỏi của tôi là đặc biệt về việc chuyển đổi một tài liệu JSON lớn, là một mảng từ điển/băm thành data.frame. Để làm điều đó, bạn phải xây dựng bộ cột làm công đoàn của tất cả các tên danh sách phẳng, phải không? – Sim

4

R có hai gói để xử lý đầu vào JSON: rjsonRJSONIO. Nếu tôi hiểu chính xác ý của bạn bằng cách "thu thập các cấu trúc dữ liệu không đồng nhất hoặc không đồng nhất", tôi nghĩ một trong hai gói này sẽ nhập loại cấu trúc đó là list.

Sau đó, bạn có thể làm phẳng danh sách đó (thành véc tơ) bằng cách sử dụng chức năng unlist.

Nếu danh sách được cấu trúc phù hợp (danh sách không lồng nhau trong đó mỗi phần tử có cùng độ dài) thì as.data.frame thay thế để chuyển đổi danh sách thành một khung dữ liệu.

Một ví dụ:

(my_data <- list(x = list('1' = 1, '2' = list(y = 2)))) 
unlist(my_data) 
+0

Có gì với downvote? 'unlist' có vẻ giống như" tiện ích làm phẳng cấu trúc dữ liệu chung "mà @Sim muốn. Trong thực tế, câu hỏi tương tự được liên kết bởi @ttmaccer bao gồm các câu trả lời sử dụng rộng rãi 'unlist'. –

+0

@ttmaccer: Vâng, bạn không thể có cả hai cách trong R. Bạn hoặc là một cấu trúc dữ liệu phẳng (vectơ) với một kiểu dữ liệu đơn lẻ hoặc một cấu trúc lồng nhau (danh sách) với các kiểu hỗn hợp. Tôi nghĩ rằng có đủ công cụ trong R mà bất kỳ JSON nào cũng có thể biến đổi thành bất kỳ thứ gì bạn muốn. –

+0

@RichieCotton @ttmaccer Tôi đồng ý rằng 'unlist' sẽ không hoạt động theo cách chung chung. Nếu đây là R tốt nhất đã ra khỏi hộp, tôi sẽ đi trước và viết các flattener gốc đệ quy tôi đã sử dụng trong các ngôn ngữ khác. – Sim

1

Gói jsonlite là một ngã ba của RJSONIO thiết kế đặc biệt để làm cho chuyển đổi giữa JSON và khung dữ liệu dễ dàng hơn. Bạn không cung cấp bất kỳ ví dụ nào về dữ liệu json, nhưng tôi nghĩ rằng đây có thể là những gì bạn đang tìm kiếm. Hãy xem số điện thoại blog post hoặc the vignette này.

0

Câu trả lời hay với chức năng làm phẳng và tên miền. Mất một vài phút để tìm ra tất cả các tùy chọn cần thiết để có được từ một vector của chuỗi JSON đến một data.frame, vì vậy tôi nghĩ rằng tôi muốn ghi lại rằng ở đây. Giả sử jsonvec là một vectơ của các chuỗi JSON. Sau đây xây dựng một data.frame (data.table) trong đó có một hàng trên mỗi chuỗi và mỗi cột tương ứng với một nút lá có thể khác nhau của cây JSON. Bất kỳ chuỗi nào thiếu một nút lá cụ thể đều được điền vào NA.

library(data.table) 
library(jsonlite) 
parsed = lapply(jsonvec, fromJSON, simplifyVector=FALSE) 
flattened = lapply(parsed, flatten) #using flatten from accepted answer 
d = rbindlist(flattened, fill=TRUE) 
Các vấn đề liên quan