2015-06-09 5 views
7

den folgenden Datenrahmen vor:Konvertieren einen Datenrahmens zu einer treeNetwork kompatibel Liste

Country  Provinces   City Zone 
1 Canada Newfondland  St Johns A 
2 Canada   PEI Charlottetown B 
3 Canada Nova Scotia  Halifax C 
4 Canada New Brunswick Fredericton D 
5 Canada  Quebec   NA NA 
6 Canada  Quebec Quebec City NA 
7 Canada  Ontario  Toronto A 
8 Canada  Ontario  Ottawa B 
9 Canada  Manitoba  Winnipeg C 
10 Canada Saskatchewan  Regina D 

Gäbe es einen klugen Weg, es kompatibel Liste in eine treeNetwork zu konvertieren (aus dem networkD3 Paket) in Form von:

CanadaPC <- list(name = "Canada", 
       children = list(
        list(name = "Newfoundland", 
         children = list(list(name = "St. John's", 
              children = list(list(name = "A"))))), 
        list(name = "PEI", 
         children = list(list(name = "Charlottetown", 
              children = list(list(name = "B"))))), 
        list(name = "Nova Scotia", 
         children = list(list(name = "Halifax", 
              children = list(list(name = "C"))))), 
        list(name = "New Brunswick", 
         children = list(list(name = "Fredericton", 
              children = list(list(name = "D"))))), 
        list(name = "Quebec", 
         children = list(list(name = "Quebec City"))), 
        list(name = "Ontario", 
         children = list(list(name = "Toronto", 
              children = list(list(name = "A"))), 
             list(name = "Ottawa", 
              children = list(list(name = "B"))))), 
        list(name = "Manitoba", 
         children = list(list(name = "Winnipeg", 
              children = list(list(name = "C"))))), 
        list(name = "Saskatchewan", 
         children = list(list(name = "Regina", 
              children = list(list(name = "D"))))))) 

um einen Reingold-Tilford Baum zu zeichnen, die einen beliebigen Satz von Ebenen haben würden:

enter image description here

Ich habe mehrere suboptimale Routinen einschließlich einer unordentlichen Kombination von for Loops ausprobiert, aber ich kann das nicht im gewünschten Format bekommen.

Idealerweise würde die Funktion skalieren, um die erste Spalte als root (Startpunkt) zu betrachten, und die anderen Spalten wären verschiedene Ebenen von Kindern.


bearbeiten

A similar question wurde zum gleichen Thema gefragt und @MrFlick bot eine interessante rekursive Funktion. Der ursprüngliche Datenrahmen hatte einen festen Satz von Ebenen. Ich führte NA s ein, um eine weitere Komplexitätsebene (beliebige Menge von Ebenen) hinzuzufügen, die in der anfänglichen Lösung von @MrFlick nicht angesprochen wird. Hier ist eine solche Implementierung


Daten

structure(list(Country = c("Canada", "Canada", "Canada", "Canada", 
"Canada", "Canada", "Canada", "Canada", "Canada", "Canada"), 
    Provinces = c("Newfondland", "PEI", "Nova Scotia", "New Brunswick", 
    "Quebec", "Quebec", "Ontario", "Ontario", "Manitoba", "Saskatchewan" 
    ), City = c("St Johns", "Charlottetown", "Halifax", "Fredericton", 
    NA, "Quebec City", "Toronto", "Ottawa", "Winnipeg", "Regina" 
    ), Zone = c("A", "B", "C", "D", NA, NA, "A", "B", "C", 
    "D")), class = "data.frame", row.names = c(NA, -10L), .Names = c("Country", 
"Provinces", "City", "Zone")) 

Antwort

7

Eine bessere Strategie für dieses Szenario kann eine rekursive split() sein. Als erstes ist hier die Beispieldaten

dd<-structure(list(Country = c("Canada", "Canada", "Canada", "Canada", 
"Canada", "Canada", "Canada", "Canada", "Canada", "Canada"), 
    Provinces = c("Newfondland", "PEI", "Nova Scotia", "New Brunswick", 
    "Quebec", "Quebec", "Ontario", "Ontario", "Manitoba", "Saskatchewan" 
    ), City = c("St Johns", "Charlottetown", "Halifax", "Fredericton", 
    NA, "Quebec City", "Toronto", "Ottawa", "Winnipeg", "Regina" 
    ), Zone = c("A", "B", "C", "D", NA, NA, "A", "B", "C", 
    "D")), class = "data.frame", row.names = c(NA, -10L), .Names = c("Country", 
"Provinces", "City", "Zone")) 

zur Kenntnis, dass‘ich die "NA" Saiten mit echten NA Werte ersetzt haben. Nun wird die Funktion

rsplit <- function(x) { 
    x <- x[!is.na(x[,1]),,drop=FALSE] 
    if(nrow(x)==0) return(NULL) 
    if(ncol(x)==1) return(lapply(x[,1], function(v) list(name=v))) 
    s <- split(x[,-1, drop=FALSE], x[,1]) 
    unname(mapply(function(v,n) {if(!is.null(v)) list(name=n, children=v) else list(name=n)}, lapply(s, rsplit), names(s), SIMPLIFY=FALSE)) 
} 

Dann können wir

rsplit(dd) 

laufen scheint mit den Testdaten zu arbeiten. Der einzige Unterschied ist die Reihenfolge, in der die Kinderknoten angeordnet sind.

+0

Ich denke, es bietet nicht die richtige Verschachtelung, da die Funktion 'treeNetwork()' nicht die erwartete Ausgabe rendert. Ich habe die Frage bearbeitet, um die richtige 'dput()' Struktur widerzuspiegeln. –

+0

Was genau ist falsch an der Eingabe? Sie enthalten keinen Code zum Testen des Ergebnisses. – MrFlick

+1

Tatsächlich nimmt diese Lösung keinen Wurzelknoten an, daher gibt sie eine Liste zurück. Versuchen Sie 'rsplit (dd) [[1]]' – MrFlick