简体   繁体   中英

matplotlib mark_inset with different data in inset plot

This is a slightly tricky one to explain. Basically, I want to make an inset plot and then utilize the convenience of mpl_toolkits.axes_grid1.inset_locator.mark_inset, but I want the data in the inset plot to be completely independent of the data in the parent axes.

Example code with the functions I'd like to use:

import numpy as np

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from mpl_toolkits.axes_grid1.inset_locator import mark_inset
from mpl_toolkits.axes_grid1.inset_locator import InsetPosition

data = np.random.normal(size=(2000,2000))
plt.imshow(data, origin='lower')
parent_axes = plt.gca()

ax2 = inset_axes(parent_axes, 1, 1)
ax2.plot([900,1100],[900,1100])

# I need more control over the position of the inset axes than is given by the inset_axes function
ip = InsetPosition(parent_axes,[0.7,0.7,0.3,0.3])
ax2.set_axes_locator(ip)

# I want to be able to control where the mark is connected to, independently of the data in the ax2.plot call
mark_inset(parent_axes, ax2, 2,4)

# plt.savefig('./inset_example.png')
plt.show()

The example code produces the following image: 在此处输入图片说明

So to sum up: The location of the blue box is entire controlled by the input data to ax2.plot(). I would like to manually place the blue box and enter whatever I want into ax2. Is this possible?

quick edit: to be clear, I understand why inset plots would have the data linked, as that's the most likely usage. So if there's a completely different way in matplotlib to accomplish this, do feel free to reply with that. However, I am trying to avoid manually placing boxes and lines to all of the axes I would place, as I need quite a few insets into a large image.

If I understand correctly, you want an arbitrarily scaled axis at a given position that looks like a zoomed inset, but has no connection to the inset marker's position.

Following your approach you can simply add another axes to the plot and position it at the same spot of the true inset, using the set_axes_locator(ip) function. Since this axis is drawn after the original inset, it will be on top of it and you'll only need to hide the tickmarks of the original plot to let it disappear completely ( set_visible(False) does not work here, as it would hide the lines between the inset and the marker position).

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes, mark_inset, InsetPosition

data = np.random.normal(size=(200,200))
plt.imshow(data, origin='lower')
parent_axes = plt.gca()

ax2 = inset_axes(parent_axes, 1, 1)
ax2.plot([60,75],[90,110])
# hide the ticks of the linked axes
ax2.set_xticks([])
ax2.set_yticks([])

#add a new axes to the plot and plot whatever you like
ax3 = plt.gcf().add_axes([0,0,1,1])
ax3.plot([0,3,4], [2,3,1], marker=ur'$\u266B$' , markersize=30, linestyle="") 
ax3.set_xlim([-1,5])
ax3.set_ylim([-1,5])


ip = InsetPosition(parent_axes,[0.7,0.7,0.3,0.3])
ax2.set_axes_locator(ip)
# set the new axes (ax3) to the position of the linked axes
ax3.set_axes_locator(ip)
# I want to be able to control where the mark is connected to, independently of the data in the ax2.plot call
mark_inset(parent_axes, ax2, 2,4)

plt.show()

在此处输入图片说明

FWIW, I came up with a hack that works.

In the source code for inset_locator, I added a version of mark_inset that takes another set of axes used to define the TransformedBbox:

def mark_inset_hack(parent_axes, inset_axes, hack_axes, loc1, loc2, **kwargs):
    rect = TransformedBbox(hack_axes.viewLim, parent_axes.transData)

    pp = BboxPatch(rect, **kwargs)
    parent_axes.add_patch(pp)

    p1 = BboxConnector(inset_axes.bbox, rect, loc1=loc1, **kwargs)
    inset_axes.add_patch(p1)
    p1.set_clip_on(False)
    p2 = BboxConnector(inset_axes.bbox, rect, loc1=loc2, **kwargs)
    inset_axes.add_patch(p2)
    p2.set_clip_on(False)

    return pp, p1, p2

Then in my original-post code I make an inset axis where I want the box to be, pass it to my hacked function, and make it invisible:

# location of desired axes
axdesire = inset_axes(parent_axes,1,1)
axdesire.plot([100,200],[100,200])

mark_inset_hack(parent_axes, ax2, axdesire, 2,4)

axdesire.set_visible(False)

Now I have a marked box at a different location in data units than the inset that I'm marking:

在此处输入图片说明

It is certainly a total hack, and at this point I'm not sure it's cleaner than simply drawing lines manually, but I think for a lot of insets this will keep things conceptually cleaner.

Other ideas are still welcome.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM