Ezra Polson
Ezra Polson

Reputation: 235

Collapsible tree in R

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.

Upvotes: 10

Views: 4380

Answers (7)

CJ Yetman
CJ Yetman

Reputation: 8848

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

Upvotes: 0

Christoph Glur
Christoph Glur

Reputation: 1244

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)

Upvotes: 0

K Jones
K Jones

Reputation: 447

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

Upvotes: 1

Martin Schmelzer
Martin Schmelzer

Reputation: 23879

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 :)

Upvotes: 0

GPierre
GPierre

Reputation: 903

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.

Upvotes: 0

Rorschach
Rorschach

Reputation: 32426

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.

Upvotes: 5

Cyril Cherian
Cyril Cherian

Reputation: 32327

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!

Upvotes: 2

Related Questions