Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matplotlib/Pyplot: How to zoom subplots together AND x-scroll separately?

I previously asked the question "How to zoom subplots together?", and have been using the excellent answer since then.

I'm now plotting just two sets of time-series data, and I need to continue to zoom as above, but now I need to also pan one plot relative to the other (I'm doing eyeball correlation). The data comes from 2 independent instruments with different start times and different clock settings.

In use, I zoom using the 'Zoom to Rectangle' toolbar button, and I scroll using the "Pan/Zoom" button.

How may I best scroll one plot in X relative to the other? Ideally, I'd also like to capture and display the time difference. I do not need to scroll vertically in Y.

I suspect I may need to stop using the simple "sharex=" "sharey=" method, but am not certain how best to proceed.

Thanks, in advance, to the great StackOverflow community!

-BobC

like image 519
BobC Avatar asked Feb 15 '11 01:02

BobC


People also ask

How do I split a subplot in Matplotlib?

Using subplots_adjust() method to set the spacing between subplots. We can use the plt. subplots_adjust() method to change the space between Matplotlib subplots. The parameters wspace and hspace specify the space reserved between Matplotlib subplots.

How do you zoom in subplots in Python?

MatPlotLib with Python We can use the attribute sharex = "ax1", and then, use the subplot method to zoom the subplots together.

How do I zoom in x axis Matplotlib?

If you press 'x' or 'y' while panning the motion will be constrained to the x or y axis, respectively. Press the right mouse button to zoom, dragging it to a new position. The x axis will be zoomed in proportionate to the rightward movement and zoomed out proportionate to the leftward movement.


1 Answers

I hacked the above solution until it did want I think I want.

# File: ScrollTest.py
# coding: ASCII
"""
Interatively zoom plots together, but permit them to scroll independently.
"""
from matplotlib import pyplot
import sys

def _get_limits( ax ):
    """ Return X and Y limits for the passed axis as [[xlow,xhigh],[ylow,yhigh]]
    """
    return [list(ax.get_xlim()), list(ax.get_ylim())]

def _set_limits( ax, lims ):
    """ Set X and Y limits for the passed axis
    """
    ax.set_xlim(*(lims[0]))
    ax.set_ylim(*(lims[1]))
    return

def pre_zoom( fig ):
    """ Initialize history used by the re_zoom() event handler.
        Call this after plots are configured and before pyplot.show().
    """
    global oxy
    oxy = [_get_limits(ax) for ax in fig.axes]
    # :TODO: Intercept the toolbar Home, Back and Forward buttons.
    return

def re_zoom(event):
    """ Pyplot event handler to zoom all plots together, but permit them to
        scroll independently.  Created to support eyeball correlation.
        Use with 'motion_notify_event' and 'button_release_event'.
    """
    global oxy
    for ax in event.canvas.figure.axes:
        navmode = ax.get_navigate_mode()
        if navmode is not None:
            break
    scrolling = (event.button == 1) and (navmode == "PAN")
    if scrolling:                   # Update history (independent of event type)
        oxy = [_get_limits(ax) for ax in event.canvas.figure.axes]
        return
    if event.name != 'button_release_event':    # Nothing to do!
        return
    # We have a non-scroll 'button_release_event': Were we zooming?
    zooming = (navmode == "ZOOM") or ((event.button == 3) and (navmode == "PAN"))
    if not zooming:                 # Nothing to do!
        oxy = [_get_limits(ax) for ax in event.canvas.figure.axes]  # To be safe
        return
    # We were zooming, but did anything change?  Check for zoom activity.
    changed = None
    zoom = [[0.0,0.0],[0.0,0.0]]    # Zoom from each end of axis (2 values per axis)
    for i, ax in enumerate(event.canvas.figure.axes): # Get the axes
        # Find the plot that changed
        nxy = _get_limits(ax)
        if (oxy[i] != nxy):         # This plot has changed
            changed = i
            # Calculate zoom factors
            for j in [0,1]:         # Iterate over x and y for each axis
                # Indexing: nxy[x/y axis][lo/hi limit]
                #           oxy[plot #][x/y axis][lo/hi limit]
                width = oxy[i][j][1] - oxy[i][j][0]
                # Determine new axis scale factors in a way that correctly
                # handles simultaneous zoom + scroll: Zoom from each end.
                zoom[j] = [(nxy[j][0] - oxy[i][j][0]) / width,  # lo-end zoom
                           (oxy[i][j][1] - nxy[j][1]) / width]  # hi-end zoom
            break                   # No need to look at other axes
    if changed is not None:
        for i, ax in enumerate(event.canvas.figure.axes): # change the scale
            if i == changed:
                continue
            for j in [0,1]:
                width = oxy[i][j][1] - oxy[i][j][0]
                nxy[j] = [oxy[i][j][0] + (width*zoom[j][0]),
                          oxy[i][j][1] - (width*zoom[j][1])]
            _set_limits(ax, nxy)
        event.canvas.draw()         # re-draw the canvas (if required)
        pre_zoom(event.canvas.figure)   # Update history
    return
# End re_zoom()

def main(argv):
    """ Test/demo code for re_zoom() event handler.
    """
    import numpy
    x = numpy.linspace(0,100,1000)      # Create test data
    y = numpy.sin(x)*(1+x)

    fig = pyplot.figure()               # Create plot
    ax1 = pyplot.subplot(211)
    ax1.plot(x,y)
    ax2 = pyplot.subplot(212)
    ax2.plot(x,y)

    pre_zoom( fig )                     # Prepare plot event handler
    pyplot.connect('motion_notify_event', re_zoom)  # for right-click pan/zoom
    pyplot.connect('button_release_event',re_zoom)  # for rectangle-select zoom

    pyplot.show()                       # Show plot and interact with user
# End main()

if __name__ == "__main__":
    # Script is being executed from the command line (not imported)
    main(sys.argv)

# End of file ScrollTest.py
like image 112
BobC Avatar answered Oct 20 '22 00:10

BobC