Datumpräzision und Epochen#

Matplotlib kann datetime-Objekte und numpy.datetime64-Objekte mithilfe eines Einheitenkonverters verarbeiten, der diese Daten erkennt und in Gleitkommazahlen umwandelt.

Vor Matplotlib 3.3 gab die Standardkonvertierung eine Gleitkommazahl zurück, die Tage seit „0000-12-31T00:00:00“ darstellte. Ab Matplotlib 3.3 ist der Standardwert Tage ab „1970-01-01T00:00:00“. Dies ermöglicht mehr Auflösung für moderne Daten. „2020-01-01“ mit der alten Epoche konvertiert zu 730120, und eine 64-Bit-Gleitkommazahl hat eine Auflösung von 2^{-52}, oder ungefähr 14 Mikrosekunden, sodass die Mikrosekundenpräzision verloren ging. Mit der neuen Standardepoche ist „2020-01-01“ 10957.0, sodass die erreichbare Auflösung 0,21 Mikrosekunden beträgt.

import datetime

import matplotlib.pyplot as plt
import numpy as np

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

Datum und Uhrzeit#

Python datetime-Objekte haben Mikrosekundenauflösung, sodass mit der alten Standard-Matplotlib-Datumsangabe Datumsobjekte voller Auflösung nicht hin und her konvertiert werden 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 keine Probleme für Daten gibt, die der alten Epoche näher sind.

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 Mikrosekundenpräzision verwenden möchte, kann er die Epoche mit set_epoch ändern. Die Epoche muss jedoch vor allen Datumsoperationen festgelegt werden, um Verwirrung zwischen verschiedenen Epochen zu vermeiden. Der Versuch, die Epoche später zu ändern, löst einen RuntimeError aus.

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, wenn überhaupt, festlegen.

_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.datetime64-Objekte haben Mikrosekundenpräzision für einen viel größeren Zeitbereich als datetime-Objekte. Derzeit wird Matplotlib-Zeit jedoch nur in Datumsobjekte konvertiert, die eine Mikrosekundenauflösung 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#

Dies alles hat natürlich Auswirkungen auf das Plotten. Mit der alten Standard-Epoche wurden die Zeiten während der internen date2num-Konvertierung gerundet, 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(layout='constrained')
ax.plot(xold, y)
ax.set_title('Epoch: ' + mdates.get_epoch())
ax.xaxis.set_tick_params(rotation=40)
plt.show()
Epoch: 1970-01-01T00:00:00

Für Daten, die mit der neueren Standard-Epoche geplottet werden, ist der Plot glatt.

fig, ax = plt.subplots(layout='constrained')
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.
Epoch: 1970-01-01T00:00:00

Referenzen

Die Verwendung der folgenden Funktionen, Methoden, Klassen und Module wird in diesem Beispiel gezeigt

Gesamtlaufzeit des Skripts: (0 Minuten 1,964 Sekunden)

Galerie generiert von Sphinx-Gallery