1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
.. _matplotlib_sec:
Integration with matplotlib
===========================
The drawing capabilities of ``graph-tool`` (see :mod:`~graph_tool.draw`
module) can be integrated with `matplotlib <https://matplotlib.org>`_,
as we demonstrate in the following.
.. note::
:class: margin
Integration with :mod:`matplotlib` works with every backend, but `vector
drawing <https://en.wikipedia.org/wiki/Vector_graphics>`_ only works with a
`cairo <https://cairographics.org>`_-based backend (e.g. ``cairo`` or
``GTK3Cairo``). The backend can be changed by calling
:func:`matplotlib.pyplot.switch_backend`:
.. code::
import matplotlib.pyplot as plt
plt.switch_backend("cairo")
When using a backend not based on cairo, `rasterization
<https://en.wikipedia.org/wiki/Raster_graphics>`_ will be used instead. In
this case, the resolution can be controlled via the ``dpi`` parameter of
:class:`matplotlib.figure.Figure`.
Drawing with matplotlib is done by calling :func:`~graph_tool.draw.graph_draw`
and passing a container (e.g. :class:`matplotlib.axes.Axes`) as the ``mplfig``
parameter. When this option is passed, the function will return a
:func:`~graph_tool.draw.GraphArtist` object that has been added to the figure.
.. tip::
:class: margin
`Axis autoscaling
<https://matplotlib.org/stable/users/explain/axes/autoscale.html>`_ will work
as expected with :func:`~graph_tool.draw.GraphArtist`, but the aspect ratio
will be set the by the figure axis shape, rather than the node positions themselves.
In order to restore the aspect ratio independently of the axis shape,
:func:`~graph_tool.draw.GraphArtist` offers a
:meth:`~graph_tool.draw.GraphArtist.fit_view` method that does this automatically.
Furthermore, when calling :func:`~graph_tool.draw.graph_draw` without
integrating with matplotlib, the node positions correspond to cairo
coordinates, which have an origin in the upper left corner, and with the y
axis increasing from top to bottom.
In order for the visualization to be the same when matplotlib is being used,
the y axis needs to be flipped by inverting the limits with
:meth:`matplotlib.axes.Axes.set_ylim`.
Alternatively, the option ``yflip`` can be passed to
:meth:`~graph_tool.draw.GraphArtist.fit_view` for this to be done
automatically.
The example below shows how to plot several graphs in different subplots of the
same figure.
.. testsetup::
import matplotlib
backend = matplotlib.get_backend()
gt.seed_rng(44)
.. testcode::
import graph_tool.all as gt
import matplotlib.pyplot as plt
plt.switch_backend("cairo") # to enable vector drawing
fig, ax = plt.subplots(2, 2, figsize=(12, 11.5))
g = gt.collection.data["polbooks"]
a = gt.graph_draw(g, g.vp.pos, vertex_size=1.5, mplfig=ax[0,0])
a.fit_view(yflip=True)
ax[0,0].set_xlabel("$x$ coordinate")
ax[0,0].set_ylabel("$y$ coordinate")
state = gt.minimize_nested_blockmodel_dl(g)
a = state.draw(mplfig=ax[0,1])[0]
a.fit_view(yflip=True)
ax[0,1].set_xlabel("$x$ coordinate")
ax[0,1].set_ylabel("$y$ coordinate")
g = gt.collection.data["lesmis"]
a = gt.graph_draw(g, g.vp.pos, vertex_size=1.5, mplfig=ax[1,0])
a.fit_view(yflip=True)
ax[1,0].set_xlabel("$x$ coordinate")
ax[1,0].set_ylabel("$y$ coordinate")
state = gt.minimize_nested_blockmodel_dl(g)
a = state.draw(mplfig=ax[1,1])[0]
a.fit_view(yflip=True)
ax[1,1].set_xlabel("$x$ coordinate")
ax[1,1].set_ylabel("$y$ coordinate")
plt.subplots_adjust(left=0.08, right=0.99, top=0.99, bottom=0.06)
fig.savefig("gt-mpl.svg")
.. testcleanup::
plt.switch_backend(backend)
.. figure:: gt-mpl.svg
:width: 90%
Four subplots showing networks drawn using graph-tool.
Integration with :mod:`~mpl_toolkits.basemap`
+++++++++++++++++++++++++++++++++++++++++++++
As a slightly more elaborate example, below we show how we can draw the
:ns:`European airline <eu_airlines>` graph on a map using
:mod:`mpl_toolkits.basemap`.
.. testcode::
from itertools import chain
from mpl_toolkits.basemap import Basemap
fig, ax = plt.subplots(1, 1, figsize=(8, 8))
g = gt.collection.ns["eu_airlines"]
pos = gt.group_vector_property([g.vp.nodeLong, g.vp.nodeLat])
m = Basemap(projection='ortho', resolution=None,
lat_0=g.vp.nodeLat.fa.mean(), lon_0=g.vp.nodeLong.fa.mean())
m.shadedrelief(scale=.2)
lats = m.drawparallels(np.linspace(-90, 90, 13))
lons = m.drawmeridians(np.linspace(-180, 180, 13))
lat_lines = chain(*(tup[1][0] for tup in lats.items()))
lon_lines = chain(*(tup[1][0] for tup in lons.items()))
all_lines = chain(lat_lines, lon_lines)
for line in all_lines:
line.set(linestyle='-', alpha=0.3, color='w')
a = gt.graph_draw(g, pos=pos.t(lambda x: m(*x)), # project positions
edge_color=(.1,.1,.1,.1), mplfig=ax)
a.fit_view()
a.set_zorder(10)
tight_layout()
fig.savefig("gt-map.svg")
.. figure:: gt-map.svg
:width: 90%
:ns:`Network of European fligths <eu_airlines>` drawn on the globe with an
`orthographic projection
<https://en.wikipedia.org/wiki/Orthographic_projection>`_.
|