4

I'm trying to draw a line inside a scatterplot in matplotlib. I'm using an event function, so that images will be displayed when hovering over with the cursor. Now I want to draw a line between two specific points and display all images from scatter points crossed by line in a subplot next to scatter.

Is this possible and how can i achieve this?

Thanks for any help.

Following code is similar to previous posts here, but can't remember which. Changed some like from plot to scatter.

So far I'm using this code for displaying image on event:

def view_embedding_scatter(model, img_dim, root_path):

#data is generated from pytorch model
#images have a input dim of 128x128
#labelarr is feed bei DataLoader, represents classes
#x and y are reduced on dimensions by pca
#arr contains all images 


# create figure and plot scatter
fig = plt.figure()
ax = fig.add_subplot()
sc = ax.scatter(x,y, c=labelarr, marker=".", linewidths=0.5, cmap="Dark2")


# create the annotations box
im = OffsetImage(arr[0,:,:], zoom=0.75)
xybox=(50., 50.)
ab = AnnotationBbox(im, (0,0), xybox=xybox, xycoords='data',
        boxcoords="offset points",  pad=0.3,  arrowprops=dict(arrowstyle="->"))
# add it to the axes and make it invisible
ax.add_artist(ab)
ab.set_visible(False)

def hover(event):
    # if the mouse is over the scatter points
    if sc.contains(event)[0]:
        # find out the index within the array from the event
        ind, = sc.contains(event)[1]["ind"]
        # get the figure size
        w,h = fig.get_size_inches()*fig.dpi
        ws = (event.x > w/2.)*-1 + (event.x <= w/2.) 
        hs = (event.y > h/2.)*-1 + (event.y <= h/2.)
        # if event occurs in the top or right quadrant of the figure,
        # change the annotation box position relative to mouse.
        ab.xybox = (xybox[0]*ws, xybox[1]*hs)
        # make annotation box visible
        ab.set_visible(True)
        # place it at the position of the hovered scatter point
        ab.xy =(x[ind], y[ind])
        # set the image corresponding to that point
        im.set_data(arr[ind,:,:])
    else:
        #if the mouse is not over a scatter point
        ab.set_visible(False)
    fig.canvas.draw_idle()

# add callback for mouse moves
fig.canvas.mpl_connect('motion_notify_event', hover)  
plt.tight_layout()         
plt.show()
jpschreiter
  • 313
  • 1
  • 3
  • 6
  • 1
    Depending on your dataset, I think you may actually be looking for selecting those points that are _close to_ the line (within a given error) rather than those which fall _directly_ on the drawn line, is this correct? Perhaps the easiest solution may then be to use a selector similar to the [lasso selection example](https://matplotlib.org/3.3.2/gallery/widgets/lasso_selector_demo_sgskip.html?highlight=lasso%20selector%20demo) and then "reverse mapping" those data points to your image-ids? – Asmus Nov 05 '20 at 10:27
  • Thanks for your reply, yes you got it right, but will select relatively small error, to reduce displayed images. – jpschreiter Nov 05 '20 at 10:43
  • Have you already checked out the demonstrations of the matplotlib selector widgets, e.g. [the polygon selector](https://matplotlib.org/3.3.2/gallery/widgets/polygon_selector_demo.html) and [the EllipseSelector](https://matplotlib.org/3.3.2/api/widgets_api.html#matplotlib.widgets.EllipseSelector)? – Asmus Nov 05 '20 at 10:48
  • Will try your solution, thanks – jpschreiter Nov 05 '20 at 11:00
  • Tested the lasso selection, Problem I'm facing is that it is not really interactive, you can select the datapoints which will work for me, returning the coordinates. So i can select indices in my embedding and select the images to be displayed. But after one selection i have to close the plot an cannot display any other selection. – jpschreiter Nov 05 '20 at 11:06
  • Check out more of the [documented code samples](https://matplotlib.org/3.3.2/api/widgets_api.html#matplotlib.widgets.EllipseSelector); it includes code showing how to connect a key press event (see: `fig.canvas.mpl_connect('key_press_event', toggle_selector)` and you should be able to deactivate / reset the lasso tool (or any other widget) through calls like `set_active(False)` , `set_visible(False)` and finally redrawing the figure with `ax.figure.canvas.draw()`. Also check out [this answer](https://stackoverflow.com/a/31926249/565489) for the nice "Highlighter" class. – Asmus Nov 05 '20 at 15:07

0 Answers0