Intro to multiple plots
In this notebook you will learn what multiple plots can do for you and what is the best way to create them.
Multiple better than individual
The power of multiple plots is threefold:
Multiple plot is just a coordinator. It doesn’t hold any data related to it’s childplots, which are full instances of
Plot
themselves. This allows you to grab the separate child plots whenever you want, as well as modifying only some of its child plots very easily.It will create the layout for you without requiring any effort on your side.
If all your plots inside a multiple plot need to read from the same data, the plot will coordinate all plots so that they can share the data. Therefore, data will be read only once, saving both time and memory.
Types of multiple plots
There are three ways of combining your plots in the visualization framework, each with its associated class:
In the same plot (
MultiplePlot
): it’s the most basic one. It just takes the traces from all plots and displays them in the same plot.As subplots (
SubPlots
): Creates a grid of subplots, where each item of the grid contains a plot. Uses plotly’s subplots capabilities.As frames of an animation (
Animation
): Creates an animation where each child plot is represented in a frame. plotly’s animation capabilities.
[1]:
from sisl.viz import MultiplePlot, Animation, SubPlots
Let’s create a simple tight-binding model for the plots in this notebook.
[2]:
import sisl
import numpy as np
r = np.linspace(0, 3.5, 50)
f = np.exp(-r)
orb = sisl.AtomicOrbital('2pzZ', (r, f))
geom = sisl.geom.graphene(orthogonal=True, atoms=sisl.Atom(6, orb))
geom = geom.move([0, 0, 5])
H = sisl.Hamiltonian(geom)
H.construct([(0.1, 1.44), (0, -2.7)], )
Merging existing plots
This is the most simple way of creating a multiple plot: you just build your plots, and then pass them to the multiple plot constructor.
However, this will miss one key feature of multiple plots. Since you’ve created each plot separately, each plot has its own data, even if they would be able to share it.
Therefore, this is only recommended when the plots are independent from each other.
As an example, from the hamiltonian that we constructed, let’s build a wavefunction plot and a pdos plot:
[3]:
wf_plot = H.plot.wavefunction(i=1, axes="xy", transforms=["square"], zsmooth="best")
pdos_plot = H.plot.pdos(Erange=[-10,10])
plots = [wf_plot, pdos_plot]
And now, we will merge them. There are two main ways to do the merge:
Calling the multiple plot class that we want to use (
MultiplePlot
,Animation
orSubplots
):
[4]:
# You just pass the plots and then any extra arguments for the plot class (see help(SubPlots))
SubPlots(plots=plots, cols=2)
Using the
merge
method that all plots have.
[5]:
plots[0].merge(plots[1:], to="subplots", cols=2)
Both are exactly equivalent, but this second one is probably better since you don’t need to import the class.
You do need to specify somehow how to merge the plots though! As you may have noticed, there’s a to
argument that lets you specify how you want the plots to be merged.
Here are the docs for Plot.merge
:
[6]:
help(plots[0].merge)
Help on method merge in module sisl.viz.plot:
merge(others, to='multiple', extend_multiples=True, **kwargs) method of sisl.viz.plots.grid.WavefunctionPlot instance
Merges this plot's instance with the list of plots provided
Parameters
-------
others: array-like of Plot() or Plot()
the plots that we want to merge with this plot instance.
to: {"multiple", "subplots", "animation"}, optional
the merge method. Each option results in a different way of putting all the plots
together:
- "multiple": All plots are shown in the same canvas at the same time. Useful for direct
comparison.
- "subplots": The layout is divided in different subplots.
- "animation": Each plot is converted into the frame of an animation.
extend_multiples: boolean, optional
if True, if `MultiplePlot`s are passed, they are splitted into their children, so that the result
is the merge of its children with the rest.
If False, a `MultiplePlot` is treated as a solid unit.
kwargs:
extra arguments that are directly passed to `MultiplePlot`, `Subplots`
or `Animation` initialization.
Returns
-------
MultiplePlot, Subplots or Animation
depending on the value of the `to` parameter.
Let MultiplePlot
handle plot creation
As already mentioned, creating your plots beforehand is only a good strategy if plots are independent from each other, in the sense that they can not share data.
In cases where plots can share data, just let MultiplePlot
create your plots. It is so easy that you will end up doing it in this way all the time, even in cases where it doesn’t have efficiency benefits :)
Everytime you create a plot, there are three special keyword arguments that you can pass: varying
, animate
and subplots
. These keywords let you easily initialize MultiplePlot
, Animation
and Subplots
instances, respectively.
They can be used in two ways:
You can pass a dictionary with the keys of the settings that you want to vary and the value for each “step”.
[7]:
H.plot.wavefunction(axes="xy", transforms=["square"], animate={"i":[1,2], "zsmooth": ["best", False]})
In this case we animated the wavefunction plot to see the squares of wavefunctions 1 and 2. The second one, for some reason, we wanted it to display “a bit” pixelated.
You can also pass the list of values as regular settings and then inform the multiple plot keyword (in this case
subplots
) which settings to vary.
[8]:
H.plot.wavefunction(
colorscale=["temps", "portland", "peach", "viridis"], i=[0,1,2,3], axes="xy", transforms=["square"], zsmooth="best",
subplots=["colorscale", "i"], rows=2
)
There you go, four wavefunctions, each one displayed in a different colorscale :)
Remember that these subplots are all sharing the same data, so the eigenstates of the hamiltonian have only been stored once!
This next cell is just to create a thumbnail
[9]:
_.show("png")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Input In [9], in <cell line: 1>()
----> 1 _.show("png")
File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/plot.py:1176, in Plot.show(self, listen, return_figWidget, *args, **kwargs)
1173 except Exception as e:
1174 warn(e)
-> 1176 return self._backend.show(*args, **kwargs)
File ~/checkouts/readthedocs.org/user_builds/sisl/checkouts/v0.12.2/sisl/viz/backends/plotly/backend.py:42, in PlotlyBackend.show(self, *args, **kwargs)
41 def show(self, *args, **kwargs):
---> 42 return self.figure.show(*args, **kwargs)
File ~/checkouts/readthedocs.org/user_builds/sisl/conda/v0.12.2/lib/python3.10/site-packages/plotly/basedatatypes.py:3398, in BaseFigure.show(self, *args, **kwargs)
3365 """
3366 Show a figure using either the default renderer(s) or the renderer(s)
3367 specified by the renderer argument
(...)
3394 None
3395 """
3396 import plotly.io as pio
-> 3398 return pio.show(self, *args, **kwargs)
File ~/checkouts/readthedocs.org/user_builds/sisl/conda/v0.12.2/lib/python3.10/site-packages/plotly/io/_renderers.py:388, in show(fig, renderer, validate, **kwargs)
385 fig_dict = validate_coerce_fig_to_dict(fig, validate)
387 # Mimetype renderers
--> 388 bundle = renderers._build_mime_bundle(fig_dict, renderers_string=renderer, **kwargs)
389 if bundle:
390 if not ipython_display:
File ~/checkouts/readthedocs.org/user_builds/sisl/conda/v0.12.2/lib/python3.10/site-packages/plotly/io/_renderers.py:296, in RenderersConfig._build_mime_bundle(self, fig_dict, renderers_string, **kwargs)
293 if hasattr(renderer, k):
294 setattr(renderer, k, v)
--> 296 bundle.update(renderer.to_mimebundle(fig_dict))
298 return bundle
File ~/checkouts/readthedocs.org/user_builds/sisl/conda/v0.12.2/lib/python3.10/site-packages/plotly/io/_base_renderers.py:127, in ImageRenderer.to_mimebundle(self, fig_dict)
126 def to_mimebundle(self, fig_dict):
--> 127 image_bytes = to_image(
128 fig_dict,
129 format=self.format,
130 width=self.width,
131 height=self.height,
132 scale=self.scale,
133 validate=False,
134 engine=self.engine,
135 )
137 if self.b64_encode:
138 image_str = base64.b64encode(image_bytes).decode("utf8")
File ~/checkouts/readthedocs.org/user_builds/sisl/conda/v0.12.2/lib/python3.10/site-packages/plotly/io/_kaleido.py:133, in to_image(fig, format, width, height, scale, validate, engine)
131 # Raise informative error message if Kaleido is not installed
132 if scope is None:
--> 133 raise ValueError(
134 """
135 Image export using the "kaleido" engine requires the kaleido package,
136 which can be installed using pip:
137 $ pip install -U kaleido
138 """
139 )
141 # Validate figure
142 # ---------------
143 fig_dict = validate_coerce_fig_to_dict(fig, validate)
ValueError:
Image export using the "kaleido" engine requires the kaleido package,
which can be installed using pip:
$ pip install -U kaleido