Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib artist to stay same size when zoomed in but ALSO move with panning?

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:

enter image description here

In order to be a sensible highlighting bar, it needs to have the following two traits:

  • If the plot is panned, the bar moves with the markers (so it always covers them).
  • If the plot is zoomed, the bar's display height doesn't change (so it always is slightly taller than the markers).

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.

like image 329
Chelonian Avatar asked Mar 21 '12 20:03

Chelonian


People also ask

How do you zoomed a portion of image and insert in the same plot in MatPlotLib?

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.

How do I zoom out on a MatPlotLib plot?

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.

How do you magnify a plot in Python?

zoom() function in axis module of matplotlib library is used to zoom in or out on axis .

What GUI does MatPlotLib use?

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.


1 Answers

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()
like image 143
pelson Avatar answered Sep 27 '22 19:09

pelson