Hierarchical data and charts with echarty

Hierarchical data in R can be represented with different data structures:

Below are examples for those four data types. Click Show buttons to see code.
Many users find the second representation most intuitive and straightforward.

Several charts can represent hierarchical data. Most common are sunburst, tree and treemap, but sometimes sankey, graph, even stacked bars can also do the job.

List of lists

A simple data structure, but could become difficult to follow with larger trees. echarty can read it as it is, no need for data transformation.

data <- list(
    list(name='Grandpa',
        children= list(
            list(name='Uncle Leo', value=15,
                children= list(list(name='Cousin Jack',value=2), 
                    list(name='Cousin Mary',value=5,
                        children=list(list(name='Jackson',value=2))), 
                            list(name='Cousin Ben',value=4))),
            list(name='Father', value=10,
               children= list(list(name='Me',value=5),
                    list(name='Brother Peter',value=1))))),
    list(name='Granma Nancy',
        children= list(
            list(name='Uncle Nike',
               children=list(list(name='Cousin Betty',value=1), 
                    list(name='Cousin Jenny',value=2)))))
)# -------------------------------------------------

library(echarty)
ec.init(
    series= list(list(
        type= 'sunburst', 
        data= data, 
        radius= list(0, '90%'),
        labelLayout= list(hideOverlap= TRUE) ))
)

Data frame with parent-child columns

Data needs to be transformed from data.frame to lists with utility ec.data(format=‘treePC’).

df <- data.frame(
    parents = c("","Reptiles", "Reptiles", "Mammals", "Mammals", "Fish", "Sharks", "Sharks", "Animals", "Animals", "Animals"),
    children = c("Animals", "Snakes", "Lizards", "Dogs", "Humans", "Sharks", "hammerhead", "thresher", "Reptiles", "Mammals", "Fish"),
    value = c(100, 15, 20, 10, 25, 20, 8, 12, 40, 35, 25)) 
dpc <- ec.data(df, format='treePC')

p <- ec.init(
    series= list(list(type= 'sunburst', 
        data= dpc, # =[[1]]$children,
        radius= c(0, '90%'), 
        label= list(rotate='tangential'), 
        emphasis=list(focus='ancestor') ))
)

p <- ec.init(
    series= list(list(type= 'treemap', 
        data=dpc, leafDepth=2, 
        label= list(offset = c(0, -12)), 
        symbolSize= ec.clmn() )),
    tooltip= list(show= TRUE)
)

#Hierarchy with sankey
edges <- df[-1,] |> rename(source= parents, target= children)
nodes <- list();
for(n in unique(c(edges$source, edges$target)))
    nodes <- append(nodes, list(list(name=n)))
ec.init(
    tooltip= list(show=T),
    series= list(list(type= 'sankey', 
        data= nodes, 
        edges= ec.data(edges, 'names') ))
) 

ec.init(
    tooltip= list(formatter='{c}'),
    series= list(list(type= 'tree', 
        data= dpc, symbol= 'circle',
        label= list(offset = c(0, -12)), 
        symbolSize= ec.clmn() ))  # size by value
)

Data frame with a column for each level

Data is transformed from data.frame to lists with utility ec.data(format=‘treeTK’)


# build required pathString,value and optional itemStyle columns
df <- as.data.frame(Titanic) |> rename(value= Freq) |> mutate(
    pathString= paste('Titanic\nSurvival', Survived, Age, Sex, Class, sep='/'),
    itemStyle= case_when(Survived=='Yes' ~"color='green'", TRUE ~"color='LightSalmon'")) |>
    select(pathString, value, itemStyle)

dat <- ec.data(df, format='treeTK')
dat[[1]] <- within(dat[[1]], { 
    itemStyle <- list(color= 'white'); pct <- 100 })  # customize top

ec.init(
    tooltip= list(formatter= ec.clmn('%@<br>%@%','value','pct')),
    series= list(list(
        type= 'sunburst', radius= c(0, '90%'), label= list(rotate=0),
        # type= 'tree', symbolSize= htmlwidgets::JS("x => {return Math.log(x)*10}",
        # type= 'treemap', upperLabel= list(show=TRUE, height=30), itemStyle= list(borderColor= '#999'), #leafDepth=4,
        data= dat,
        labelLayout= list(hideOverlap= TRUE),
        emphasis= list(focus='none') 
    ))
)

Data frame with nested data.frame children

Similar to list-of-lists, but with data.frame items, rarely used. Data transformation is carried out with jsonlite::toJSON

animl <- data.frame(name= "Animals", value= 255)  # top level
animl$children <- 
    list(data.frame(name= c("Mammals", "Reptiles","Fish"), value= c(100,90,60)))
animl$children[[1]]$children <- c(
    list(data.frame(name= c("Dogs", "Humans"), value= c( 15, 35))),
    list(data.frame(name= c("Snakes", "Lizards"), value= c(30, 40))),
    list(data.frame(name= c("Sharks"), value = 30)) )
animl$children[[1]]$children[[3]]$children <- 
    list(data.frame(name= c("hammerhead", "thresher"), value= c(10,20)))

data <- jsonlite::toJSON(animl)

p <- ec.init(
    series= list(list(type= 'tree', 
        data= data, 
        label= list(offset=c(0, -12)), 
        symbolSize= ec.clmn()  # size by value
)))

p <- ec.init(
    series= list(list(type= 'treemap', 
        data= data, leafDepth= 2)) 
)

ec.init(
    series= list(list(type= 'sunburst', 
        # use children instead of root(data) to auto-color descendants
        data= jsonlite::toJSON(animl$children[[1]]), 
        radius= c(0, '90%'), 
        label= list(rotate= 'tangential') ))
)
See also an interesting morphing demo between tree and sunburst charts.
PS: if you like these solutions, please consider granting a Github star ⭐ to echarty.