Nota
Haga clic aquí para descargar el código de ejemplo completo
Fecha Precisión y Épocas #
Matplotlib puede manejar datetime
objetos y numpy.datetime64
objetos utilizando un convertidor de unidades que reconoce estas fechas y las convierte en números de punto flotante.
Antes de Matplotlib 3.3, el valor predeterminado para esta conversión devuelve un flotante que era días desde "0000-12-31T00:00:00". A partir de Matplotlib 3.3, el valor predeterminado es días desde "1970-01-01T00:00:00". Esto permite más resolución para las fechas modernas. "2020-01-01" con la época anterior convertida a 730120, y un número de coma flotante de 64 bits tiene una resolución de 2^{-52}, o aproximadamente 14 microsegundos, por lo que se perdió la precisión de microsegundos. Con la nueva época predeterminada "2020-01-01" es 10957.0, por lo que la resolución alcanzable es de 0,21 microsegundos.
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()
Fecha y hora #
Los objetos de Python datetime
tienen una resolución de microsegundos, por lo que con las antiguas fechas predeterminadas de matplotlib no se podían hacer ida y vuelta a los objetos de fecha y hora de resolución completa.
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
Tenga en cuenta que esto es solo un error de redondeo, y no hay problema para las fechas más cercanas a la época anterior:
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 usuario quiere usar fechas modernas con una precisión de microsegundos, puede cambiar la época usando set_epoch
. Sin embargo, la época debe establecerse antes de cualquier operación de fecha para evitar confusiones entre diferentes épocas. Intentar cambiar la época más tarde generará 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.
Para este tutorial, restablecimos el centinela usando un método privado, pero los usuarios solo deben configurar la época una vez, si es que lo hacen.
_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
fechahora64 #
numpy.datetime64
los objetos tienen una precisión de microsegundos para un espacio de tiempo mucho más grande que los datetime
objetos. Sin embargo, actualmente el tiempo de Matplotlib solo se vuelve a convertir a objetos de fecha y hora, que tienen una resolución de microsegundos y años que solo abarcan de 0000 a 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
Trazado #
Todo esto, por supuesto, tiene un efecto en la trama. Con la época predeterminada anterior, los tiempos se redondearon durante la date2num
conversión interna, lo que provocó saltos en los datos:
_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()
Para las fechas trazadas usando la época más reciente, la trama es fluida:
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.
Referencias
En este ejemplo se muestra el uso de las siguientes funciones, métodos, clases y módulos: