This is a very direct follow-up on this question.
Using matplotlib, I'd like to be able to place a sort of "highlighting bar" over a range of data markers that I know will all be in a straight horizontal line.
This bar/rectangle should be slightly taller than the markers and contain them, something like this for the three markers below:

In order to be a sensible highlighting bar, it needs to have the following two traits:
If it is helpful to know, these markers have no meaningful y values (they are plotted all at y=-1), only meaningful x values. Therefore, the height of the bar is meaningless in data coordinates; it merely needs to be always just tall enough to enclose the markers.
MatPlotLib with Python Create x and y points, using numpy. To zoom a part of an image, we can make data for x and y points, in that range. Plot x and y points (Step 1), using the plot() method with lw=2, color red and label. Use the legend() method to place text for the plot, Main curve.
Press the right mouse button to zoom, dragging it to a new position. The x axis will be zoomed in proportionately to the rightward movement and zoomed out proportionately to the leftward movement. The same is true for the y axis and up/down motions.
zoom() function in axis module of matplotlib library is used to zoom in or out on axis .
Matplotlib is a plotting library for the Python programming language and its numerical mathematics extension NumPy. It provides an object-oriented API for embedding plots into applications using general-purpose GUI toolkits like Tkinter, wxPython, Qt, or GTK.
Great question! This was a good challenge and requires a combination of things to achieve.
Firstly, we need to invent a transform which will return the device coordinates of a pre-defined value plus an offset based on the given point. For instance, if we know we want the bar to be at x_pt, y_pt, then the transform should represent (in pseudo code):
def transform(x, y):
    return x_pt_in_device + x, y_pt_in_device + y
Once we have done this, we could use this transform to draw a box of 20 pixels around a fixed data point. However, you only want to draw a box of fixed pixel height in the y direction, but in the x direction you would like standard data scaling.
Therefore, we need to create a blended transform which can transform the x and y coordinates independently. The whole code to do what you are asking:
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()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With