Reputation: 13
It's my first time asking a question here. So please tell me if anything is amiss.
So I’m trying to create a dataset of synthetically generated charts to train a neural net to find bounding boxes for different elements of a chart - legend box, chart title, axes labels, etc. That's the part I've managed to do.
Next what I need is to create a mapping from different legend entries to their corresponding datapoints. I need to create annotations for bounding boxes around the different handles and text like this:
I've tried looking around the docs, but there can't find any related functionality. Looking into properties of legend using matplotlib.artist.getp()
also got me nothing on this.
fig, ax = plt.subplots(figsize=(12, 4))
x_vals = np.linspace(0, 5, 5)
y_vals = np.random.uniform(size=(5,))
ax.plot(x_vals, y_vals, label='line1')
ax.plot(x_vals, y_vals + np.random.randn(), label='line2')
leg = ax.legend()
ax.set_label('Label via method')
matplotlib.artist.getp(leg)
Output:
agg_filter = None
alpha = None
animated = False
bbox_to_anchor = TransformedBbox( Bbox(x0=0.125, y0=0.125, x1=0...
children = [<matplotlib.offsetbox.VPacker object at 0x7f3582d...
clip_box = None
clip_on = True
clip_path = None
contains = None
default_handler_map = {<class 'matplotlib.container.StemContainer'>: <ma...
figure = Figure(864x288)
frame = FancyBboxPatch(640.55,203.64;60.625x33)
frame_on = True
gid = None
label =
legend_handler_map = {<class 'matplotlib.container.StemContainer'>: <ma...
lines = [<matplotlib.lines.Line2D object at 0x7f35834f4400...
patches = <a list of 0 Patch objects>
path_effects = []
picker = None
rasterized = None
sketch_params = None
snap = None
texts = <a list of 2 Text objects>
title = Text(0,0,'None')
transform = IdentityTransform()
transformed_clip_path_and_affine = (None, None)
url = None
visible = True
window_extent = Bbox(x0=640.5500000000001, y0=203.64, x1=701.17500...
zorder = 5
Any help would be appreciated. Please tell me if any clarification is needed. Thanks
Upvotes: 1
Views: 357
Reputation: 4638
I spent a solid 30min poking around trying to figure something out.. I'm sure there's an easier way but here's something I whipped up in the hopes it helps you
import matplotlib.pyplot as plt; plt.ion()
from matplotlib.patches import Rectangle as rect
fig, ax = plt.subplots()
ax.plot([0,1],[4,6],label='test')
leg = ax.legend(edgecolor='w', loc='upper left')
leg.get_frame().set_alpha(0)
line = leg.get_lines()[0]
plt.draw()
plt.pause(0.001)
#all of this is based on pixel coordinates in the figure
(lx0, ly0, lx1, ly1) = (leg.get_window_extent().x0,
leg.get_window_extent().y0,
leg.get_window_extent().x1,
leg.get_window_extent().y1)
(mx0, my0, mx1, my1) = (line.get_window_extent(fig).x0,
line.get_window_extent(fig).y0,
line.get_window_extent(fig).x1,
line.get_window_extent(fig).y1)
(ax0, ay0, ax1, ay1) = (ax.get_window_extent().x0,
ax.get_window_extent().y0,
ax.get_window_extent().x1,
ax.get_window_extent().y1)
#convert pixel coords to graphical coords
x0, x1 = ax.get_xlim()
y0, y1 = ax.get_ylim()
ratex = (x1-x0) / (ax1-ax0)
ratey = (y1-y0) / (ay1-ay0)
newx0 = (mx0 - ax0) * ratex + x0
newx1 = (mx1 - ax0) * ratex + x0
newy0 = (my0 - ay0) * ratey + y0 - 0.05
newy1 = (my1 - ay0) * ratey + y0 + 0.05
#box around legend marker
ax.add_patch(rect((newx0, newy0), newy1-newy0, newx1-newx0, edgecolor='k', alpha=0.5, facecolor='w'))
#convert pixel coords to graphical coords
tx0 = mx1
tx1 = lx1
ty0 = ly0
ty1 = ly1
newx0 = (tx0 - ax0) * ratex + x0
newx1 = (tx1 - ax0) * ratex + x0
newy0 = (ty0 - ay0) * ratey + y0
newy1 = (ty1 - ay0) * ratey + y0
#box around legend txt
ax.add_patch(rect((newx0, newy0), newy1-newy0, newx1-newx0, edgecolor='k', alpha=0.5, facecolor='w'))
this produces the following plot:
I'm not 100% sure why the boxes are off but you could modify this to make it work somehow...you could also use this to get coordinates for the legend entries, and draw a vector from those coordinates to coordinates on the corresponding data lines
Upvotes: 1