"""Utility functions to prepare data for Plotly Treemap, Sunburst and Icicle plots."""
# Author(s): Davide.De-Marchi@ec.europa.eu
# Copyright © European Union 2022-2023
#
# Licensed under the EUPL, Version 1.2 or as soon they will be approved by
# the European Commission subsequent versions of the EUPL (the "Licence");
#
# You may not use this work except in compliance with the Licence.
#
# You may obtain a copy of the Licence at:
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
# Unless required by applicable law or agreed to in writing, software
# distributed under the Licence is distributed on an "AS IS"
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
#
# See the Licence for the specific language governing permissions and
# limitations under the Licence.
##################################################################################################################################
# Creation of a plotly treemap
##################################################################################################################################
# Node class to construct the treemap: stores a name and a float value
class TreemapNode():
def __init__(self, name, value=0.0):
self.name = name
self.value = value
def __repr__(self):
return self.name
def __str__(self):
return self.name
##################################################################################################################################
# Creation of Treemap to be displayed with Plotly
##################################################################################################################################
##################################################################################################################################
# Create a treemap from a list of names with an implicit tree structure, example: ['JRC', 'JRC.D', 'JRC.D.3', ...]
# Returns 3 lists to be input to Plotly treemap: labels, parents, values!
# The values for intermediate nodes are calculated automatically
#
# Example:
# valuefor = {'A.1.1': 10.0, 'A.2': 5.0}
# labels, parents, values = createTreemapFromList(['A','A.1','A.2','A.1.1'], rootName='A', valuefor=valuefor)
#
# import plotly.graph_objects as go
# fig = go.Figure()
# fig.add_trace(go.Treemap(ids=labels, labels=labels, parents=parents, values=values, branchvalues='total', maxdepth=3, root_color="lightgrey"))
# fig.add_trace(go.Sunburst(ids=labels, labels=labels, parents=parents, values=values, branchvalues='total', maxdepth=3, root_color="lightgrey"))
# fig.show()
##################################################################################################################################
[docs]def createTreemapFromList(nameslist=[], rootName='Root', separator='.', valuefor={}):
"""
Preprocessing of a list of strings having a hierarchical structure (defined by a separator, e.g. '.'), in view of the display of a Plotly Treemap, Sunburst or Icicle chart. Each node of the tree will have a 'dimension' that influences the way to display it (the space occupied, or the size, etc.)
Parameters
----------
nameslist : list of strings, optional
List of strings to preprocess. The hierarchical structure is defined by the separator character. Default is []
rootName : str, optional
Name to assign to the root node of the hierarchical structure (default is 'Root')
separator : str, optional
Separator character that defines the hierarchical structure (default is '.')
valuefor : dict, optional
Dictionary to assign a numerical value to each node of the tree (default is {})
Return
------
a tuple of 3 elements: labels, parents, values
labels is a list of the names of the nodes
parents is the list of the parents of each of the nodes
values is the list of numerical values assigned to each of the node (and summed up in the parent-son relation)
These 3 lists can be given as input to the plotly.graph_objects.Treemap/Sunburst/Icicle functions
Example
-------
Creation of a Treemap chart in Plotly using numerical data associated to JRC units and directorates::
import plotly.graph_objects as go
from vois import treemapPlotly
valuefor = {'JRC.A.1': 3.0, 'JRC.A.2': 5.0, 'JRC.B.1': 12.0, 'JRC.B.2': 7.0,
'JRC.B.3': 3.0, 'JRC.C.1': 7.0, 'JRC.C.2': 2.0}
labels, parents, values = treemapPlotly.createTreemapFromList(['JRC.A','JRC.A.1',
'JRC.A.2','JRC.B.1',
'JRC.B.2','JRC.B.3',
'JRC.C.1','JRC.C.2',],
rootName='JRC',
valuefor=valuefor)
fig = go.Figure()
fig.add_trace(go.Treemap(ids=labels, labels=labels, parents=parents, values=values,
branchvalues='total', maxdepth=3, root_color="lightgrey"))
fig.update_layout(margin=dict(t=38, l=0, r=0, b=10), height=400,
hoverlabel=dict(bgcolor="#eee", align="left"),
title={'text': "JRC units", 'y':0.96, 'x':0.5,
'xanchor': 'center', 'yanchor': 'top'})
fig.show()
.. figure:: figures/treemap.png
:scale: 100 %
:alt: treemap example
Display of a Plotly Treemap extracted from a list of strings separated by '.'
"""
# Add a node to a tree-node
def addNode(name, value, parent):
nonlocal nodes, parent_of
if not name in nodes:
elem = TreemapNode(name, value=value)
nodes[name] = elem
parent_of[name] = parent
while parent != None:
parent.value += value
parent = parent_of[parent.name]
return elem
nodes = {}
parent_of = {}
root = addNode(rootName,0.0,None)
# Manage an implicit tree with separator!
for name in nameslist:
parts = name.split(separator)
fullname = ''
for part in parts:
parentname = fullname
if len(fullname) == 0: fullname += part
else: fullname += separator + part
parent = root
if parentname in nodes: parent = nodes[parentname]
value = 0.0
if fullname in valuefor: value = valuefor[fullname]
addNode(fullname,value,parent)
#return nodes, parent_of
labels = []
parents = []
values = []
for key, value in nodes.items():
labels.append(key)
pname = str(parent_of[key])
if pname == 'None': parents.append(None)
else: parents.append(pname)
values.append(value.value)
return labels, parents, values