Matplotlib artista para mantener el mismo tamaño cuando se amplía pero TAMBIÉN se mueve con outlook?

Este es un seguimiento muy directo de esta pregunta .

Con matplotlib , me gustaría poder colocar una especie de “barra de resaltado” sobre un rango de marcadores de datos que sé que estarán en una línea recta horizontal.

Esta barra / rectángulo debe ser ligeramente más alto que los marcadores y contenerlos, algo como esto para los tres marcadores a continuación:

introduzca la descripción de la imagen aquí

Para ser una barra de resaltado sensible, debe tener los siguientes dos rasgos:

  • Si la ttwig se desplaza, la barra se mueve con los marcadores (por lo que siempre los cubre).
  • Si la ttwig se amplía, la altura de visualización de la barra no cambia (por lo que siempre es un poco más alta que los marcadores).

Si es útil saberlo, estos marcadores no tienen valores de y significativos (se representan todos en y = -1), solo valores de x significativos. Por lo tanto, la altura de la barra no tiene sentido en las coordenadas de datos; simplemente debe ser siempre lo suficientemente alto como para encerrar los marcadores.

Gran pregunta Este fue un buen desafío y requiere una combinación de cosas para lograrlo.

En primer lugar, debemos inventar una transformación que devuelva las coordenadas del dispositivo de un valor predefinido más un desplazamiento basado en el punto dado. Por ejemplo, si sabemos que queremos que la barra esté en x_pt, y_pt, entonces la transformación debe representar (en pseudo código):

def transform(x, y): return x_pt_in_device + x, y_pt_in_device + y 

Una vez que hayamos hecho esto, podríamos usar esta transformación para dibujar una caja de 20 píxeles alrededor de un punto de datos fijo. Sin embargo, solo desea dibujar un cuadro de altura de píxel fijo en la dirección y, pero en la dirección x desea una escala de datos estándar.

Por lo tanto, necesitamos crear una transformada combinada que pueda transformar las coordenadas x e y de forma independiente. Todo el código para hacer lo que pides:

 import matplotlib.pyplot as plt import matplotlib.patches as mpatches import matplotlib.path as mpath import matplotlib.transforms as mtrans import numpy as np class FixedPointOffsetTransform(mtrans.Transform): """ Always returns the same transformed point plus the given point in device coordinates as an offset. """ def __init__(self, trans, fixed_point): mtrans.Transform.__init__(self) self.input_dims = self.output_dims = 2 self.trans = trans self.fixed_point = np.array(fixed_point).reshape(1, 2) def transform(self, values): fp = self.trans.transform(self.fixed_point) values = np.array(values) if values.ndim == 1: return fp.flatten() + values else: return fp + values plt.scatter([3.1, 3.2, 3.4, 5], [2, 2, 2, 6]) ax = plt.gca() fixed_pt_trans = FixedPointOffsetTransform(ax.transData, (0, 2)) xdata_yfixed = mtrans.blended_transform_factory(ax.transData, fixed_pt_trans) x = [3.075, 3.425] # x range of box (in data coords) height = 20 # of box in device coords (pixels) path = mpath.Path([[x[0], -height], [x[1], -height], [x[1], height], [x[0], height], [x[0], -height]]) patch = mpatches.PathPatch(path, transform=xdata_yfixed, facecolor='red', edgecolor='black', alpha=0.4, zorder=0) ax.add_patch(patch) plt.show()