5.2. Graphiques animés#

Dans cette partie, nous allons apprendre les commandes de base de matplotlib pour animer les graphiques. Notez bien que ce n’est pas forcément le plus simple dans les jupyter-notebooks à cause du choix du backend.

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt

mpl.rcParams['savefig.dpi'] = 80
plt.rcParams['figure.dpi'] = 60
%matplotlib notebook

UPS_primary = (0.38823529411764707, 0.0, 0.23529411764705882)
UPS_bleu = (0.0, 0.3058823529411765, 0.49019607843137253)
UPS_orange = (0.9333333333333333, 0.20392156862745098, 0.13725490196078433)
UPS_vert = (0.0, 0.5019607843137255, 0.47843137254901963)

5.2.1. Animation#

Il est possible de faire des animations, c’est-à-dire d’afficher un graphique avec des données qui sont modifiées au cours du temps (par exemple par un code qui retourne une solution à l’instant \(t\)).

from matplotlib.animation import FuncAnimation

help(FuncAnimation)
Help on class FuncAnimation in module matplotlib.animation:

class FuncAnimation(TimedAnimation)
 |  FuncAnimation(fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
 |  
 |  `TimedAnimation` subclass that makes an animation by repeatedly calling
 |  a function *func*.
 |  
 |  .. note::
 |  
 |      You must store the created Animation in a variable that lives as long
 |      as the animation should run. Otherwise, the Animation object will be
 |      garbage-collected and the animation stops.
 |  
 |  Parameters
 |  ----------
 |  fig : `~matplotlib.figure.Figure`
 |      The figure object used to get needed events, such as draw or resize.
 |  
 |  func : callable
 |      The function to call at each frame.  The first argument will
 |      be the next value in *frames*.   Any additional positional
 |      arguments can be supplied using `functools.partial` or via the *fargs*
 |      parameter.
 |  
 |      The required signature is::
 |  
 |          def func(frame, *fargs) -> iterable_of_artists
 |  
 |      It is often more convenient to provide the arguments using
 |      `functools.partial`. In this way it is also possible to pass keyword
 |      arguments. To pass a function with both positional and keyword
 |      arguments, set all arguments as keyword arguments, just leaving the
 |      *frame* argument unset::
 |  
 |          def func(frame, art, *, y=None):
 |              ...
 |  
 |          ani = FuncAnimation(fig, partial(func, art=ln, y='foo'))
 |  
 |      If ``blit == True``, *func* must return an iterable of all artists
 |      that were modified or created. This information is used by the blitting
 |      algorithm to determine which parts of the figure have to be updated.
 |      The return value is unused if ``blit == False`` and may be omitted in
 |      that case.
 |  
 |  frames : iterable, int, generator function, or None, optional
 |      Source of data to pass *func* and each frame of the animation
 |  
 |      - If an iterable, then simply use the values provided.  If the
 |        iterable has a length, it will override the *save_count* kwarg.
 |  
 |      - If an integer, then equivalent to passing ``range(frames)``
 |  
 |      - If a generator function, then must have the signature::
 |  
 |           def gen_function() -> obj
 |  
 |      - If *None*, then equivalent to passing ``itertools.count``.
 |  
 |      In all of these cases, the values in *frames* is simply passed through
 |      to the user-supplied *func* and thus can be of any type.
 |  
 |  init_func : callable, optional
 |      A function used to draw a clear frame. If not given, the results of
 |      drawing from the first item in the frames sequence will be used. This
 |      function will be called once before the first frame.
 |  
 |      The required signature is::
 |  
 |          def init_func() -> iterable_of_artists
 |  
 |      If ``blit == True``, *init_func* must return an iterable of artists
 |      to be re-drawn. This information is used by the blitting algorithm to
 |      determine which parts of the figure have to be updated.  The return
 |      value is unused if ``blit == False`` and may be omitted in that case.
 |  
 |  fargs : tuple or None, optional
 |      Additional arguments to pass to each call to *func*. Note: the use of
 |      `functools.partial` is preferred over *fargs*. See *func* for details.
 |  
 |  save_count : int, optional
 |      Fallback for the number of values from *frames* to cache. This is
 |      only used if the number of frames cannot be inferred from *frames*,
 |      i.e. when it's an iterator without length or a generator.
 |  
 |  interval : int, default: 200
 |      Delay between frames in milliseconds.
 |  
 |  repeat_delay : int, default: 0
 |      The delay in milliseconds between consecutive animation runs, if
 |      *repeat* is True.
 |  
 |  repeat : bool, default: True
 |      Whether the animation repeats when the sequence of frames is completed.
 |  
 |  blit : bool, default: False
 |      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.
 |  
 |  cache_frame_data : bool, default: True
 |      Whether frame data is cached.  Disabling cache might be helpful when
 |      frames contain large objects.
 |  
 |  Method resolution order:
 |      FuncAnimation
 |      TimedAnimation
 |      Animation
 |      builtins.object
 |  
 |  Methods defined here:
 |  
 |  __init__(self, fig, func, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  new_frame_seq(self)
 |      Return a new sequence of frame information.
 |  
 |  new_saved_frame_seq(self)
 |      Return a new sequence of saved/cached frame information.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  save_count
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from TimedAnimation:
 |  
 |  repeat
 |  
 |  ----------------------------------------------------------------------
 |  Methods inherited from Animation:
 |  
 |  __del__(self)
 |  
 |  pause(self)
 |      Pause the animation.
 |  
 |  resume(self)
 |      Resume the animation.
 |  
 |  save(self, filename, writer=None, fps=None, dpi=None, codec=None, bitrate=None, extra_args=None, metadata=None, extra_anim=None, savefig_kwargs=None, *, progress_callback=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.
 |  
 |  to_html5_video(self, embed_limit=None)
 |      Convert the animation to an HTML5 ``<video>`` tag.
 |      
 |      This saves the animation as an h264 video, encoded in base64
 |      directly into the HTML5 video tag. This respects :rc:`animation.writer`
 |      and :rc:`animation.bitrate`. This also makes use of the
 |      *interval* to control the speed, and uses the *repeat*
 |      parameter to decide whether to loop.
 |      
 |      Parameters
 |      ----------
 |      embed_limit : float, optional
 |          Limit, in MB, of the returned animation. No animation is created
 |          if the limit is exceeded.
 |          Defaults to :rc:`animation.embed_limit` = 20.0.
 |      
 |      Returns
 |      -------
 |      str
 |          An HTML5 video tag with the animation embedded as base64 encoded
 |          h264 video.
 |          If the *embed_limit* is exceeded, this returns the string
 |          "Video too large to embed."
 |  
 |  to_jshtml(self, fps=None, embed_frames=True, default_mode=None)
 |      Generate HTML representation of the animation.
 |      
 |      Parameters
 |      ----------
 |      fps : int, optional
 |          Movie frame rate (per second). If not set, the frame rate from
 |          the animation's frame interval.
 |      embed_frames : bool, optional
 |      default_mode : str, optional
 |          What to do when the animation ends. Must be one of ``{'loop',
 |          'once', 'reflect'}``. Defaults to ``'loop'`` if the *repeat*
 |          parameter is True, otherwise ``'once'``.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Animation:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
fig = plt.figure(figsize=(6, 6))
fig.clf()

ax = fig.add_subplot(1, 1, 1)
xdata = np.linspace(0, 2*np.pi, 256)
ydata = np.sin(xdata)
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1, 1)
ln = ax.plot([], [], linewidth=2, color='orange')[0]
ax.set_title("frame = 0", fontsize=15)

def update(frame):
    ln.set_data(xdata[:frame], ydata[:frame])
    ax.set_title(f"frame = {frame}", fontsize=15)
    return ln

ani = FuncAnimation(
    fig, update,
    blit=False, interval=20,
    frames=xdata.size
)
ani.save("05_2_anim1.gif")
ipywidget
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import ipywidgets as widgets

mpl.rcParams['savefig.dpi'] = 80
plt.rcParams['figure.dpi'] = 60
%matplotlib notebook

UPS_primary = (0.38823529411764707, 0.0, 0.23529411764705882)
UPS_bleu = (0.0, 0.3058823529411765, 0.49019607843137253)
UPS_orange = (0.9333333333333333, 0.20392156862745098, 0.13725490196078433)
UPS_vert = (0.0, 0.5019607843137255, 0.47843137254901963)

UPS_colors = {
    'UPS': UPS_primary,
    'bleu': UPS_bleu,
    'orange': UPS_orange,
    'vert': UPS_vert
}
def phi(t, f):
    return np.sin(2*np.pi*f*t)

t= np.linspace(0, 2*np.pi, 10000)
f = 1
fig = plt.figure(figsize=(6, 6))
ax2 = fig.add_subplot(1, 1, 1)
line, = ax2.plot([], [], color='orange', linewidth=2)
ax2.set_title(f"$f={f}$")
ax2.set_xlim(0, 2*np.pi)
ax2.set_ylim(-1, 1)
color_names = list(UPS_colors.keys())

def update(f, color):
    line.set_data(t, phi(t, f))
    line.set_color(UPS_colors[color])
    ax2.set_title(f"$f={f}$")
    plt.show()

widgets.interact(
    update,
    f=widgets.FloatSlider(
        value=1,
        min=0, max=2, step=.01,
        continuous_update=True,
        description='fréquence',
    ),
    color=widgets.Dropdown(
        options=color_names,
        value=color_names[0],
        description="couleur"
    )
)
plt.show()
ipywidget