31
loading...
This website collects cookies to deliver better user experience
mkdir matplotlib_timeline
cd matplotlib_timeline
python3 -m venv venv
source venv/bin/activate
pip3 install matplotlib
import matplotlib.pylab as plt
def timeline(titulo, eventos_fechas, eventos_textos, eventos_delta_y):
# Representaremos los años dentro de una línea de 10 unidades
ESCALA = 10.0
# Dejaremos un margen e una unidad, por delante y por detrás en el eje X, por lo que el gráfico
# contendrá 12 unidades (las 10 de los años + 2 de los márgenes).
MARGEN = 1
MARGEN_DELTA = 1.0 / (ESCALA + MARGEN * 2)
# Averiguamos la distancia en años entre el primer acontecimiento y el último, para calcular
# a cuantas unidades de nuestro eje X se corresponde un año.
fecha_minimo = min(eventos_fechas)
fecha_maximo = max(eventos_fechas)
fecha_rango = fecha_maximo - fecha_minimo
fecha_posicion = ESCALA / fecha_rango
# Generamos una lista con la posición relativa que cada año tiene que ocupar dentro del eje X
# Y por comodidad, una lista de posición en el eje Y, para los puntos y las marcas de texto
fechas_x = [(fecha - fecha_minimo) * fecha_posicion + MARGEN for fecha in eventos_fechas]
fechas_y = [0 for fecha in eventos_fechas]
lineas_y = [1 for fecha in eventos_fechas]
# Creamos el gŕafico y establecemos los límites que nos interesan
fig, ax = plt.subplots(figsize=(15, 8), constrained_layout=True)
ax.set_ylim(-2, 2)
ax.set_xlim(0, ESCALA + MARGEN * 2)
ax.set_title(titulo, fontweight="bold", fontfamily='sans', fontsize=22, color='#4a4a4a')
# Dibujamos la línea sobre la que representar los puntos del timeline
ax.axhline(0, xmin=MARGEN_DELTA, xmax=1-MARGEN_DELTA, c='#4a4a4a', zorder=1)
# Dibujamos sobre la línea los puntos correspondientes a cada año de la lista de acontecimientos.
# Primero un círculo grande y después otro más pequeño encima, para dar la sensación de una
# circunferencia con borde y rellena de color.
ax.scatter(fechas_x, fechas_y, s=120, c='#4a4a4a', zorder=2)
ax.scatter(fechas_x, fechas_y, s=30, c='#faff00', zorder=3)
# Establecemos el tamaño de los texto, así como la separación vertical respecto a la
# linea vertical de marca.
FONT_SIZE = 12
DELTA_TEXTO = 0.1
# Determinamos la posición que debe ocupara cada texto dentro del gráfico
# En el eje X coincide con la posición del año sobre el eje.
# En el eje Y lo determina el desplazamiento recibido como parámetro, más la separación
# adicional estipulada.
for x, fecha, texto, delta_y in zip(fechas_x, eventos_fechas, eventos_textos, eventos_delta_y ):
# El cálculo es diferente para valores positivos y negativos
DELTA_TEXTO_y = delta_y + DELTA_TEXTO if delta_y > 0 else delta_y - DELTA_TEXTO
# Cambiamos la alineación vertical en función de si están por enciama, o por debajo
# del eje. Así nos aseguramos una disposición consistente en el caso de textos con
# más de una línea (sin necesidad de tener que ajustar la posición en función
# del tamaño real del texto)
va = 'bottom' if delta_y > 0 else 'top'
# Para mayor claridad, anteponemos al texto un línea con el año al que corresponde
ax.text(
x, DELTA_TEXTO_y, str(fecha) + "\n" + texto,
ha='center', va=va,
fontfamily='sans', fontweight='bold', fontsize=FONT_SIZE,
color='#4a4a4a'
)
# Establecemos márcas verticales para cada acontecimiento. La posición en el eje X,
# nuevamente, la determina el año, y la longitud vertical la que hemos recibido como
# parámetro (con la que hemos ajustado la distancia vertical del texto)
markerline, stemline, baseline = ax.stem(fechas_x, eventos_delta_y, use_line_collection=True)
# Damos estilo a las marcas verticales
plt.setp(baseline, zorder=0)
plt.setp(markerline, marker='', color='#4a4a4a')
plt.setp(stemline, color='#4a4a4a')
# Para que el gŕafico quede más limpio, eliminamos las marcas de los ejes ejes
ax.set_xticks([])
ax.set_yticks([])
# También eliminamos líneas del marco
for spine in ["left", "top", "right", "bottom"]:
ax.spines[spine].set_visible(False)
# Mostramos resultado
plt.show()
# Título del timeline
titulo = "\nNuevos antibióticos desde el año 2000"
# Relación de años a mostrar en el timeline
eventos_fechas = [
2000,
2001,
2003,
2005,
2005,
2009,
2010,
2011,
2012,
2013,
2014,
2014,
2015,
2017,
2019,
2019,
]
# Evento ocurrido en el año de la relación anterior
eventos_textos = [
"linezolid",
"elithromycin",
"daptomycin",
"tigecycline",
"doripenem",
"telavancin",
"ceftaroline",
"fidaxomicin",
"bedaquiline",
"telavancin",
"tedizolid",
"dalbavancin\nceftolozane\ntazobactam",
"ceftazidime\navibactam",
"meropenem\nvaborbactam",
"imipenem\ncilastatin\nrelebactam",
"cefiderocol",
]
# Dado que dependiendo de la longitud de los texto, es bastante probable el solapamiento
# de los mismmos, para cada uno de ellos indicamos el desplazamiento respecto al eje central.
eventos_delta_y = [
0.5,
-0.5,
0.5,
-0.5,
0.5,
-0.5,
0.5,
-0.5,
0.5,
-0.5,
0.5,
-0.8,
0.8,
-0.5,
0.5,
-0.5,
]
# Llamamos a la función que genera el gráfico del timeline
timeline(titulo, eventos_fechas, eventos_textos, eventos_delta_y)