Animated plotters#
In addition to the highly customizable plotters with complex grid layout and utilities, the nested_grid_plotter library provides a set of specialized plotters. The following tutorial illustrates the possibilities for animation and built-in functions.
Reminder: all the figures can be produced without this library, with matplotlib alone.
1 (lines) and 2D (imshow) that are very frequently used in science for animation. For more complex solutions, such as the double pendulum problem, stick to the classic implementation: https://matplotlib.org/stable/gallery/animation/double_pendulum.html
The aim here is to “hide” the animation mechanics, which are quite specialized, and thus enable some people to use a simpler interface for extremely classic plots in science, i.e. animated 1D curves and 2D images.
All animations relies on precomputed list of “images” or data. So it’s not optimal in terms of memory, but that’s not what we’re aiming for here.
Configuration#
Import the required modules
[1]:
import itertools
import tempfile
from pathlib import Path
from typing import List
import matplotlib as mpl
import matplotlib.pyplot as plt
import nested_grid_plotter
import nested_grid_plotter as ngp
import numpy as np
from IPython.display import HTML
from matplotlib.animation import HTMLWriter
from matplotlib.text import Text
Check the versions being used
[2]:
print(f"matplotlib version = {mpl.__version__}")
print(f"nested_grid_plotter version = {nested_grid_plotter.__version__}")
print(f"numpy version = {np.__version__}")
matplotlib version = 3.10.1
nested_grid_plotter version = 2.0.0
numpy version = 2.3.3
Apply some basic matplotlib parameters for the figures
[3]:
# The two first lines are required to apply the changes
plt.plot()
plt.close()
new_rc_params = {
"font.size": 16,
"figure.figsize": (8, 8),
"figure.facecolor": "w",
"savefig.facecolor": "w",
"savefig.edgecolor": "k",
"savefig.dpi": 300,
"axes.titlesize": 20,
"axes.labelweight": "bold",
}
plt.rcParams.update(new_rc_params)
Make a temporary folder to save the figures
[4]:
fig_save_path = Path(tempfile.mkdtemp())
# Print the content
print(*fig_save_path.iterdir(), sep="\n") # should be empty
1D Animations#
Most of the animation mechanics are hidden, which is useful when performing the same operation again and again.
Three 1D examples (a bit modified) from the matplotlib website:
https://matplotlib.org/stable/gallery/animation/strip_chart.html
Create a simple 1D plotter with two frames
[5]:
def make_2_frames_plotter() -> ngp.AnimatedPlotter:
return ngp.AnimatedPlotter(
ngp.Figure(figsize=(10, 5), constrained_layout=True),
builder=ngp.SubplotsMosaicBuilder(
mosaic=[["ax11", "ax12"]], sharey=True, sharex=True
),
)
Create some 1D data If
xis provided, it must be for ally. Whileymust be a 2d array,xcan be 1D or 2D.
[6]:
x = np.arange(0, 2 * np.pi, 0.1)
# static
y = np.sin(x)
# animated
nb_frames = 40
y_animated = np.array([np.sin(x + i / 20) for i in range(nb_frames)]).T
# Note, the number of frames should be the second dimension
# other -> varying four times faster and with twice the amplitude
y2 = y * 2.0
y2_animated = np.array([np.sin(x + i / 10.0) * 2.0 for i in range(nb_frames)]).T
y22_animated = np.array([np.sin(x + i / 5.0) * 2.0 for i in range(nb_frames)]).T
[7]:
plotter = make_2_frames_plotter()
# Static plot on both axes
plotter.ax_dict["ax11"].plot(x, y)
plotter.ax_dict["ax12"].plot(x, y2)
# Animated plot on "ax12"
# x can be 2D but cannot vary (only the first vector given is considered)
plotter.animated_multi_plot(
ax_name="ax11",
data={
"y_animated": {
"x": np.repeat(x.reshape(-1, 1), nb_frames, axis=1),
"y": y_animated,
}
},
)
# Animated plot on "ax12"
# so x can be 1D as well -> simpler
# multiple lines can be created at once. In that case, the dimensions consistency are
# checked
plotter.animated_multi_plot(
ax_name="ax12",
data={
"y_animated2": {"x": x, "y": y2_animated},
"y_animated22": {"x": x, "y": y22_animated},
},
)
plotter.close()
plotter.animate(nb_frames=nb_frames)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("test1D.html")
writer = HTMLWriter(fps=20, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[7]:
It is possible to add a title, axis labels, parametrize the lines display, and add some text.
Possibilité de mettre des titres, des labels pour les légendes etc.
Ajout de la variable temps!
Passage des kwargs
Legend is not
“label”: “(1) violet curve”
Limitation: See Constrained layout guide: https://matplotlib.org/stable/tutorials/intermediate/constrainedlayout_guide.html?highlight=constrained
Ad explained in the legend section: Legends can be placed outside of their parent axis. Constrained-layout is designed to handle this for Axes.legend(). However, constrained-layout does not handle legends being created via Figure.legend() (yet).
[8]:
plotter = make_2_frames_plotter()
# Static plot on both axes
plotter.ax_dict["ax11"].plot(x, y)
plotter.ax_dict["ax12"].plot(x, y2)
# Animated plot on "ax12"
# x can be 2D but cannot vary (only the first vector given is considered)
plotter.animated_multi_plot(
ax_name="ax11",
data={"y_animated (violet)": {"x": x, "y": y_animated, "kwargs": {"c": "violet"}}},
xlabel="My X label",
ylabel="My Y label",
title="My awesome title",
)
# Animated plot on "ax12"
# so x can be 1D as well -> simpler
# multiple lines can be created at once. In that case, the dimensions consistency are
# checked
plotter.animated_multi_plot(
ax_name="ax12",
data={
"y_animated2 (blue)": {"x": x, "y": y2_animated},
"y_animated22 (red)": {
"x": x,
"y": y22_animated,
"kwargs": {"c": "red", "marker": "x", "linestyle": "none"},
},
},
xlabel="My other X label",
ylabel="My other Y label",
title="My second awesome title",
)
# Add a text animation
s1 = [f"frame #{i}" for i in range(nb_frames)]
s2 = [f"time = {i * 60} s" for i in range(nb_frames)]
plotter.plot_animated_text(
plotter.ax_dict["ax11"], x=0.01, y=1.8, s=s2, fontstyle="italic", color="red"
)
plotter.plot_animated_text(
plotter.ax_dict["ax12"], x=0.01, y=1.8, s=s1, fontweight="bold"
)
plotter.add_fig_legend()
plotter.close()
plotter.animate(nb_frames=nb_frames)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("test1D.html")
writer = HTMLWriter(fps=20, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[8]:
It is also possible to create custom function and to add them to the animation system. For instance, if one wants to animate the figure title. The procedure is the same for anything else (label, attribute etc.) legend etc.
[9]:
seq = [f"My fig title @ frame #{i}" for i in range(nb_frames)]
txt = plotter.fig.suptitle(seq[0])
# Change the color, just for fun
colors = itertools.cycle(("r", "g", "b", "c", "k", "orange"))
# Define the update function
def _animate(frame: int) -> List[Text]:
"""Update the text value."""
# txt.set_text(seq[frame]) # -> to change the text of the title only
txt = plotter.fig.suptitle(seq[0], fontsize=20, color=next(colors))
return [
txt,
]
# Add the update function to the internal mechanic
plotter.animations_list.append(_animate)
# Display the result
plotter.close()
# Need to desactivate blitting for the results
plotter.animate(nb_frames=nb_frames, blit=False)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("test1D.html")
writer = HTMLWriter(fps=20, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[9]:
To conclude, do not hesistate to use the help which also gives some updated documentation.
[10]:
help(ngp.AnimatedPlotter)
Help on class AnimatedPlotter in module nested_grid_plotter.animated_plotter:
class AnimatedPlotter(nested_grid_plotter.base_plotter.NestedGridPlotter)
| AnimatedPlotter(fig: Optional[matplotlib.figure.Figure] = None, builder: Optional[nested_grid_plotter.base_plotter.NestedBuilder] = None) -> None
|
| Nestedgrid plotter with embedded animation support.
|
| Method resolution order:
| AnimatedPlotter
| nested_grid_plotter.base_plotter.NestedGridPlotter
| nested_grid_plotter.base_plotter.Plotter
| builtins.object
|
| Methods defined here:
|
| __init__(self, fig: Optional[matplotlib.figure.Figure] = None, builder: Optional[nested_grid_plotter.base_plotter.NestedBuilder] = None) -> None
| Initiate the instance.
|
| Parameters
| ----------
| fig_params : Optional[Dict[str, Any]], optional
| See :class:`NestedGridPlotter` for other possible arguments.
| The default is None.
| subfigs_params : Optional[Dict[str, Any]], optional
| DESCRIPTION. The default is None.
| subplots_mosaic_params : Optional[Dict[str, Any]], optional
| DESCRIPTION. The default is None.
|
| Returns
| -------
| None
|
| animate(self, nb_frames: int, blit: bool = True) -> matplotlib.animation.FuncAnimation
| Animate the plot.
|
| Parameters
| ----------
| nb_frames : int
| The number of frames to consider for the animation.
| blit: bool, optional
| Whether blitting is used to optimize drawing. Note: when using blitting,
| any animated artists will be drawn according to their zorder; however,
| they will be drawn on top of any previous artists, regardless of their
| zorder. The default is True.
|
| Returns
| -------
| animation.FuncAnimation
| The animation.
|
| animated_colored_line_between_pts(self, ax_name: str, data: Dict[str, Dict[str, Any]], nb_frames: Optional[int] = None, title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None) -> None
| Plot a 1D animated curves.
|
| The number of frames can be determined automatically from the data.
|
| Parameters
| ----------
| ax_name : str
| Name of the axis on which to plot the animation.
| data : Dict[str, Dict[str, Any]]]
| Data to be plotted.
| nb_frames: int
| Number of frames to use in the animation. If None, the second dimension of
| the provided data arrays is used.
| title : Optional[str], optional
| Title to give to the plot. The default is None.
| xlabel : Optional[str], optional
| Label for the xaxis. The default is None.
| ylabel : Optional[str], optional
| Label for the yaxis. The default is None.
|
| Raises
| ------
| ValueError
| If the provided `data` dictionary contains inconsistent arrays.
|
| Returns
| -------
| None
|
| animated_multi_imshow(self, ax_names: Sequence[str], data: Dict[str, numpy.ndarray[tuple[Any, ...], numpy.dtype[numpy.float64]]], fig: Union[matplotlib.figure.Figure, matplotlib.figure.SubFigure, NoneType] = None, nb_frames: Optional[int] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None, imshow_kwargs: Optional[Dict[str, Any]] = None, cbar_kwargs: Optional[Dict[str, Any]] = None, is_symmetric_cbar: bool = False, cbar_title: Optional[str] = None) -> matplotlib.colorbar.Colorbar
| Plot an animated 2D field with imshow.
|
| The number of frames can be determined automatically from the data.
|
| Parameters
| ----------
| ax_names : str
| List of axis names in which to plot the data. The order of axes must be
| the same as that of the data.
| data : Dict[str, Union[np.ndarray, Dict[str, Any]]]
| Data to be plotted.
| fig: Optional[Figure, SubFigure]
| Which figure to consider for the color bar. By default, use self.fig.
| nb_frames : Optional[int]
| Number of frame to use. By default, it is the number of provided steps,
| that is to say the last dimension of the arrays. If the number of frames
| exceeds the number of steps available, some steps will be repeated once
| or more and a warning is raised.
| xlabel : Optional[str], optional
| Label to apply to all xaxes. The default is None.
| ylabel : Optional[str], optional
| Label to apply to all yaxes. The default is None.
| imshow_kwargs: Optional[Dict[str, Any]] optional
| Optional arguments for `plt.imshow`. The default is None.
|
| Examples
| --------
| Examples can be given using either the ``Example`` or ``Examples``
| sections. Sections support any reStructuredText formatting, including
| literal blocks::
|
| $ python example_numpy.py
|
| Raises
| ------
| ValueError
| If the provided `data` dictionary contains inconsistent arrays.
|
| Returns
| -------
| None
|
| animated_multi_plot(self, ax_name: str, data: Dict[str, Dict[str, Any]], nb_frames: Optional[int] = None, title: Optional[str] = None, xlabel: Optional[str] = None, ylabel: Optional[str] = None) -> None
| Plot a 1D animated curves.
|
| The number of frames can be determined automatically from the data.
|
| Parameters
| ----------
| ax_name : str
| Name of the axis on which to plot the animation.
| data : Dict[str, Dict[str, Any]]]
| Data to be plotted.
| nb_frames: int
| Number of frames to use in the animation. If None, the second dimension of
| the provided data arrays is used.
| title : Optional[str], optional
| Title to give to the plot. The default is None.
| xlabel : Optional[str], optional
| Label for the xaxis. The default is None.
| ylabel : Optional[str], optional
| Label for the yaxis. The default is None.
|
| Raises
| ------
| ValueError
| If the provided `data` dictionary contains inconsistent arrays.
|
| Returns
| -------
| None
|
| plot_animated_text(self, ax: matplotlib.axes._axes.Axes, x: float, y: float, s: Sequence[str], **kwargs: Any) -> None
| Add a text animation to the given axis.
|
| Parameters
| ----------
| ax : Axes
| Axis to which add the text.
| x : float
| x position of the text.
| y : float
| y position of the text.
| s : Sequence[str]
| Sequence of text value to display.
| **kwargs : Dict[str, Any]
| Optional arguments for the class:`Text`.
|
| Returns
| -------
| None
|
| save_animation(self, filename: Union[str, pathlib.Path], writer: Optional[matplotlib.animation.MovieWriter] = None, fps: Optional[int] = None, dpi: Optional[float] = None, codec: Optional[str] = None, bitrate: Optional[int] = None, extra_args: Optional[List[str]] = None, metadata: Optional[Dict[str, str]] = None, extra_anim: Optional[List[matplotlib.animation.Animation]] = None, savefig_kwargs: Optional[Dict[str, Any]] = None, *, progress_callback: Optional[Callable] = None) -> None
| Save the animation as a movie file by drawing every frame.
|
| Parameters
| ----------
| filename : str
| The output filename, e.g., :file:`mymovie.mp4`.
|
| writer : `MovieWriter` or str, default: :rc:`animation.writer`
| A `MovieWriter` instance to use or a key that identifies a
| class to use, such as 'ffmpeg'.
|
| fps : int, optional
| Movie frame rate (per second). If not set, the frame rate from the
| animation's frame interval.
|
| dpi : float, default: :rc:`savefig.dpi`
| Controls the dots per inch for the movie frames. Together with
| the figure's size in inches, this controls the size of the movie.
|
| codec : str, default: :rc:`animation.codec`.
| The video codec to use. Not all codecs are supported by a given
| `MovieWriter`.
|
| bitrate : int, default: :rc:`animation.bitrate`
| The bitrate of the movie, in kilobits per second. Higher values
| means higher quality movies, but increase the file size. A value
| of -1 lets the underlying movie encoder select the bitrate.
|
| extra_args : list of str or None, optional
| Extra command-line arguments passed to the underlying movie encoder. These
| arguments are passed last to the encoder, just before the output filename.
| The default, None, means to use :rc:`animation.[name-of-encoder]_args` for
| the builtin writers.
|
| metadata : dict[str, str], default: {}
| Dictionary of keys and values for metadata to include in
| the output file. Some keys that may be of use include:
| title, artist, genre, subject, copyright, srcform, comment.
|
| extra_anim : list, default: []
| Additional `Animation` objects that should be included
| in the saved movie file. These need to be from the same
| `.Figure` instance. Also, animation frames will
| just be simply combined, so there should be a 1:1 correspondence
| between the frames from the different animations.
|
| savefig_kwargs : dict, default: {}
| Keyword arguments passed to each `~.Figure.savefig` call used to
| save the individual frames.
|
| progress_callback : function, optional
| A callback function that will be called for every frame to notify
| the saving progress. It must have the signature ::
|
| def func(current_frame: int, total_frames: int) -> Any
|
| where *current_frame* is the current frame number and *total_frames* is the
| total number of frames to be saved. *total_frames* is set to None, if the
| total number of frames cannot be determined. Return values may exist but are
| ignored.
|
| Example code to write the progress to stdout::
|
| progress_callback = lambda i, n: print(f'Saving frame {i}/{n}')
|
| Notes
| -----
| *fps*, *codec*, *bitrate*, *extra_args* and *metadata* are used to
| construct a `.MovieWriter` instance and can only be passed if
| *writer* is a string. If they are passed as non-*None* and *writer*
| is a `.MovieWriter`, a `RuntimeError` will be raised.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| animation
| Get the animation or raise an attribute error if not defined.
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __annotations__ = {'_animation': typing.Optional[matplotlib.animation....
|
| ----------------------------------------------------------------------
| Methods inherited from nested_grid_plotter.base_plotter.Plotter:
|
| add_axis_legend(self, ax_name: 'str', is_reverse_items: 'bool' = False, **kwargs: 'Any') -> 'Tuple[List[Any], List[str]]'
| Add a legend to the graphic.
|
| Parameters
| ----------
| ax_name : str
| Ax for which to add the legend.
| is_reverse_items: bool
| Whether to reverse the order of items in the legend. The default is False.
| .. versionadded:: 2.0
| **kwargs : Any
| Additional arguments for `plt.legend`.
|
| Returns
| -------
| (Tuple[List[Any], List[str]])
| Tuple of list of handles and list of associated labels.
|
| add_axis_legend_outside_frame(self, ax_name: 'str', bbox_x_shift: 'Optional[float]' = None, bbox_y_shift: 'Optional[float]' = None, loc: "Literal['left', 'right', 'top', 'bottom']" = 'bottom', borderaxespad: 'float' = 1.0, is_reverse_items: 'bool' = False, **kwargs: 'Any') -> 'Optional[Legend]'
| Add a legend to the ax outside the ax frame.
|
| Parameters
| ----------
| ax_name : str
| The name of the ax for which to add a legend outside the frame.
| bbox_x_shift : float, optional
| Legend vertical shift (up oriented). The default is 0.0.
| bbox_y_shift : float, optional
| Legend horizontal shift (right oriented). The default is 0.0.
| loc : Literal["left", "right", "top", "bottom"], optional
| Side on which to place the legend box. The default is "bottom".
| is_reverse_items: bool
| Whether to reverse the order of items in the legend. The default is False.
| .. versionadded:: 2.0
| **kwargs : Any
| Additional arguments for `plt.legend`.
|
| Returns
| -------
| Optional[Legend]
| The created legend instance. None if no handles nor labels have been found.
|
| add_extra_legend_item(self, ax_name: 'str', handle: 'Any', label: 'str') -> 'None'
| Add an extra legend item to the given axis.
|
| Parameters
| ----------
| ax_name : str
| Ax for which to add the item.
| handle : Any
| Handle to add to the legend.
| label : str
| Label associated to the given handle.
|
| Returns
| -------
| None
|
| add_fig_legend(self, name: 'Optional[str]' = None, bbox_x_shift: 'float' = 0.0, bbox_y_shift: 'float' = 0.0, loc: "Literal['left', 'right', 'top', 'bottom']" = 'bottom', is_reverse_items: 'bool' = False, **kwargs: 'Any') -> 'Optional[Legend]'
| Add a legend to the plot.
|
| To the main figure or to one subfigure.
|
| Parameters
| ----------
| name : Optional[str], optional
| The subfigure to which add the legend. If no name is given, it applies to
| the all figure. Otherwise to a specific subfigure. The default is None.
| bbox_x_shift : float, optional
| Legend vertical shift (up oriented). The default is 0.0.
| bbox_y_shift : float, optional
| Legend horizontal shift (right oriented). The default is 0.0.
| loc : Literal["left", "right", "top", "bottom"], optional
| Side on which to place the legend box. The default is "bottom".
| is_reverse_items: bool
| Whether to reverse the order of items in the legend. The default is False.
| .. versionadded:: 2.0
| **kwargs : Any
| Additional arguments for `plt.legend`.
|
| Returns
| -------
| Optional[Legend]
| The created legend instance. None if no handles nor labels have been found.
|
| add_grid_and_tick_prams_to_all_axes(self, subfigure_name: 'Optional[str]' = None, **kwargs: 'Any') -> 'None'
| Add a grid to all the axes of the figure.
|
| Parameters
| ----------
| subfigure_name : Optional[str], optional
| Name of the target subfigure. If None, apply to the all figure.
| The default is None.
| **kwargs : Any
| Keywords for `add_grid_and_thick_prams_to_axis`.
|
| Returns
| -------
| None
|
| clear_all_axes(self) -> 'None'
| Clear all the axes from their data and layouts.
|
| It also resets the additional handles and labels for the legend.
|
| clear_fig_legends(self) -> 'None'
| Remove all added figure legends
|
| close(self) -> 'None'
| Close the current figure.
|
| get_axes(self, ax_names: 'Sequence[str]') -> 'List[Axes]'
| Get a sequence of axes from the plotter.
|
| Parameters
| ----------
| ax_names : Sequence[str]
| Name of the axes to get. Must be iterable.
|
| Returns
| -------
| Axes
| The desired axes.
|
| get_axis(self, ax_name: 'str') -> 'Axes'
| Get an axis from the plotter.
|
| Parameters
| ----------
| ax_name : str
| Name of the axis to get.
|
| Returns
| -------
| Axes
| The desired axis.
|
| get_subfigure(self, subfig_name: 'str') -> 'SubFigure'
| Get an axis from the plotter.
|
| Parameters
| ----------
| subfig_name : str
| Name of the subfigure to get.
|
| Returns
| -------
| SubFigure
| The desired subfigure.
|
| get_subfigure_ax_dict(self, subfig_name: 'str') -> 'Dict[str, Axes]'
|
| identify_axes(self, fontsize: 'int' = 48) -> 'None'
| Draw the label in a large font in the center of the Axes.
|
| Parameters
| ----------
| ax_dict : Dict[str, Axes]
| Mapping between the title / label and the Axes.
| fontsize : int, optional
| How big the label should be.
|
| Returns
| -------
| None
|
| savefig(self, *args: 'Any', **kwargs: 'Any') -> 'None'
| Save the current figure.
|
| Parameters
| ----------
| *args : Any
| Positional arguments for :meth:`matplotlib.figure.Figure.savefig`.
| **kwargs : Any
| Keywords arguments for :meth:`matplotlib.figure.Figure.savefig`.
|
| Returns
| -------
| None
|
| ----------------------------------------------------------------------
| Readonly properties inherited from nested_grid_plotter.base_plotter.Plotter:
|
| ax_dict
| Return a flatten version of `grouped_ax_dict`.
|
| axes
| Return all axes as a list.
|
| axes_names
| Return all axes names as a list.
|
| sf_dict
| Return a flatten version of `grouped_sf_dict`.
|
| ----------------------------------------------------------------------
| Data descriptors inherited from nested_grid_plotter.base_plotter.Plotter:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Oscilloscope#
https://matplotlib.org/stable/gallery/animation/strip_chart.html
Pas mal non plus ce petit exemple.
Handling nan#
[11]:
plotter = ngp.AnimatedPlotter()
nb_frames = 15
series = -np.arange(nb_frames)
data = np.full((series.size, nb_frames), fill_value=np.nan)
for i in range(series.size):
data[: i + 1, i] = series[: i + 1]
plotter.animated_multi_plot(
ax_name="ax1-1",
data={
"Obj fun": {"y": data, "kwargs": {"c": "r", "linestyle": "--"}},
},
nb_frames=nb_frames,
title="Objective function",
xlabel="Iteration #",
)
# plotter.ax_dict["ax2-1"].set_yscale("log")
plotter.close()
plotter.animate(nb_frames=nb_frames)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("nan_animation.html")
writer = HTMLWriter(fps=2, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[11]:
2D Animations#
Works in a very similar way
Example from https://matplotlib.org/stable/gallery/animation/dynamic_image.html#sphx-glr-gallery-animation-dynamic-image-py
Talk about the number of frames etc.
[12]:
plotter = make_2_frames_plotter()
def f(x, y):
return np.sin(x) + np.cos(y)
x = np.linspace(0, 2 * np.pi, 120)
y = np.linspace(0, 2 * np.pi, 100).reshape(-1, 1)
nb_frames = 20
data1 = []
data2 = []
for i in range(40):
data1.append(f(x + np.pi / 20.0 * i, y + np.pi / 20.0 * i))
data2.append(f(x + np.pi / 20.0 * i, y - np.pi / 20.0 * i))
# Carefull, dimensions are (nx, ny, nt) -> use dstack
data = {"data1": np.dstack(data1), "data2": np.dstack(data2)}
# Plot it
plotter.animated_multi_imshow(
["ax11", "ax12"], data, nb_frames=nb_frames, imshow_kwargs={"cmap": "viridis"}
)
plotter.close()
plotter.animate(nb_frames=nb_frames)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("test2D.html")
writer = HTMLWriter(fps=20, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[12]:
Possibility to scale the data with
imshow_kwargs, to use logscale, to add axes labels etc. Choose where to place the color bar etc.
[13]:
# Plot it
plotter = make_2_frames_plotter()
plotter.animated_multi_imshow(
["ax11", "ax12"],
data,
nb_frames=nb_frames,
imshow_kwargs={"cmap": "viridis", "extent": (0.0, 500, 0.0, 600)},
cbar_title="Happiness level",
xlabel="X (m)",
ylabel="Y (m)",
)
plotter.close()
plotter.animate(nb_frames=nb_frames)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("test2D_2.html")
writer = HTMLWriter(fps=20, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[13]:
Using symmetric log scale
[14]:
# Plot it
plotter = make_2_frames_plotter()
plotter.animated_multi_imshow(
["ax11", "ax12"],
data,
nb_frames=nb_frames,
imshow_kwargs={
"cmap": "viridis",
"extent": (0.0, 500, 0.0, 600),
"norm": mpl.colors.SymLogNorm(linthresh=0.5, linscale=1, base=10),
},
cbar_title="Happiness level",
xlabel="X (m)",
ylabel="Y (m)",
)
plotter.close()
plotter.animate(nb_frames=nb_frames)
# Save the animation locally on the computer
fname_html = fig_save_path.joinpath("test2D_2.html")
writer = HTMLWriter(fps=20, embed_frames=True)
writer.frame_format = "svg" # Ensure svg format
plotter.animation.save(fname_html, writer=writer)
# Display the animation
HTML(fname_html.read_text())
[14]:
Combining 1 and 2D for animated plots#
TODO: show the compatibility with legend management etc.
3D Animations#
TODO: this is not implemented yet.