Précision de la date et époques #

Matplotlib peut gérer datetimedes objets et numpy.datetime64des objets à l'aide d'un convertisseur d'unité qui reconnaît ces dates et les convertit en nombres à virgule flottante.

Avant Matplotlib 3.3, la valeur par défaut pour cette conversion renvoie un flottant qui était jours depuis "0000-12-31T00:00:00". Depuis Matplotlib 3.3, la valeur par défaut est en jours à partir de "1970-01-01T00:00:00". Cela permet plus de résolution pour les dates modernes. "2020-01-01" avec l'ancienne époque convertie en 730120, et un nombre à virgule flottante 64 bits a une résolution de 2^{-52}, soit environ 14 microsecondes, donc la précision à la microseconde a été perdue. Avec la nouvelle époque par défaut "2020-01-01" est 10957.0, donc la résolution réalisable est de 0,21 microsecondes.

import datetime
import numpy as np

import matplotlib.pyplot as plt
import matplotlib.dates as mdates


def _reset_epoch_for_tutorial():
    """
    Users (and downstream libraries) should not use the private method of
    resetting the epoch.
    """
    mdates._reset_epoch_test_example()

Dateheure #

Les objets Python datetimeont une résolution en microsecondes, donc avec les anciennes dates matplotlib par défaut, les objets datetime en pleine résolution ne pouvaient pas aller-retour.

old_epoch = '0000-12-31T00:00:00'
new_epoch = '1970-01-01T00:00:00'

_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
mdates.set_epoch(old_epoch)  # old epoch (pre MPL 3.3)

date1 = datetime.datetime(2000, 1, 1, 0, 10, 0, 12,
                          tzinfo=datetime.timezone.utc)
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip:  ', date2)
Before Roundtrip:  2000-01-01 00:10:00.000012+00:00 Matplotlib date: 730120.0069444446
After Roundtrip:   2000-01-01 00:10:00.000020+00:00

Notez qu'il ne s'agit que d'une erreur d'arrondi et qu'il n'y a pas de problème pour les dates proches de l'ancienne époque :

date1 = datetime.datetime(10, 1, 1, 0, 10, 0, 12,
                          tzinfo=datetime.timezone.utc)
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip:  ', date2)
Before Roundtrip:  0010-01-01 00:10:00.000012+00:00 Matplotlib date: 3288.006944444583
After Roundtrip:   0010-01-01 00:10:00.000012+00:00

Si un utilisateur souhaite utiliser des dates modernes à la microseconde près, il peut modifier l'époque à l'aide de set_epoch. Cependant, l'époque doit être définie avant toute opération de date pour éviter toute confusion entre les différentes époques. Essayer de changer l'époque plus tard soulèvera un RuntimeError.

try:
    mdates.set_epoch(new_epoch)  # this is the new MPL 3.3 default.
except RuntimeError as e:
    print('RuntimeError:', str(e))
RuntimeError: set_epoch must be called before dates plotted.

Pour ce didacticiel, nous réinitialisons la sentinelle à l'aide d'une méthode privée, mais les utilisateurs ne doivent définir l'époque qu'une seule fois, voire pas du tout.

_reset_epoch_for_tutorial()  # Just being done for this tutorial.
mdates.set_epoch(new_epoch)

date1 = datetime.datetime(2020, 1, 1, 0, 10, 0, 12,
                          tzinfo=datetime.timezone.utc)
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip:  ', date2)
Before Roundtrip:  2020-01-01 00:10:00.000012+00:00 Matplotlib date: 18262.006944444583
After Roundtrip:   2020-01-01 00:10:00.000012+00:00

datetime64 #

numpy.datetime64les objets ont une précision à la microseconde pour un espace de temps beaucoup plus grand que datetimeles objets. Cependant, actuellement, l'heure Matplotlib n'est reconvertie qu'en objets datetime, qui ont une résolution en microsecondes et des années qui ne s'étendent que de 0000 à 9999.

_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
mdates.set_epoch(new_epoch)

date1 = np.datetime64('2000-01-01T00:10:00.000012')
mdate1 = mdates.date2num(date1)
print('Before Roundtrip: ', date1, 'Matplotlib date:', mdate1)
date2 = mdates.num2date(mdate1)
print('After Roundtrip:  ', date2)
Before Roundtrip:  2000-01-01T00:10:00.000012 Matplotlib date: 10957.006944444583
After Roundtrip:   2000-01-01 00:10:00.000012+00:00

Traçage #

Tout cela a bien sûr un effet sur le traçage. Avec l'ancienne époque par défaut, les heures étaient arrondies lors de la conversion interne date2num, ce qui entraînait des sauts dans les données :

_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
mdates.set_epoch(old_epoch)

x = np.arange('2000-01-01T00:00:00.0', '2000-01-01T00:00:00.000100',
              dtype='datetime64[us]')
# simulate the plot being made using the old epoch
xold = np.array([mdates.num2date(mdates.date2num(d)) for d in x])
y = np.arange(0, len(x))

# resetting the Epoch so plots are comparable
_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
mdates.set_epoch(new_epoch)

fig, ax = plt.subplots(constrained_layout=True)
ax.plot(xold, y)
ax.set_title('Epoch: ' + mdates.get_epoch())
ax.xaxis.set_tick_params(rotation=40)
plt.show()
Époque : 1970-01-01T00:00:00

Pour les dates tracées à l'aide de l'époque la plus récente, le tracé est lisse :

fig, ax = plt.subplots(constrained_layout=True)
ax.plot(x, y)
ax.set_title('Epoch: ' + mdates.get_epoch())
ax.xaxis.set_tick_params(rotation=40)
plt.show()

_reset_epoch_for_tutorial()  # Don't do this.  Just for this tutorial.
Époque : 1970-01-01T00:00:00

Références

L'utilisation des fonctions, méthodes, classes et modules suivants est illustrée dans cet exemple :

Galerie générée par Sphinx-Gallery