2012-07-19 22 views
10

Diese Frage zu einem generischen Mechanismus, jede Sammlung von nicht-zyklischen homogenen oder heterogenen Datenstrukturen in einen Datenrahmen für die Umwandlung. Dies kann besonders nützlich sein, wenn Sie mit der Aufnahme vieler JSON-Dokumente oder mit einem großen JSON-Dokument arbeiten, bei dem es sich um ein Array von Wörterbüchern handelt.R: Allgemein Abplattung JSON data.frame

Es gibt mehrere SO-Fragen, die sich mit der Manipulation tief verschachtelter JSON-Strukturen befassen und diese in Datenrahmen mit Funktionen wie plyr, lapply usw. umwandeln. Alle Fragen und Antworten, die ich gefunden habe, beziehen sich auf bestimmte Fälle und nicht auf einen allgemeinen Ansatz für den Umgang mit Sammlungen komplexer JSON-Datenstrukturen. In Python und Ruby wurde ich gut bedient, indem ich ein generisches Dienstprogramm zur Verflachung von Datenstrukturen implementierte, das den Pfad zu einem Blattknoten in einer Datenstruktur als den Namen des Wertes an diesem Knoten in der abgeflachten Datenstruktur verwendet. Zum Beispiel würde der Wert my_data[['x']][[2]][['y']] als result[['x.2.y']] erscheinen.

Wenn eine Sammlung dieser Datenstrukturen nicht vollständig homogen ist, wäre der Schlüssel für eine erfolgreiche Verflachung eines Datenrahmens die Ermittlung der Namen aller möglichen Datenrahmenspalten, z. B. durch die Vereinigung aller Schlüssel/Namen der Werte in den einzelnen abgeflachten Datenstrukturen.

Das scheint ein allgemeines Muster zu sein und so frage ich mich, ob jemand das schon für R gebaut hat. Wenn nicht, werde ich es bauen, aber angesichts Rs einzigartigen vielversprechenden Datenstrukturen würde ich mich über Ratschläge freuen ein Implementierungsansatz, der Heap Thrashing minimiert.

+0

Huh? Zu viel Englisch für mich (jedenfalls) zu verstehen. Schlagen Sie vor, einige reproduzierbare Eingaben mit (vermutlich) langsamem Code zu versehen, der die gewünschte Ausgabe erzeugt, und gehen Sie von dort aus weiter. Vielleicht weiß ich JSON einfach nicht. Kannst du etwas verpastbares in eine neue R-Sitzung stellen, die einige JSON-Daten von irgendwo herunterlädt, um deine Frage zu demonstrieren? [Wie man ein gutes reproduzierbares Beispiel macht] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) –

Antwort

7

Hallo @Sim Ich hatte verursachen auf Ihrem Problem gestern zu reflektieren definieren:

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 von AnnotationDbi angepasst ist ::: make.name.tree)

( flatten wird von Diskussion hier How to flatten a list to a list without coercion? angepasst)

als ein einfaches Beispiel

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" 

so ist das Ergebnis eine abgeflachte Liste mit etwa der Namensstruktur vor. Zwang wird auch vermieden, was ein Plus ist.

Ein komplizierteres Beispiel

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

UPDATE

naive Weise zu entfernen .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

Sieht vielversprechend aus. Wie würden Sie vorschlagen, dass wir die nachfolgenden '.1's loswerden? – Sim

+0

Sie sollten 'Namen (flatternd_structure) 'neu zuweisen können, oder? – Sim

+0

Ich stimme zu. Reiniger jetzt. Meine Frage bezog sich speziell auf das Konvertieren eines großen JSON-Dokuments, das ein Array von Dictionaries/Hashes ist, in data.frame. Dafür müßtest du den Spaltensatz als Vereinigung aller abgeflachten Listennamen erstellen, oder? – Sim

4

R hat zwei Pakete für den JSON-Eingang: rjson und RJSONIO. Wenn ich richtig verstehe, was Sie unter "Sammlung von nicht-zyklischen homogenen oder heterogenen Datenstrukturen" verstehen, denke ich, dass eines dieser Pakete diese Art von Struktur als list importieren wird.

Sie können dann diese Liste abflachen (in einen Vektor) unter Verwendung der unlist Funktion.

Wenn die Liste entsprechend strukturiert ist (eine nicht verschachtelte Liste, in der jedes Element die gleiche Länge hat), dann bietet as.data.frame eine Alternative, um die Liste in einen Datenrahmen umzuwandeln.

Ein Beispiel:

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

Was ist mit dem Downvote? 'unlist 'scheint genau so zu sein wie das" generic data structure flattening Utility ", das @Sim will. In der Tat enthält die ähnliche Frage, die mit @ttmaccer verbunden ist, Antworten, die ausgiebig von "unlist" Gebrauch machen. –

+0

@ttmaccer: Ja, Sie können nicht beide Möglichkeiten in R haben. Sie entweder eine flache (Vektor) Datenstruktur mit einem einzigen Datentyp oder eine verschachtelte (Liste) Struktur mit gemischten Typen. Ich denke, es gibt genug Werkzeuge in R, dass JSON transformierbar ist in was auch immer Sie wollen. –

+0

@RichieCotton @ttmaccer Ich stimme zu, dass 'unlist 'nicht auf generische Weise funktioniert. Wenn dies das beste ist, das R aus der Box hat, werde ich weitermachen und den rekursiven Abstiegs-Flattener schreiben, den ich in anderen Sprachen benutzt habe. – Sim

1

Das nachlauf jsonlite Paket eine Gabel von RJSONIO ist speziell zu machen Konvertierung zwischen JSON entworfen und Datenrahmen einfacher. Sie geben kein Beispiel json Daten, aber ich denke, das könnte das sein, was Sie suchen. Schauen Sie sich hierzu blog post oder the vignette an.

0

Große Antwort mit den Funktionen flatten und getnames. Es dauerte ein paar Minuten, um alle Optionen herauszufinden, die man benötigt, um aus einem Vektor von JSON-Strings zu einem dat.frame zu gelangen, also dachte ich, ich würde das hier aufzeichnen. Angenommen, jsonvec ist ein Vektor von JSON-Strings. Im Folgenden wird ein data.frame (data.table) erstellt, bei dem pro Zeile eine Zeile vorhanden ist und jede Spalte einem anderen möglichen Blattknoten der JSON-Struktur entspricht. Jede Zeichenfolge, die einen bestimmten Blattknoten fehlt, ist mit NA gefüllt.

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)