# visualize/nxgrph.py
""" :mod:`~tessif.visualize.nxgrph` is a :mod:`tessif`
interface drawing :class:`networkx.Graph` like objects. Designed to be used as
programmatic interface with the option for quick and easy humanly invoked
tweaks.
Visualizing graphs using :mod:`~tessif.visualize.nxgrph` builds on
providing data in a 3-fold parameter system:
- :paramref:`~draw_graph.dflts` are provided by
:attr:`tessif.frused.defaults.nxgrph_visualize_defaults`
serving as fallback data to garantuee a graph can be drawn.
- :paramref:`attribute
<draw_nodes.node_attr>` like parameters are
(supposedly) provided by a
:class:`tessif.transform.es2mapping.base.ESTransformer` child and will
overwrite defaults (see
:paramref:`~tessif.transform.es2mapping.base.ESTransformer.node_data`)
- :paramref:`~draw_graph.kwargs` alllow human intervention and will
overwrite programmatic input
Note
----
To make use of the programmatic interface see the
:mod:`tessif.transform.es2mapping` module. Choose one of the hard coded
:class:`tessif.transform.es2mapping.base.ESTransformer` childs, or create one
of your own and supply it to :paramref:`draw_graph.formatier`.
Example
-------
Highlighting the edge from 1 to 2 and the node 0 in a 3-complete graph
using 'human intervention':
>>> import matplotlib.pyplot as plt
>>> import networkx as nx
>>> import tessif.visualize.nxgrph as nxv
>>> G = nx.complete_graph(3)
>>> grphdrw = nxv.draw_graph(
... G, node_color={0: 'red'}, edge_color={(1, 2): 'red'})
IGNORE:
>>> title = plt.gca().set_title('visualize.nxgrph module intent example')
>>> plt.draw()
>>> plt.pause(2)
>>> plt.close('all')
IGNORE
Visualizing a results transformed ES simulation model - Design Case:
1. Handle imports:
>>> import tessif.examples.data.omf.py_hard as omf_examples
>>> from tessif.transform.es2mapping import omf as tomf
>>> from tessif.transform import nxgrph as nxt
2. Simulate energy system (using oemof in this case):
>>> es = omf_examples.create_star()
3. Choose ES dependend :class:`Resultiers
<tessif.transform.es2mapping.omf.LoadResultier>` and/or
:class:`Formatiers <tessif.transform.es2mapping.omf.AllFormatier>` of
your liking:
>>> formatier = tomf.AllFormatier(es, cgrp='carrier')
>>> grph = nxt.Graph(tomf.LoadResultier(es))
4. Tweak edge width a little:
>>> for key, value in formatier.edge_data()['edge_width'].items():
... formatier.edge_data()['edge_width'][key] = 3 * value
5. Draw the graph:
>>> grphdrw = nxv.draw_graph(grph, formatier=formatier, layout='neato')
>>> plt.draw()
IGNORE:
>>> title = plt.gca().set_title('visualize.nxgrph module design example')
>>> plt.pause(7)
>>> plt.close('all')
IGNORE
.. image:: images/design_example.png
:align: center
:alt: design_example
"""
from collections import defaultdict
from tessif.frused import namedtuples as nts
import networkx as nx
from matplotlib import pyplot as plt
import logging
import dcttools
from tessif.write import log
import tessif.frused.defaults as convs
logger = logging.getLogger(__name__)
dcttools.logger = logger
[docs]@log.timings
def draw_nodes(
grph, pos, node_attr, dflts, draw_fency_nodes=True,
fltr='', xcptns=[], **kwargs):
"""
Draw a Graph's nodes using the
:func:`~networkx.drawing.nx_pylab.draw_networkx_nodes`
Convenience wrapper for tweaking a nodes's position, shape, size and color.
Designed to be called by :func:`draw_graph`. Nonetheless usable as
standalone drawing utility.
Parameters
----------
grph : :class:`networkx.Graph` like
The graph object to be drawn.
pos : dict
A dict with nodes as keys and positions as values
node_attr : dict of dict
A dict with attribute names as keys and dicts as values. Value dicts
consist of nodes as keys and attribute parameters as values::
{attr_name: {node: attr_parameter}}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` object.
Use one of those for automated node designs. Will be unpacked and
passed as kwargs to
:func:`~networkx.drawing.nx_pylab.draw_networkx_nodes`
dflts: dict
A dict with attribute names as keys and default parameters as values::
{attr_name: default}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use this dictionairy to key all the attributes you want be accessed
during drawing. The :paramref:`~draw_nodes.node_attr` will be
filled with all attributes missing and after that populated with the
:paramref:`~draw_nodes.kwargs` values.
draw_fency_nodes: bool
Whether to use visualization to highlight node attributes or not.
If ``True``:
- nodes of variable size are visualized with outer fading circles
- nodes of explicitly stated size are visualized using the
``node_fill_size`` attribute
If ``False``:
- nodes of variable size are drawn as filled circle at default size
- nodes of explicitly stated size are drawn as filled circles
fltr: str (default='')
:paramref:`~draw_nodes.kwargs` filter strings. Kwargs having
:paramref:`~draw_nodes.fltr` in them will be stripped
of the filter keyword and passed on for drawing.
For filtering without stripping list the respective kwarg in
:paramref:`~draw_nodes.xcptns`
Allows for wrapping the function with other drawing subutilities into
a toplevel function like :meth:`draw_graph` wihtout sacrificing the
possibility to manipulate each sublevel call via ``kwargs``.
xcptns: sequence of str (default=[])
Sequence of key word arguments that won't be stripped of the
:paramref:`~draw_nodes.fltr`.
Whether a kwarg should be stripped or not, depends on the naming
convention of :func:`~networkx.drawing.nx_pylab.draw_networkx_nodes`.
Make sure to checkout their documentation to get expected behaviour.
kwargs:
If you don't want to make use of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child. you can
"manually" format your nodes using keyword arguments. They can either
be the same for all nodes as in::
node_size=10
Or dictionairies using nodes as keys and attribute parameters as
values as in::
node_size={'node_1': 10, 'node_2': 3}
Nodes not present as keys will be set as defined in
:paramref:`~draw_nodes.dflts`. See
:func:`~networkx.drawing.nx_pylab.draw_networkx_nodes` for supported
kwargs.
Return
------
node_draws : list
list of :class:`matplotlib.collections.PathCollection` representing
the nodes
Notes
-----
Current implementation needs ``node_fill_size`` entries to be present in
all :paramref:`~draw_nodes.node_attr` node dicts (of the nodes beeing
drawn) for drawing fency nodes. Hence it is recommended to have a default
entry of ``node_fill_size`` in the :paramref:`~draw_nodes.dflts` dict.
Examples
--------
>>> import matplotlib.pyplot as plt
>>> import networkx as nx
>>> import tessif.visualize.nxgrph as nxv
>>> from collections import defaultdict
>>> G=nx.complete_graph(3)
>>> pos=nx.spring_layout(G)
>>> node_attr=defaultdict(dict)
>>> defaults={'node_uid': None, 'node_size': 3000, 'node_shape':'o',
... 'node_fill_size': 3000, 'node_color': 'green', 'node_alpha': 1.0}
>>> fltr='node_'
>>> xcptns=['node_size', 'node_color', 'node_shape', 'node_fill_size']
>>> node_draws = nxv.draw_nodes(
... G, pos ,node_attr, defaults, draw_fency_nodes=False,
... fltr=fltr, xcptns=xcptns, node_color='pink')
>>> print(list(map(type, node_draws))[0])
<class 'matplotlib.collections.PathCollection'>
IGNORE:
>>> title = plt.gca().set_title('draw_nodes example')
>>> plt.draw()
>>> plt.pause(2)
>>> plt.close('all')
IGNORE
.. image:: images/draw_nodes_example.png
:align: center
:alt: draw_nodes_example.png
"""
# Use kfrep to replace fltr in keys and kfltr for prefiltering prefix
node_attr, node_dflts, node_kwargs = dcttools.kfrep(
dcts=dcttools.kfltr(dcts=[node_attr, dflts, kwargs], fltr=fltr),
fnd=fltr, xcptns=xcptns)
# Swap keys to allow aggregation
node_attr = dcttools.kswap(node_attr)
# Aggregate all attributes into node_attr for drawing:
node_attr = dcttools.maggregate(
tlkys=grph.nodes, nstd_dcts=[node_attr, ], dcts=[node_dflts, ],
**node_kwargs)
# A list of networkx returned node linestyles
node_draws = []
# Iterate through nodes to respect possible updates
for node in grph.nodes:
# filter out non node kwargs:
node_attr[node] = {key: value for key, value in node_attr[node].copy().items()
if key in convs.nx_label_kwargs['nodes']}
# pop node fill size since it ain't a registered kwarg
node_fill_size = node_attr[node].pop('node_fill_size')
# 3.) Draw nodes:
# check if node size is variable...
if node_attr[node].get('node_size') == 'variable':
# ... yes, so do you want to visualize that ? ....
if draw_fency_nodes:
# ... yes, so draw a multi circle with fading intensity
# towards the outer radius (designed for visualizing variable
# node sizes like buses)
draws = 3
for draw in range(draws):
# decrease node radius with each draw:
node_attr[node]['node_size'] = round(
dflts['node_size'] *
dflts['node_variable_size_scaling'] *
(1-draw/draws), 0)
# increase alpha with each draw:
node_attr[node]['alpha'] = round(
(draw+1)/draws, 3)
node_draws.append(nx.draw_networkx_nodes(
grph, pos, nodelist=[node],
**node_attr[node]))
else:
# ... no, variable nodes should be drawn in default size.
node_attr[node]['node_size'] = dflts['node_size']
node_draws.append(nx.draw_networkx_nodes(
grph, pos, nodelist=[node], **node_attr[node]))
else:
# ... no, node size is explicitly stated...
# ... but do you want the fill_size attribute to be visualized? ...
if draw_fency_nodes:
# ... yes, so draw an outer border and an inner filled cycle
draws = 2
for draw in range(draws):
if draw == 0:
# Drawing outer border:
original_color = node_attr[node]['node_color']
node_attr[node]['node_color'] = 'white'
edgecolors = original_color
else:
# Drawing inner filled cycle:
original_size = node_attr[node]['node_size']
node_attr[node]['node_size'] = node_fill_size
edgecolors = node_attr[node].get(
'node_color', dflts['node_color'])
# Do the actual drawing
node_draws.append(nx.draw_networkx_nodes(
grph, pos, nodelist=[node],
edgecolors=edgecolors,
**node_attr[node]))
# restore dflts
node_attr[node]['node_color'] = original_color
if draw > 0:
node_attr[node]['node_size'] = original_size
else:
# ... no, just draw nodes with the size stated
node_draws.append(nx.draw_networkx_nodes(
grph, pos, nodelist=[node], **node_attr[node]))
return node_draws
[docs]@log.timings
def drawing_node_labels(grph, pos, node_attr, dflts,
fltr='', xcptns=[], **kwargs):
r"""
Draw a Graph's node labels using
:func:`~networkx.drawing.nx_pylab.draw_networkx_labels`
Convenience wrapper for tweaking node label appearances. Designed to be
called by :func:`draw_graph`. Nonetheless usable as standalone drawing
utility.
Parameters
----------
grph : :class:`networkx.Graph` like
The graph object to be drawn.
pos : dict
A dict with nodes as keys and positions as values
node_attr : dict of dict
A dict with attribute names as keys and dicts as values. Value dicts
consist of nodes as keys and attribute parameters as values::
{attr_name: {node: attr_parameter}}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use one of those for automated node label designs. Will be unpacked and
passed as kwargs to
:func:`~networkx.drawing.nx_pylab.draw_networkx_labels`
dflts: dict
A dict with attribute names as keys and default parameters as values::
{attr_name: default}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use this dictionairy to key all the attributes you want be accessed
during drawing. The :paramref:`~drawing_node_labels.node_attr` will be
filled with all attributes missing and after that populated with the
:paramref:`~drawing_node_labels.kwargs` values.
fltr: str (default='')
:paramref:`~drawing_node_labels.kwargs` filter strings. Kwargs having
:paramref:`~drawing_node_labels.fltr` in them will be stripped
of the filter keyword and passed on for drawing.
For filtering without stripping list the respective kwarg in
:paramref:`~drawing_node_labels.xcptns`
Allows for wrapping the function with other drawing subutilities into
a toplevel function like :meth:`draw_graph` wihtout sacrificing the
possibility to manipulate each sublevel call via ``kwargs``.
xcptns: sequence of str (default=[])
Sequence of key word arguments that won't be stripped of the
:paramref:`~drawing_node_labels.fltr`.
Whether a kwarg should be stripped or not, depends on the naming
convention of :func:`~networkx.drawing.nx_pylab.draw_networkx_labels`.
Make sure to checkout their documentation for getting expected
behaviour.
kwargs:
If you don't want to make use of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child. you can
"manually" format your nodes using keyword arguments. They can either
be the same for all nodes as in::
font_color='black'
Or dictionairies using nodes as keys and attribute parameters as
values as in::
font_color={'node_1': 'red', 'node_2': 'blue'}
Nodes not present as keys will be set as defined in
:paramref:`~drawing_node_labels.dflts`. See
:func:`~networkx.drawing.nx_pylab.draw_networkx_labels` for supported
kwargs.
Return
------
node_uid_draws : dict
:class:`matplotlib.text.Text` objects representing the drawn node
labels keyed by label as in:
``{'node_uid': Text('drawn_label')}``
Notes
-----
Current (August 2019) implementation of
:func:`~networkx.drawing.nx_pylab.draw_networkx_labels` does not need any
:paramref:`~drawing_node_labels.dflts` or
:paramref:`~drawing_node_labels.xcptns` to be defined.
Examples
--------
Drawing node labels (Cryptic :func:`print` is to demonstrate return):
>>> import matplotlib.pyplot as plt
>>> import networkx as nx
>>> import tessif.visualize.nxgrph as nxv
>>> from collections import defaultdict
>>> G=nx.complete_graph(3)
>>> pos=nx.spring_layout(G)
>>> node_attr=defaultdict(dict)
>>> defaults={'node_uid': None}
>>> fltr='node_'
>>> node_uid_draws = nxv.drawing_node_labels(
... G, pos, node_attr, defaults, fltr=fltr,
... font_color='red', node_font_color='green')
>>> print(
... list(k for k in node_uid_draws)[0], ':',
... list(map(type, (v for v in node_uid_draws.values())))[0])
0 : <class 'matplotlib.text.Text'>
IGNORE:
>>> title = plt.gca().set_title('drawing_node_labels example')
>>> plt.gca().margins(10, 10)
>>> plt.draw()
>>> plt.pause(2)
>>> plt.close('all')
IGNORE
.. image:: images/drawing_node_labels_example.png
:align: center
:alt: drawing_node_labels_example_image.png
"""
# Use kfrep to replace fltr in keys and kfltr for prefiltering prefix
node_attr, dflts, kwargs = dcttools.kfrep(
dcts=dcttools.kfltr(dcts=[node_attr, dflts, kwargs], fltr=fltr),
fnd=fltr, xcptns=xcptns)
# Swap keys to allow aggregation
node_attr = dcttools.kswap(node_attr)
# Aggregate all attributes into node_attr for drawing
node_attr = dcttools.maggregate(
tlkys=grph.nodes, nstd_dcts=[node_attr, ], dcts=[dflts, ],
**kwargs)
# Text objects representing the node labels keyed by node labels
node_uid_draws = {}
# Draw nodes:
for node in grph.nodes:
# manually fix font_alpha (node_font_alpha before aggregating) to alpha
# make sure it's set to 1 if neither defined in node_attr nor dflts
node_attr[node]['alpha'] = node_attr[node].pop(
'font_alpha', dflts.get('node_font_alpha', 1.0))
# filter out non label kwargs:
node_attr[node] = {key: value for key, value in node_attr[node].copy().items()
if key in convs.nx_label_kwargs['labels']}
node_uid_draws.update(
nx.draw_networkx_labels(grph, pos, **node_attr[node]))
return node_uid_draws
[docs]@log.timings
def draw_edges(grph, pos, edge_attr, node_attr,
dflts, fltr=('', ''), xcptns=([], [],), **kwargs):
r"""
Draw a Graph's edges using
:func:`~networkx.drawing.nx_pylab.draw_networkx_edges`.
Convenience wrapper for tweaking an edge's position shape, size and color.
Designed to be called by :func:`draw_graph`. Nonetheless usable as
standalone drawing utility.
Parameters
----------
grph : :class:`networkx.Graph` like
The graph object to be drawn.
pos : dict
A dict with nodes as keys and positions as values
edge_attr : dict of dict
A dict with attribute names as keys and dicts as values. Value dicts
consist of tuples of inflow node and target node as keys and attribute
parameters as values::
{attr_name: {(inflow, node): attr_parameter}}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use one of those for automated edge designs. Will be unpacked and
passed as kwargs to
:func:`~networkx.drawing.nx_pylab.draw_networkx_edges`.
node_attr: dict of dict
Used to acces ``node_shape`` and ``node_size`` attributes to correctly
display edges when using custom node sizes and shapes. See also
:paramref:`~draw_nodes.node_attr`.
dflts: dict
A dict with attribute names as keys and default parameters as values::
{attr_name: default}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use this dictionairy to key all the attributes you want be accessed
during drawing. The :paramref:`~draw_edges.edge_attr` will be
filled with all attributes missing and after that populated with the
:paramref:`~draw_edges.kwargs` values.
fltr: 2-tuple of str (default=('',''))
:paramref:`draw_edges.kwargs` filter strings. Kwargs having
:paramref:`draw_edges.fltr` in them will be stripped
of the filter keyword and passed on for drawing.
- :code:`fltr[0]` applied to :paramref:`~draw_edges.node_attr`.
- :code:`fltr[1]` applied to :paramref:`~draw_edges.edge_attr`.
For filtering without stripping list the resptive kwarg in
:paramref:`~draw_edges.xcptns`
Allows for wrapping the function with other drawing subutilities into
a toplevel function like :meth:`draw_graph` wihtout sacrificing the
possibility to manipulate each sublevel call via ``kwargs``.
xcptns: 2-tuple of iterable of str (default=([],[]))
2-tuple of iterable of key word arguments that won't be stripped of
the :paramref:`~draw_edges.fltr`.
- :code:`xcptns[0]` applied to :paramref:`~draw_edges.node_attr`.
- :code:`xcptns[1]` applied to :paramref:`~draw_edges.edge_attr`.
Whether a kwarg should be stripped or not, depends on the naming
convention of :func:`~networkx.drawing.nx_pylab.draw_networkx_edges`.
Make sure to checkout their documentation to get expected behaviour.
kwargs:
If you don't want to make use of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child. you can
"manually" format your edges using keyword arguments. They can either
be the same for all edges as in::
edge_width=10
Or dictionairies using edge tuples as keys and attribute parameters as
values as in::
edge_width={('node_1', 'node_2'): 10, ('node_1': 'node_3'): 3}
Edges not present as keys will be set as defined in
:paramref:`~draw_edges.dflts`. See
:func:`~networkx.drawing.nx_pylab.draw_networkx_edges` for supported
kwargs.
Return
------
edge_draws : list
list of :class:`matplotlib.patches.FancyArrowPatch` representing the
edges
Notes
-----
Current implementation needs ``node_size`` and ``node_shape`` entries
to be present in either the :paramref:`~draw_edges.edge_attr` dict or in
the :paramref:`~draw_edges.dflts` dict. (With the last beeing recommended)
Examples
--------
>>> import matplotlib.pyplot as plt
>>> import networkx as nx
>>> import tessif.visualize.nxgrph as nxv
>>> from collections import defaultdict
>>> G=nx.complete_graph(3)
>>> pos=nx.spring_layout(G)
>>> edge_attr=defaultdict(dict)
>>> node_attr=defaultdict(dict)
>>> defaults={'node_size': 3000, 'node_shape':'o',
... 'edge_width': 1, 'edge_color': 'black',
... 'edge_arrowstyle': 'simple', 'edge_arrowsize': 7,
... 'edge_vmin': 0.0, 'edge_vmax': 1.0, 'edge_cmap': plt.cm.Greys}
>>> fltr=('node_', 'edge_')
>>> xcptns=(['node_size', 'node_shape'],
... ['edge_color', 'edge_vmin', 'edge_vmax', 'edge_cmap'])
>>> edge_draws = nxv.draw_edges(G, pos, edge_attr, node_attr, defaults,
... fltr=fltr, xcptns=xcptns, edge_color='pink')
>>> print(list(map(type, edge_draws))[0])
<class 'matplotlib.collections.LineCollection'>
IGNORE:
>>> title = plt.gca().set_title('draw_edges example')
>>> plt.gca().margins(0.5, 0.5)
>>> plt.draw()
>>> plt.pause(2)
>>> plt.close('all')
IGNORE
.. image:: images/draw_edges_example.png
:align: center
:alt: draw_edges_example_image.png
"""
# Create a namedtuple 'Edge' for better maintenance
Edges = []
for edge in grph.edges:
Edges.append(nts.Edge(*edge))
# Use kfrep to replace fltr in keys and kfltr for prefiltering prefix
node_attr, node_dflts, node_kwargs = dcttools.kfrep(
dcts=dcttools.kfltr(dcts=[node_attr, dflts, kwargs], fltr=fltr[0]),
fnd=fltr[0], xcptns=xcptns[0])
# Swap keys to allow aggregation
node_attr = dcttools.kswap(node_attr)
# Aggregate everything into node_attr for drawing:
node_attr = dcttools.maggregate(
tlkys=grph.nodes, nstd_dcts=[node_attr, ], dcts=[node_dflts, ],
**node_kwargs)
# Replace fltr in keys using kfrep to prefilter all attributes:
edge_attr, edge_dflts, edge_kwargs = dcttools.kfrep(
dcts=dcttools.kfltr(dcts=[edge_attr, dflts, kwargs], fltr=fltr[1]),
fnd=fltr[1], xcptns=xcptns[1])
edge_attr = dcttools.kswap(edge_attr)
# Aggregate everything into edge_attr for drawing:
edge_attr = dcttools.maggregate(
tlkys=Edges, nstd_dcts=[edge_attr, ], dcts=[edge_dflts, ],
**edge_kwargs)
# List of matplotlib.collections.PathCollection objs representing the edges
edge_draws = []
# 3.) Iterate thorugh all edges for drawing:
for edge in Edges:
# Add node size to edge attribute dict:
# networkx 2.5 expecpts iterable of source and target node sizes ...
edge_attr[edge]['node_size'] = [
node_attr[edge.source].get('node_size', dflts['node_size']),
node_attr[edge.target].get('node_size', dflts['node_size'])]
# but you need to provide a nodelist..
# which in this case consists of source and target cause only 1 edge
# is drawn at a singular call
edge_attr[edge]['nodelist'] = [edge.source, edge.target]
# convert variable node size to default * scaling:
for i, node_size in enumerate(edge_attr[edge].copy()['node_size']):
if node_size == 'variable':
edge_attr[edge]['node_size'][i] = (
dflts['node_size'] * dflts['node_variable_size_scaling'])
edge_attr[edge]['node_shape'] = node_attr[edge.target].get(
'node_shape', dflts['node_shape'])
# draw edges
for edge in Edges:
# filter out non label kwargs:
edge_attr[edge] = {
key: value for key, value in edge_attr[edge].copy().items()
if key in convs.nx_label_kwargs['edges']}
edge_draws.append(nx.draw_networkx_edges(
grph, pos, edgelist=[edge], **edge_attr[edge]))
return edge_draws
[docs]@log.timings
def drawing_edge_labels(grph, pos, edge_attr, dflts,
fltr='', xcptns=[], **kwargs):
r"""
Draw a Graph's edge labels using
:func:`~networkx.drawing.nx_pylab.draw_networkx_edge_labels`.
Convenience wrapper for tweaking edge label appearances. Designed to be
called by :func:`draw_graph`. Nonetheless usable as standalone drawing
utility.
Parameters
----------
grph : :class:`networkx.Graph` like
The graph object to be drawn.
pos : dict
A dict with nodes as keys and positions as values
edge_attr : dict of dict
A dict with attribute names as keys and dicts as values. Value dicts
consist of tuples of inflow node and target node as keys and attribute
parameters as values::
{attr_name: {(inflow, node): attr_parameter}}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use one of those for automated edge label designs. Will be unpacked and
passed as kwargs to
:func:`~networkx.drawing.nx_pylab.draw_networkx_edge_labels`.
dflts: dict
A dict with attribute names as keys and default parameters as values::
{attr_name: default}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use this dictionairy to key all the attributes you want be accessed
during drawing. The :paramref:`~drawing_edge_labels.edge_attr` will be
filled with all attributes missing and after that populated with the
:paramref:`~drawing_edge_labels.kwargs` values.
fltr: str (default='')
:paramref:`~drawing_edge_labels.kwargs` filter strings. Kwargs having
:paramref:`~drawing_edge_labels.fltr` in them will be stripped
of the filter keyword and passed on for drawing.
For filtering without stripping list the respective kwarg in
:paramref:`~drawing_edge_labels.xcptns`
Allows for wrapping the function with other drawing subutilities into
a toplevel function like :meth:`draw_graph` wihtout sacrificing the
possibility to manipulate each sublevel call via ``kwargs``.
xcptns: sequence of str (default=[])
Sequence of key word arguments that won't be stripped of the
:paramref:`~drawing_edge_labels.fltr`.
Whether a kwarg should be stripped or not, depends on the naming
convention of
:func:`~networkx.drawing.nx_pylab.draw_networkx_edge_labels`.
Make sure to checkout their documentation to get expected behaviour.
kwargs:
If you don't want to make use of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child. you can
"manually" format your edges using keyword arguments. They can either
be the same
for all edges as in::
edge_font_color = 'black'
Or dictionairies using tuples of inflow node and target node as keys
and attribute parameters as values as in::
edge_font_color = {('n1', 'n2'): 'red', ('n1', 'n3'): 'blue'}
Edges not present as keys will be set as defined in
:paramref:`~drawing_edge_labels.dflts`.
See :func:`~networkx.drawing.nx_pylab.draw_networkx_edge_labels` for
supported kwargs.
Return
------
edge_label_draws : dict
:class:`matplotlib.text.Text` objects representing the drawn edge
labels keyed by edges as in:
``{('inflow_node', 'target_node'): Text('drawn_label')}``
Notes
-----
Current (August 2019) implementation of
:func:`~networkx.drawing.nx_pylab.draw_networkx_edge_labels` does not need
any :paramref:`~drawing_edge_labels.dflts` and only
``edge_labels`` to be defined in :paramref:`~drawing_edge_labels.xcptns`.
Examples
--------
Drawing edge labels. (Cryptic :func:`print` is to demonstrate return
type):
>>> import matplotlib.pyplot as plt
>>> import networkx as nx
>>> import tessif.visualize.nxgrph as nxv
>>> from collections import defaultdict
>>> G=nx.complete_graph(3)
>>> pos=nx.spring_layout(G)
>>> edge_attr=defaultdict(dict)
>>> defaults={'edge_labels': 'default'}
>>> fltr='edge_'
>>> xcptns=['edge_labels']
>>> edge_label_draws = nxv.drawing_edge_labels(
... G, pos, edge_attr, defaults,
... fltr=fltr, xcptns=xcptns, edge_font_color='red',
... edge_labels={(0,1): {(0, 1): 'label'}})
>>> print(
... list(k for k in edge_label_draws)[0], ':',
... list(map(type, (v for v in edge_label_draws.values())))[0])
(0, 1) : <class 'matplotlib.text.Text'>
IGNORE:
>>> title = plt.gca().set_title('drawing_edge_labels example')
>>> plt.gca().margins(20, 20)
>>> plt.draw()
>>> plt.pause(2)
>>> plt.close('all')
IGNORE
.. image:: images/drawing_edge_labels_example.png
:align: center
:alt: drawing_edge_labels_example_image.png
"""
# Create a namedtuple 'Edge' for better maintenance
Edges = []
for edge in grph.edges:
Edges.append(nts.Edge(*edge))
# Use kfrep to replace fltr in keys and kfltr for prefiltering prefix
edge_attr, edge_dflts, edge_kwargs = dcttools.kfrep(
dcts=dcttools.kfltr(dcts=[edge_attr, dflts, kwargs], fltr=fltr),
fnd=fltr, xcptns=xcptns)
# Swap keys to allow aggregation
edge_attr = dcttools.kswap(edge_attr)
# Aggregate everything into edge_attr for drawing:
edge_attr = dcttools.maggregate(
tlkys=Edges, nstd_dcts=[edge_attr, ], dcts=[edge_dflts, ],
**edge_kwargs)
# manually fix edge_labels because netwrokx expects some wierd double keyed
# behaviour
for edge, attributes in edge_attr.items():
if attributes['edge_labels'] == dflts['edge_labels']:
edge_attr[edge]['edge_labels'] = {edge: dflts['edge_labels']}
# Text objects containing the label keyed as edge tuples:
edge_label_draws = {}
# Iterate thorugh all edges for drawing:
for edge in Edges:
# manually fix font_alpha (node_font_alpha before aggregating) to alpha
# make sure it's set to 1 if neither defined in node_attr nor dflts
edge_attr[edge]['alpha'] = edge_attr[edge].pop(
'font_alpha', dflts.get('edge_font_alpha', 1.0))
# filter out non label kwargs:
edge_attr[edge] = {key: value for key, value in edge_attr[edge].copy().items()
if key in convs.nx_label_kwargs['edge_labels']}
# do the actual drawing
edge_label_draws.update(
nx.draw_networkx_edge_labels(grph, pos, **edge_attr[edge]))
return edge_label_draws
[docs]@log.timings
def draw_legend(grph, ax, lgnds=[], dflts={}, fltr='', xcptns=[], **kwargs):
"""Draw a legend using :func:`matplotlib.pyplot.legend`.
Convenience wrapper for customizing legend drawing.
Parameters
----------
grph : :class:`networkx.Graph` like
The graph object to be drawn.
ax: :class:`matplotlib.axes.Axes`
Axes object to draw the legend on
lgnds: Sequence of dict
Iterable of legend kwarg dictionairies as in::
[{'handles': [matplotlib_handle_objects],
'labels': ['Names'],...}]
One legend will be drawn for each dict. Designed to be
provided by an :class:`~tessif.transform.es2mapping.base.ESTransformer`
child. Allows automated legend design.
dflts: dict
A dict with attribute names as keys and default parameters as values::
{attr_name: default}
Designed to be returned by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child.
Use this dictionairy to key all the attributes you want be accessed
during legend drawing. The :paramref:`~draw_legend.lgnds` items will
be filled with all attributes missing and after that populated with the
:paramref:`~draw_legend.kwargs` values.
fltr: str (default='')
:paramref:`~draw_legend.kwargs` filter strings. Kwargs having
:paramref:`~draw_legend.fltr` in them will be stripped
of the filter keyword and passed on for drawing.
For filtering without stripping list the respective kwarg in
:paramref:`~draw_legend.xcptns`
Allows for wrapping the function with other drawing subutilities into
a toplevel function like :meth:`draw_graph` wihtout sacrificing the
possibility to manipulate each sublevel call via ``kwargs``.
xcptns: sequence of str (default=[])
Sequence of key word arguments that won't be stripped of the
:paramref:`~draw_legend.fltr`.
Whether a kwarg should be stripped or not, depends on the naming
convention of :func:`matplotlib.pyplot.legend`
Make sure to checkout their documentation for getting expected
behaviour.
kwargs:
If you don't want to make use of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child. you can
"manually" format your legends using keyword arguments as in::
numpoints = 10
Parameters not present as keys will be set as defined in
:paramref:`~draw_legend.dflts` or :func:`matplotlib.pyplot.legend`
provided defaults.
See :func:`matplotlib.pyplot.legend` for supported kwargs.
Return
------
legends : list
List of :class:`matplotlib.legend.Legend` that were drawn.
Notes
-----
Make sure to provide handles and labels, or otherwise there will be no
legend.
"""
# Adjust legend location:
# ... get bounding box of current plot:
bbox = ax.get_position()
# ... shrink bounding box to 80% of original width
ax.set_position([bbox.x0, bbox.y0, bbox.width*0.8, bbox.height])
# List of legend objects for returning
legends = []
# iterate through all lgnds
for legend_kwargs in lgnds:
# Use kfrep to replace fltr in keys and kfltr for prefiltering prefix
legend_dflts, legend_kwargs, provided_kwargs = dcttools.kfrep(
dcts=dcttools.kfltr(dcts=[dflts, legend_kwargs, kwargs], fltr=fltr),
fnd=fltr, xcptns=xcptns)
legend_attr = dcttools.flaggregate(
dcts=[legend_dflts, legend_kwargs, provided_kwargs])
# Create the legend object
lgnd = ax.legend(**legend_attr)
# Pass it to the artist for drawing
ax.add_artist(lgnd)
# add it to the list of legends for returning:
legends.append(lgnd)
return legends
[docs]@log.timings
def draw_graph(
grph, formatier=None, layout='dot',
draw_node_labels=True, draw_fency_nodes=False,
draw_edge_labels=False, legends=None,
defaults=convs.nxgrph_visualize_defaults,
tags=convs.nxgrph_visualize_tags,
exceptions=convs.nxgrph_visualize_xcptns, **kwargs):
r"""
Draw a fully customizable energy system graph.
For customizing use automated visual parameter dicts generated
by something like :class:`~tessif.transform.es2mapping.base.ESTransformer`
or tweak individual parameters using :paramref:`draw_graph.kwargs`.
Parameters
----------
grph : :class:`networkx.Graph`-like
The graph object to be drawn.
formatier: :class:`~tessif.transform.es2mapping.base.ESTransformer`-like,
Object designed for automating energy system graph design. A collection
of graph styling attributes packed inside dictionairies matching the
networkx drawing interface demands.
default=None
layout: str, default='dot'
String to define the layout used by
:func:`networkx.drawing.nx_pydot.graphviz_layout`
See the `doc page <https://graphviz.gitlab.io/documentation/>`_ and the
`documentation <https://graphviz.gitlab.io/_pages/pdf/dot.1.pdf>`_ for
more details on graph layouts.
draw_node_labels : bool, default=True
Draw node labels if True.
See also :func:`drawing_node_labels`
draw_fency_nodes : bool ,default=False
Draw visually verbose nodes if True.
See also :paramref:`draw_nodes.draw_fency_nodes`
draw_edge_labels : bool, default=False
Draw edge labels if True.
See also :func:`drawing_edge_labels`
legends: :class:`~collections.abc.Sequence` of dict, default=None
Attempt legend drawing If ``not None``.
See :paramref:`draw_legend.lgnds` for details.
defaults: dict
Dicts providing visual attribute defaults used when
:paramref:`~draw_graph.formatier` does not provide one.
default = :attr:`~tessif.frused.conventions.nxgrph_visualize_defaults`
tags: tuple
Tuple of strings used to filter out :paramref:`~draw_graph.kwargs`.
Field number 0/1 will be interpreted as node/edge tags respectively.
default = :attr:`~tessif.frused.conventions.nxgrph_visualize_tags`
exceptions: tuple
Tuple of :class:`~collections.abc.Sequence` objects containing
attrbiute keys not to be stripped of :paramref:`draw_graph.tags`.
Field number 0/1 will be interpreted as node/edge xctpns respectively.
default = :attr:`~tessif.frused.conventions.nxgrph_visualize_xcptns`
title: str
Title to use for the plot.
kwargs : key word arguments
Unfortunately networkx does not use consistant naming convention
when using their kwargs. Edge kwargs are mostly prefixed with
``edge_`` but not all of them
(i.e. :func:`~networkx.drawing.nx_pylab.draw_networkx_edge_labels`).
Hence :mod:`tessif.visualize` allows prefixing (or tagging in
general) ALL keywords. The current tags used to seperate the kwargs
are: :attr:`~tessif.frused.conventions.nxgrph_visualize_tags`
The tags will be stripped of the attribute when passed as unpacked
dict to the respective networkx drawing utility. This is of course very
susceptible to changes. Let's hope an appropriate deprecation warning
will be thrown when they decide to change that. Simply adding the
respective entry to :paramref:`draw_graph.exceptions` will hopefully
ensure future compatibility.
For a list of sensible kwargs see also:
- :func:`networkx.drawing.nx_pylab.draw_networkx`
- :paramref:`draw_nodes.kwargs`
- :paramref:`drawing_node_labels.kwargs`
- :paramref:`draw_edges.kwargs`
- :paramref:`drawing_edge_labels.kwargs`
- :paramref:`draw_legend.kwargs`
Return
------
graphdraw : dict
dict containing the drawn data keyed by its elements as in::
{'nodes': draw_nodes(),
'node_lables': drawing_node_labels(),
'edges': draw_edges(),
'edge_labels': drawing_edge_labels(),
'legends': draw_legend()}
Examples
--------
Using :attr:`tessif.frused.defaults.nxgrph_visualize_defaults`,
:attr:`tessif.frused.defaults.nxgrph_visualize_tags` and
:attr:`tessif.frused.defaults.nxgrph_visualize_xcptns` for default
drawing.
>>> import matplotlib.pyplot as plt
>>> import networkx as nx
>>> import tessif.visualize.nxgrph as nxv
>>> G=nx.complete_graph(3)
>>> graphdraw = nxv.draw_graph(G, node_color='pink', edge_color='red')
>>> for key, value in graphdraw.items():
... print(key, ':', type(value[0]))
nodes : <class 'matplotlib.collections.PathCollection'>
node_uids : <class 'matplotlib.text.Text'>
edges : <class 'matplotlib.collections.LineCollection'>
IGNORE:
>>> title = plt.gca().set_title('draw_graph example')
>>> plt.draw()
>>> plt.pause(2)
>>> plt.close('all')
IGNORE
.. image:: images/draw_graph_example.png
:align: center
:alt: draw_graph_example_image.png
"""
# Create an empty dict to store the drawing elements for returning
graphdraw = {}
# Create an empty canvas to draw on:
f, ax = plt.subplots()
# Switch of axix plotting
ax.set_axis_off()
# Define draw_graph defaults which are to be used when no formatter was
# passed
if formatier:
# Overwrite defaults with possible formatier.defaults
defaults = dict(defaults, **formatier.defaults)
# Generate layout
pos = nx.drawing.nx_agraph.graphviz_layout(
grph, prog=layout,
)
# args='-Gsplines=true -Granksep=10 -Goverlap=scalexy')
# Visualize nodes...
# ... generate a node_attr dictionairy: {node: {attr: value}}...
# ... Using formatier as base if given:
if formatier:
node_attr = formatier.node_data()
# Use empty defaultdict if not..:
else:
# ... to enable aggregate algorithm inside the draw_* functions
node_attr = defaultdict(dict)
# Draw nodes:
graphdraw['nodes'] = draw_nodes(grph, pos, node_attr, defaults,
draw_fency_nodes, fltr=tags[0],
xcptns=exceptions[0], **kwargs)
# Draw node labels:
if draw_node_labels:
graphdraw['node_uids'] = drawing_node_labels(
grph, pos, node_attr, defaults,
fltr=tags[0], xcptns=[], **kwargs)
# Visualize edges...
# ... Generate a edge_attr dictionairy: {Edge: {attr: vaue}}...
# ... Use formatier as base if given:
if formatier:
edge_attr = formatier.edge_data()
# Use empty defaultdict if not:
else:
edge_attr = defaultdict(dict)
# Draw edges:
graphdraw['edges'] = draw_edges(
grph, pos, edge_attr, node_attr, defaults,
fltr=(tags[0], tags[1]), xcptns=(exceptions[0], exceptions[1]),
**kwargs)
# Draw edge lables:
if draw_edge_labels:
graphdraw['edge_labels'] = drawing_edge_labels(
grph, pos, edge_attr, defaults,
fltr=tags[1], xcptns=exceptions[1], **kwargs)
# Draw legend:
if legends:
graphdraw['legends'] = draw_legend(
grph, ax, legends, defaults, fltr=tags[2], **kwargs)
if kwargs.get('title', None) is not None:
ax.set_title(kwargs.get('title'))
return graphdraw
[docs]@log.timings
def draw_graphical_representation(
formatier, colored_by='name', shift_colors=False):
r"""
Example for a visually enhanced energy graph drawing.
Mainly designed as reference on how to exploit the dictionairy aggregating
capabilities of :func:`draw_graph` and the convenience of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child when trying
to use :func:`~networkx.drawing.nx_pylab.draw` like functions of networkx.
Creates a plain :class:`networkx.DiGraph` object only consisting of nodes
and edges found in :paramref:`~draw_graphical_representation.formatier`.
This plain graph is then provided to :func:`draw_graph` with the
:paramref:`~draw_graphical_representation.formatier` providing
manually stated key word arguments.
This is of course not the design case, because this behaviour could be
easily archived only using :func:`draw_graph` while providing an
:paramref:`~draw_graph.formatier` returning the desired formats when
accessed for
:attr:`~tessif.transform.es2mapping.base.ESTransformer.node_data`
and :attr:`~tessif.transform.es2mapping.base.ESTransformer.edge_data`.
This however assumes we are commited enough to write our own
:class:`~tessif.transform.es2mapping.base.ESTransformer` instead of simply
tweaking the hell out of the ones already hard coded via simple keyword
arguments.
Parameters
----------
formatier : :class:`~tessif.transform.es2mapping.base.ESTransformer`
Energy system to dictionairy transformer object returning its data
as a 2 layer nested dict in the form of
``{attribute: {node/edge: parameter}}`` if accessed for
``node_data``/``edge_data`` respectively. As well es a default
dictionairy for node and edge attributes.
colored_by : {'name', 'carrier', 'sector'}, optional
Specification on how to group nodes for coloring.
(Respective node color dict provided by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child)
Default implementations are:
- 'component': Searches for keywords in node.label.component
- 'label': Searches for keywords in str(node.label)
- 'carrier': Searches for keywords in node.label.carrier
- 'sector': Searches for keywords in node.label.sector
(Refer to :attr:`~tessif.frused.namedtuples.NodeColorGroupings` for
namedtuple implementation)
Note
----
To try out tweaking things your own way, create a custom Formatier
class inheriting
:class:`~tessif.transform.es2mapping.base.ESTransformer` and overwrite
``_map_node_colors()`` for additional coloring logic.
shift_colors : bool, default=False
Use a colormap cycle for color groupings if ``True``. Otherwise all
nodes of one group are colored the same.
(Respective node color map dict provided by a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child)
Return
------
networkx.DiGraph
Graph object visualized.
Example
-------
Setting :attr:`spellings.get_from's <tessif.frused.spellings.get_from>`
logging level to debug for decluttering doctest output:
>>> from tessif.frused import configurations
>>> configurations.spellings_logging_level = 'debug'
Simulate the omeof standard energy system and draw it:
>>> from tessif.frused.paths import example_dir
>>> from tessif.transform.es2mapping import omf as tomf
>>> from tessif.transform import nxgrph as nxt
>>> from tessif.visualize import nxgrph as nxv
>>> import os
>>> from tessif import simulate
>>> from tessif import parse
>>> es = simulate.omf(
... path=os.path.join(example_dir, 'data', 'omf',
... 'xlsx', 'energy_system.xlsx'),
... parser=parse.xl_like)
>>> formatier = tomf.AllFormatier(es, cgrp='all')
>>> grph = nxt.Graph(tomf.FlowResultier(es))
>>> for key, value in formatier.edge_data()['edge_width'].items():
... formatier.edge_data()['edge_width'][key] = 4 * value
>>> nxv.draw_graphical_representation(
... formatier=formatier, colored_by='name')
IGNORE:
>>> title = plt.gca().set_title('graphical_representation example')
>>> plt.draw()
>>> plt.pause(10)
>>> plt.close('all')
IGNORE
.. image:: images/graphical_representation_example.png
:align: center
:alt: alternate text
"""
# Alias the energy system transformer serving as formatter
fmt = formatier
# Create a dummy graph only containing nodes and edges
grph = nx.DiGraph()
grph.add_nodes_from(fmt.nodes)
grph.add_edges_from(fmt.edges)
# 3.) draw the esgraph:
draw_graph(
grph,
layout='dot',
draw_node_labels=False,
draw_fency_nodes=True,
draw_edge_labels=False,
node_size=fmt.node_size,
node_fill_size=fmt.node_fill_size,
node_shape=fmt.node_shape,
node_color=getattr(fmt.node_color_maps, colored_by)
if shift_colors else getattr(fmt.node_color, colored_by),
edge_width=fmt.edge_width,
edge_color=fmt.edge_color,
legends=[fmt.node_style_legend,
getattr(fmt.node_legend, colored_by),
fmt.edge_style_legend]
if not colored_by == 'name' else [getattr(
fmt.node_legend, colored_by), fmt.edge_style_legend],
)
[docs]@log.timings
def draw_numerical_representation(
formatier, colored_by='name', shift_colors=False):
"""
Examplary code for a numerically enhanced energy graph drawing.
Mainly designed as reference on how to exploit the dictionairy aggregating
capabilities of :func:`draw_graph` and the convenience of a
:class:`~tessif.transform.es2mapping.base.ESTransformer` child when trying
to use the :func:`~networkx.drawing.nx_pylab.draw` like functions of
networkx.
Creates a plain :class:`networkx.DiGraph` object only consisting of nodes
and edges found in
:paramref:`~draw_numerical_representation.formatier`.
This plain graph is then provided to :func:`draw_graph` with the
:paramref:`~draw_numerical_representation.formatier` providing
manually stated key word arguments.
This is of course not the design case, because this behaviour could be
easily archived only using :func:`draw_graph` while providing an
:paramref:`~draw_graph.formatier` returning the desired formats when
accessed for
:attr:`~tessif.transform.es2mapping.base.ESTransformer.node_data`
and :attr:`~tessif.transform.es2mapping.base.ESTransformer.edge_data`.
This however assumes we are commited enough to write our own
:class:`~tessif.transform.es2mapping.base.ESTransformer` instead of simply
tweaking the hell out of the ones already hard coded via simple keyword
arguments.
Parameters
----------
formatier : :class:`~tessif.transform.es2mapping.base.ESTransformer`
Energy system to dictionairy transformer object returning its data
as a 2 layer nested dict in the form of
``{attribute: {node/edge: parameter}}`` if accessed for
``node_data``/``edge_data`` respectively. As well es a default
dictionairy for node and edge attributes.
colored_by : {'name', 'carrier', 'sector'}, optional
Specification on how to group nodes for coloring.
(Respective node color dict provided by an
:class:`~tessif.transform.es2mapping.base.ESTransformer` child)
Default implementations are:
- 'name': Searches for keywords in str(node.label)
- 'carrier': Searches for keywords in node.label.carrier
- 'sector': Searches for keywords in node.label.sector
(Refer to :attr:`~tessif.frused.namedtuples.NodeColorGroupings` for
namedtuple implementation)
Note
----
To try out tweaking things your own way, create a custom Formatier
class inheriting
:class:`~tessif.transform.es2mapping.base.ESTransformer` and overwrite
``_map_node_colors()`` for additional coloring logic.
shift_colors : bool, default=False
Use a colormap cycle for color groupings if ``True``. Otherwise all
nodes of one group are colored the same.
(Respective node color map dict provided by an
:class:`~tessif.transform.es2mapping.base.ESTransformer` child)
Example
-------
Setting :attr:`spellings.get_from's <tessif.frused.spellings.get_from>`
logging level to debug for decluttering doctest output:
>>> from tessif.frused import configurations
>>> configurations.spellings_logging_level = 'debug'
Simulate the omeof standard energy system and draw it:
>>> from tessif.frused.paths import example_dir
>>> from tessif.transform.es2mapping import omf as tomf
>>> from tessif.transform import nxgrph as nxt
>>> from tessif.visualize import nxgrph as nxv
>>> import os
>>> from tessif import parse, simulate
>>> es = simulate.omf(
... path=os.path.join(example_dir, 'data', 'omf',
... 'xlsx', 'energy_system.xlsx'),
... parser=parse.xl_like)
>>> formatier = tomf.AllFormatier(es, cgrp='all')
>>> grph = nxt.Graph(tomf.FlowResultier(es))
>>> nxv.draw_numerical_representation(
... formatier=formatier, colored_by='sector')
IGNORE:
>>> title = plt.gca().set_title('numerical_representation example')
>>> plt.draw()
>>> plt.pause(10)
>>> plt.close('all')
IGNORE
.. image:: images/numerical_representation_example.png
:align: center
:alt: alternate text
"""
# Alias the energy system transformer serving as formatter
fmt = formatier
# Create a dummy graph only containing nodes and edges
grph = nx.DiGraph()
grph.add_nodes_from(fmt.nodes)
grph.add_edges_from(fmt.edges)
# Draw the esgraph:
draw_graph(
grph,
layout='sfdp',
draw_node_labels=True,
draw_fency_nodes=False,
draw_edge_labels=True,
node_uids=fmt.node_summaries,
node_size=3000,
node_color=getattr(fmt.node_color_maps, colored_by)
if shift_colors else getattr(fmt.node_color, colored_by),
edge_labels=fmt.edge_summaries,
legends=[getattr(fmt.node_legend, colored_by)],
)