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.
A simple data structure, but could become difficult to follow with larger trees. echarty can read it as-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 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.param= list(type= 'tree',
data= data, label= list(offset=c(0, -12)), symbolSize= ec.clmn() # size by value
))
#p
p <- ec.init(series.param= list(type= 'treemap', data= data, leafDepth= 2))
#p
p <- ec.init(series.param= 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') )
)
#p
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
)
A familiar example is the Titanic dataset.
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')
))
)
Most popular representation is the penguins multivariate
dataset.
Data transformation is provided by an Extras utility.
See a live demo
of a similar Krane chart with 7K leaves.
Note= " The famous 'penguins' dataset has 344 records in the following format:
species,island,bill_length_mm,bill_depth_mm,flipper_length_mm,body_mass_g,sex
Adelie,Torgersen,39.1,18.7,181,3750,MALE
Adelie,Torgersen,39.5,17.4,186,3800,FEMALE
Adelie,Torgersen,40.3,18,195,3250,FEMALE
Adelie,Biscoe,37.8,18.3,174,3400,FEMALE
Adelie,Dream,37.2,18.1,178,3900,MALE
Adelie,Dream,39.5,17.8,188,3300,FEMALE
Chinstrap,Dream,50.5,18.4,200,3400,FEMALE
Gentoo,Biscoe,46.1,13.2,211,4500,FEMALE
Gentoo,Biscoe,50,16.3,230,5700,MALE"
tmp <- read.csv('https://cdn.jsdelivr.net/gh/mwaskom/seaborn-data@refs/heads/master/penguins.csv')
#data <- getListHierarchy(tmp) # transform utility is part of Extras
p <- ec.init( tooltip= list(position= c('5%','55%')),
series.param= list(type= 'sunburst', radius= list(0, 200),
emphasis= list(focus= 'none', itemStyle= list(color= 'magenta')),
data= data, labelLayout= list(hideOverlap= TRUE) )
)
#p
See also an interesting morphing demo between
tree and sunburst charts.