10

I am motivated by this article regarding Collapsible Tree in R

http://bl.ocks.org/mbostock/4339083

I am trying to reproduce the same example using a toy dataset like this

ID      Car Bus Train   Feedback_Car    Feedback_Bus    Feedback_Train
23433   Yes Yes Yes     Toyota          GreyHound       Amtrak

Which can be represented as a collapsible tree as follows

enter image description here

I am wondering if anybody can help me reproduce that concept (collapsible trees) using this toy dataset above, this example will then give me an idea how different components work, for example formatting the JSON data in R etc...and serve as a starting point. Thanks in advance.

Rorschach
  • 31,301
  • 5
  • 78
  • 129
Ezra Polson
  • 235
  • 3
  • 13

7 Answers7

5

This collapsible tree looks really cool. My approach here is to first, create a graph using igraph. I was hoping there was already a function to convert an igraph to json, however, it looks like that is an issue on github that hasn't been implemented. So, here is a simple function to do that. Then, you can just plug the resulting data into the linked source and you have a collapsible tree.

## Read your data
dat <- read.table(text="ID      Car Bus Train   Feedback_Car    Feedback_Bus    Feedback_Train
23433   Yes Yes Yes     Toyota          GreyHound       Amtrak", header=TRUE)

## Make an edgelist from your data
edges <- rbind(cbind(dat$ID, names(dat)[2:4]),
               cbind(names(dat)[2:4], as.vector(t(dat[5:7]))))

## Convert to a graph data structure
library(igraph)
g <- graph_from_edgelist(edges)

## This is the non-interactive version
plot(g, layout=layout.reingold.tilford(g, root='23433'))

enter image description here

## Recursive function to make a list of nodes to be parsed by toJSON
## call it with 'node' as the root node (here '23433')
f <- function(g, node, size=1000) {
    n <- neighbors(g, node, mode='out')
    if (length(n) == 0) return( list(name=node, size=size) )
    children <- lapply(n$name, function(x) f(g, x, size))
    list(name=node, children=children)
}

## Convert to json
library(jsonlite)
json <- toJSON(f(g, '23433'), auto_unbox = TRUE)

## I made a directory collapsible to store the index.html from the linked
## site, as well as this data
## For completeness, you should be able to run this to see the interactive results,
## But, of course, this is creating files on your box
dir.create('collapsible')
writeLines(json, 'collapsible/data.json')

## Download the index.html
download.file("https://gist.githubusercontent.com/mbostock/4339083/raw/0d003e5ea1686dd6e79562b37f8c7afca287d9a2/index.html", "collapsible/index.html", method='curl')

## Replace with the correct data
txt <- readLines('collapsible/index.html')
txt[grepl("^d3.json", txt)] <- "d3.json('data.json', function(error, flare) {"
writeLines(txt, 'collapsible/index.html')

## Open in broweser
browseURL(paste0('file://', normalizePath('collapsible/index.html')))

The results can also be seen here.

Rorschach
  • 31,301
  • 5
  • 78
  • 129
  • currently when the function `browseURL...` opens a webpage It is empty.....is that because there of an open `{` at the end of line `txt[grepl("^d3.json"..... ` that is not closed...? – Ezra Polson Oct 15 '15 at 22:13
  • 1
    that should be there although it looks confusing, it is just the opening of that function. The purpose of that line is to simply replace the data argument passed to d3.json. It will make sense if you look at the whole document. – Rorschach Oct 15 '15 at 22:15
  • @EzraPolson me too :), what did you do, what error messages? You can click the link at the bottom to see it in all its glory, with all the code you need to reproduce. – Rorschach Oct 15 '15 at 22:20
  • :) LOL, clicking on the link works, but when I copy paste this example in my R and run the code, I see a warning when I run the `download.file....` line `Warning messages: 1: running command 'curl "https://gist.githubusercontent.com/mbostock/4339083/raw/0d003e5ea1686dd6e79562b37f8c7afca287d9a2/index.html" -o "collapsible/index.html"' had status 127 2: In download.file("https://gist.githubusercontent.com/mbostock/4339083/raw/0d003e5ea1686dd6e79562b37f8c7afca287d9a2/index.html", : download had nonzero exit status` – Ezra Polson Oct 15 '15 at 22:36
  • I copied pasted that index.html page manually in the collapsible folder...never mind if it is too complicated. I will try to figure something from here...:) – Ezra Polson Oct 15 '15 at 22:36
  • 1
    @EzraPolson you are just having a problem with the `curl` option to `download.file` sounds like. You can mess with the other options, but it is `https:` so that will restrict them a bit. Probably easier to just manually copy and paste that page into `index.html`. Then, the rest should work. – Rorschach Oct 15 '15 at 22:41
2

I read the csv and make the node JSON structure like below:

d3.csv("my.csv", function(error, data) {
  var map1 = []
  data.reduce(function(map, node) {
    map1.push(node)
    return node;
  }, {});

  root = {};
  root.name = map1[0].ID;
  root.children = [];
  var car = {
    name: "Car",
    children: [{
      name: map1[0].Feedback_Car,
      children: []
    }]
  };
  root.children.push(car);
  var bus = {
    name: "Bus",
    children: [{
      name: map1[0].Feedback_Bus,
      children: []
    }]
  };
  root.children.push(bus);
  var train = {
    name: "Bus",
    children: [{
      name: map1[0].Feedback_Train,
      children: []
    }]
  };
  root.children.push(train);

});

Working code here

Hope this helps!

Cyril Cherian
  • 32,177
  • 7
  • 46
  • 55
1

I apologize for being so late. I think that you are looking for a solution in R, and not a solution that forces you to use outside code. Take advantage of the k3d3 package. https://github.com/kaseyriver11/k3d3 Here is what want:

library(k3d3)
library(RJSONIO)
library(stringr)

type <- c("Car", "Car", "Truck", "Truck", "Bus", "Bus")
name <- c("Chevy", "Ford", "Chevy", "Ford", "Greyhound", "Holiday Express")
size <- c(rep(3840,6))
data <- data.frame(type, name, size)


makeList<-function(x){
    if(ncol(x)>2){
        listSplit<-split(x[-1],x[1],drop=T)
        lapply(names(listSplit),function(y){list(name=y,children=makeList(listSplit[[y]]))})
    }else{
        lapply(seq(nrow(x[1])),function(y){list(name=x[,1][y],Percentage=x[,2][y])})
    }
}

jsonOut<-toJSON(list(name="23433",children=makeList(data)))
jsonOut2 <- str_replace_all(jsonOut, "[\r\n]" , "")

CTR(jsonOut2)

Picture of Tree with Data Provided

K Jones
  • 447
  • 2
  • 15
0

There is a detailed explanation on how to format your data here. They build on this answer on how to create Json with children.

Note: I think you will have to reshape your dataset to get the following columns: ID, Type of vehicle, Brand.

Once you have your Json ready, you grab the html file of your example and you replace 'flare.json' with the path of our data output.

Community
  • 1
  • 1
GPierre
  • 893
  • 9
  • 25
0

For what it's worth I wanted to share my way of pushing data from R to D3:

<!--begin.rcode results="asis", echo=FALSE, warning=FALSE, message=FALSE
    library(RJSONIO)
    library(MASS)
    set.seed(1234)
    data <- data.frame("Sample"=rbeta(1000,10,15))
    out  <- paste("<script type='text/javascript'> var json ='",   jsonlite::serializeJSON(data), "';</script>", sep="")
end.rcode-->

This code chunk sits right at the beginning of the body element in my RHTML file. After knitting it, the data will be written inside the output HTML file and can be accessed by D3 via the jsonvariable. Here is a screenshot of the output HTML file:

enter image description here

At the bottom of the picture you can see that you just have to parse the json object with JSON.parse() and you have your data JS ready :)

Martin Schmelzer
  • 23,283
  • 6
  • 73
  • 98
0

You can use the data.tree package to get your data converted to JSON, or also to use the networkD3 package:

dat <- read.table(text="ID      Car Bus Train   Feedback_Car    Feedback_Bus    Feedback_Train
23433   Yes Yes Yes     Toyota          GreyHound       Amtrak", header=TRUE)

## Make an edgelist from your data
edges <- rbind(cbind(dat$ID, names(dat)[2:4]),
               cbind(names(dat)[2:4], as.vector(t(dat[5:7]))))

library(data.tree)
tree <- FromDataFrameNetwork(as.data.frame(edges))

tree

That will print like this:

          levelName
1 23433            
2  ¦--Car          
3  ¦   °--Toyota   
4  ¦--Bus          
5  ¦   °--GreyHound
6  °--Train        
7      °--Amtrak   

Now, use the tree structure to plot with networkD3:

lol <- ToListExplicit(tree, unname = TRUE)

library(networkD3)

diagonalNetwork(lol)

Unfortunately, that doesn't support collapsible trees yet. But here is an example how to get what you want with Shiny. In order to convert your data to the correct JSON format, simply do this:

library(jsonlite)
json <- toJSON(lol)
Christoph Glur
  • 1,224
  • 6
  • 10
0

In the current dev version of networkD3 (v0.4.9000 @ 2017.08.30), there is a new treeNetwork() function that has this (interactive, collapsible tree network plots) and many other new features built-in.

You can install the current dev version with...

devtools::install_github("christophergandrud/networkD3")

and plot a collapsible tree network plot with your data using...

library(networkD3)

df <- read.table(header = T, stringsAsFactors = F, text = "
ID      Car Bus Train   Feedback_Car    Feedback_Bus    Feedback_Train
23433   Yes Yes Yes     Toyota          GreyHound       Amtrak
")

links <- data.frame(nodeId = c(df$ID, names(df)[2:4], as.character(df[5:7])), 
                    parentId = c("", rep(df$ID, 3), sub("^Feedback_", "", names(df[5:7]))))
links$name <- links$nodeId

treeNetwork(links, type = "tidy")

There are still plenty of bugs to work out, so we'd appreciate testing, filling out issues/bug reports, and/or pull requests. https://github.com/christophergandrud/networkD3

CJ Yetman
  • 8,373
  • 2
  • 24
  • 56