Datumsgenauigkeit und Epochen #

Matplotlib kann datetimeObjekte und numpy.datetime64Objekte mit einem Einheitenkonverter behandeln, der diese Daten erkennt und sie in Fließkommazahlen umwandelt.

Vor Matplotlib 3.3 gab die Standardeinstellung für diese Konvertierung eine Gleitkommazahl zurück, die Tage seit "0000-12-31T00:00:00" war. Ab Matplotlib 3.3 ist der Standardwert Tage ab "1970-01-01T00:00:00". Dies ermöglicht eine höhere Auflösung für moderne Datumsangaben. „2020-01-01“ mit der alten Epoche, konvertiert in 730120, und eine 64-Bit-Gleitkommazahl hat eine Auflösung von 2^{-52} oder ungefähr 14 Mikrosekunden, sodass die Mikrosekundengenauigkeit verloren ging. Mit der neuen Standardepoche „2020-01-01“ ist 10957,0, also beträgt die erreichbare Auflösung 0,21 Mikrosekunden.

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()

Datetime #

Python datetime-Objekte haben eine Mikrosekundenauflösung, sodass Datumsangaben mit der alten Standard-Matplotlib keine Datetime-Objekte mit voller Auflösung umrunden konnten.

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

Beachten Sie, dass dies nur ein Rundungsfehler ist und es kein Problem für Daten gibt, die näher an der alten Epoche liegen:

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

Wenn ein Benutzer moderne Daten mit Mikrosekundengenauigkeit verwenden möchte, kann er die Epoche mit ändern set_epoch. Die Epoche muss jedoch vor allen Datumsoperationen gesetzt werden, um Verwechslungen zwischen verschiedenen Epochen zu vermeiden. Wenn Sie später versuchen, die Epoche zu ändern, wird eine 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.

Für dieses Tutorial setzen wir den Sentinel mit einer privaten Methode zurück, aber Benutzer sollten die Epoche nur einmal festlegen, wenn überhaupt.

_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.datetime64Objekte haben eine Genauigkeit im Mikrosekundenbereich für einen viel größeren Zeitraum als datetimeObjekte. Derzeit wird die Matplotlib-Zeit jedoch nur zurück in datetime-Objekte konvertiert, die eine Auflösung von Mikrosekunden haben, und Jahre, die nur 0000 bis 9999 umfassen.

_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

Plotten #

Das alles wirkt sich natürlich auf das Plotten aus. Bei der alten Default-Epoche wurden bei der internen Umrechnung die Zeiten gerundet date2num, was zu Sprüngen in den Daten führte:

_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()
Epoche: 1970-01-01T00:00:00

Für Daten, die mit der jüngeren Epoche gezeichnet wurden, ist die Darstellung glatt:

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.
Epoche: 1970-01-01T00:00:00

Verweise

In diesem Beispiel wird die Verwendung der folgenden Funktionen, Methoden, Klassen und Module gezeigt:

Galerie generiert von Sphinx-Gallery