Draw a line over the subplot to make a zoom effect

I am trying to make a plot where the two subheadings are scaling the first. I would like to do something like the image below:

enter image description here

In particular, black lines. The code I have now is just building the subplots

import matplotlib.pyplot as plt
import numpy as np

t=np.arange(1000)
x=np.sin(np.arange(1000)/10)

plt.figure()
ax1 = plt.subplot(2,1,1)
plt.plot(t,x)

ax2 = plt.subplot(2,2,3)
plt.plot(t[100:200],x[100:200])

ax3 = plt.subplot(2,2,4)
plt.plot(t[600:700],x[600:700])

plt.show()

      

But I don't know how to add black lines (to make a zoom effect) on top of the picture. I don't know how to start with this. Anyone have an idea?

+3


source to share


2 answers


Actually I found another theme that did something similar to what I wanted: Use subplots to scale in timeouts or how to draw lines outside of axis boundaries

So here's the code:



import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle

def zoomingBox(ax1, roi, ax2, color='red', linewidth=2):
    ax1.add_patch(Rectangle([roi[0],roi[2]], roi[1]-roi[0], roi[3]-roi[2],**dict([('fill',False), ('linestyle','dashed'), ('color',color), ('linewidth',linewidth)]) ))
    srcCorners = [[roi[0],roi[2]], [roi[0],roi[3]], [roi[1],roi[2]], [roi[1],roi[3]]]
    dstCorners = ax2.get_position().corners()
    srcBB = ax1.get_position()
    dstBB = ax2.get_position()
    if (dstBB.min[0]>srcBB.max[0] and dstBB.max[1]<srcBB.min[1]) or (dstBB.max[0]<srcBB.min[0] and dstBB.min[1]>srcBB.max[1]):
        src = [0, 3]; dst = [0, 3]
    elif (dstBB.max[0]<srcBB.min[0] and dstBB.max[1]<srcBB.min[1]) or (dstBB.min[0]>srcBB.max[0] and dstBB.min[1]>srcBB.max[1]):
        src = [1, 2]; dst = [1, 2]
    elif dstBB.max[1] < srcBB.min[1]:
        src = [0, 2]; dst = [1, 3]
    elif dstBB.min[1] > srcBB.max[1]:
        src = [1, 3]; dst = [0, 2]
    elif dstBB.max[0] < srcBB.min[0]:
        src = [0, 1]; dst = [2, 3]
    elif dstBB.min[0] > srcBB.max[0]:
        src = [2, 3]; dst = [0, 1]
    for k in range(2):
        ax1.annotate('', xy=dstCorners[dst[k]], xytext=srcCorners[src[k]], xycoords='figure fraction', textcoords='data', arrowprops=dict([('arrowstyle','-'), ('color',color), ('linewidth',linewidth)]))



t=np.arange(1000)
x=np.sin(np.arange(1000)/10)

plt.figure()
ax1 = plt.subplot(2,1,1)
plt.plot(t,x)

ax2 = plt.subplot(2,2,3)
plt.plot(t[100:200],x[100:200])

ax3 = plt.subplot(2,2,4)
plt.plot(t[600:700],x[600:700])

zoomingBox(ax1, [100,200,-1.1,1.1], ax2)
zoomingBox(ax1, [600,700,-1.1,1.1], ax3)

plt.show()

      

which give the following: enter image description here

+2


source


The matplotlib page has an example of such a connection between axes. Find it here: https://matplotlib.org/users/annotations.html#zoom-effect-between-axes

Adapting this to your case is pretty straight forward:



enter image description here

from matplotlib.transforms import Bbox, TransformedBbox, blended_transform_factory

from mpl_toolkits.axes_grid1.inset_locator import BboxPatch, BboxConnector,\
    BboxConnectorPatch


def connect_bbox(bbox1, bbox2,
                 loc1a, loc2a, loc1b, loc2b,
                 prop_lines, prop_patches=None):
    if prop_patches is None:
        prop_patches = prop_lines.copy()
        prop_patches["alpha"] = prop_patches.get("alpha", 1)*0.2

    c1 = BboxConnector(bbox1, bbox2, loc1=loc1a, loc2=loc2a, **prop_lines)
    c1.set_clip_on(False)
    c2 = BboxConnector(bbox1, bbox2, loc1=loc1b, loc2=loc2b, **prop_lines)
    c2.set_clip_on(False)

    bbox_patch1 = BboxPatch(bbox1, **prop_patches)
    bbox_patch2 = BboxPatch(bbox2, **prop_patches)

    p = BboxConnectorPatch(bbox1, bbox2,
                           loc1a=loc1a, loc2a=loc2a, loc1b=loc1b, loc2b=loc2b,
                           **prop_patches)
    p.set_clip_on(False)

    return c1, c2, bbox_patch1, bbox_patch2, p




def zoom_effect02(ax1, ax2, **kwargs):
    """
    ax2 : the big main axes
    ax1 : the zoomed axes
    The xmin & xmax will be taken from the
    ax1.viewLim.
    """

    tt = ax1.transScale + (ax1.transLimits + ax2.transAxes)
    trans = blended_transform_factory(ax2.transData, tt)

    mybbox1 = ax1.bbox
    mybbox2 = TransformedBbox(ax1.viewLim, trans)

    prop_patches = kwargs.copy()
    prop_patches["ec"] = "none"
    prop_patches["alpha"] = 0.2

    c1, c2, bbox_patch1, bbox_patch2, p = \
        connect_bbox(mybbox1, mybbox2,
                     loc1a=2, loc2a=3, loc1b=1, loc2b=4, 
                     prop_lines=kwargs, prop_patches=prop_patches)

    ax1.add_patch(bbox_patch1)
    ax2.add_patch(bbox_patch2)
    ax2.add_patch(c1)
    ax2.add_patch(c2)
    ax2.add_patch(p)

    return c1, c2, bbox_patch1, bbox_patch2, p


import matplotlib.pyplot as plt
import numpy as np

t=np.arange(1000)
x=np.sin(np.arange(1000)/10.)

plt.figure()
ax1 = plt.subplot(2,1,1)
plt.plot(t,x)

ax2 = plt.subplot(2,2,3)
plt.plot(t[100:200],x[100:200])
zoom_effect02(ax2, ax1)
ax3 = plt.subplot(2,2,4)
plt.plot(t[600:700],x[600:700])
zoom_effect02(ax3, ax1)
plt.show()

      

+1


source







All Articles