I'm trying to do a plot where two subplots are zoom of a first one. What I would like to do is something like the below image:
In particular the black lines. The code I have for now is just plotting 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 the black lines (to make a zoom box effect) on top of the figure. I don't know how to start with this. Does anybody have an idea?
We can use the attribute sharex = "ax1", and then, use the subplot method to zoom the subplots together.
How to zoom a portion of an image and insert in the same plot in Matplotlib? 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.
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.
In fact I found another topic which made something quite similar to what I wanted: Use subplots to zoom into timeseries or how I can draw lines outside of axis borders
So here 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 this:
There is an example on the matplotlib page doing this kind of connection between axes. Find it here: https://matplotlib.org/users/annotations.html#zoom-effect-between-axes
Adapting this to your case is rather straight forward:
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()
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