Picker Event to display legend labels in matplotlib Ver. 3

Question:

Continue on from previous question .. Here

This time I need to plot two sets of data with different sizes on the same plot. The issue is, since the two sets of data have different sizes, some points will fall out of index. When I pick on certain point(s), I’ll get a warning like this:

  File "C:ProgramDataAnaconda3libsite-packagesmatplotlibcbook__init__.py", line 224, in process
    func(*args, **kwargs)
  File "C:UsersU240335.spyder-py3Spyder_Dingyiuntitled0.py", line 42, in onpick
IndexError: index 16 is out of bounds for axis 0 with size 10
Traceback (most recent call last):
  File "C:ProgramDataAnaconda3libsite-packagesmatplotlibcbook__init__.py", line 224, in process
    func(*args, **kwargs)
  File "C:UsersU240335.spyder-py3Spyder_Dingyiuntitled0.py", line 42, in onpick
IndexError: index 17 is out of bounds for axis 0 with size 10

And I would also like to show the value of the point (including the label) on the plot of which the cursor is clicking on and be able to do something like "clear all" after marking some points on the plot.

Here’s the full code:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random


a = np.random.uniform(0, 20, 30)
b = np.random.uniform(0, 20, 30)

x = np.random.uniform(0, 20, 30)
y = np.random.uniform(0, 20, 30)
z = [0]*10 + [1]*20
random.shuffle(z)

# x y data and legend labels
df1 = pd.DataFrame({"x1": a, "y1": b, 'z': z})

df2 = pd.DataFrame({"x2": x, "y2": y, 'z': z})

x1 = df1['x1'].loc[df1['z']==0].reset_index(drop=True).values
y1 = df1['y1'].loc[df1['z']==0].reset_index(drop=True).values

x2 = df2['x2'].loc[df1['z']==1].reset_index(drop=True).values
y2 = df2['y2'].loc[df1['z']==1].reset_index(drop=True).values


def overlay_plot2(x1,y1,x2,y2,title,xlabel,ylabel):
    
    # define the picker event
    def onpick(event):
        ind = event.ind

   
        print('n%s:' % xlabel, x1[ind] if event.artist.get_label() == '0' else x2[ind], # use event.artist.getlabel() when labels are made
              '%s:' % ylabel, y1[ind] if event.artist.get_label() == '1' else y2[ind],
              event.artist.get_label())
    # plot
    fig, ax = plt.subplots(figsize=(8, 6), dpi = 120)
    ax.scatter(x1, y1, c='b', label = '0',s=14, marker='x', picker=True)
    ax.scatter(x2, y2, c='r', label = '1',s=14, marker='o', picker=True)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.legend(
              loc="center left", 
              bbox_to_anchor=(1, 0.5),
              )
    ax.ticklabel_format(useOffset=False)
    ax.tick_params(axis = 'x',labelrotation = 45)
    plt.tight_layout()
    
    # call the event
    fig.canvas.mpl_connect('pick_event', onpick)
    

overlay_plot2(x1,y1,x2,y2,"title","xlabel","ylabel")
Asked By: Jack.D

||

Answers:

Before going too into the weeds with customizing picker events, have you taken a look at mplcursors? There is a fantastic on hover feature that may be more of what you need. You can simply do mplcursors.cursor(hover=True) to give a basic annotation with x, y, and label values. Or, you can customize the annotations. Here is some code that I use for one of my projects where I’ve customized everything (color, text, arrow, etc.). Maybe you can find some use for it as well?

Do at least look at my comment about how to correct your index error. If the below isn’t what you want, give this answer a read and it should point you in the right direction on how to annotate on click.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
import mplcursors


a = np.random.uniform(0, 20, 30)
b = np.random.uniform(0, 20, 30)

x = np.random.uniform(0, 20, 30)
y = np.random.uniform(0, 20, 30)
z = [0]*10 + [1]*20
random.shuffle(z)

# x y data and legend labels
df1 = pd.DataFrame({"x1": a, "y1": b, 'z': z})

df2 = pd.DataFrame({"x2": x, "y2": y, 'z': z})

x1 = df1['x1'].loc[df1['z']==0].reset_index(drop=True).values
y1 = df1['y1'].loc[df1['z']==0].reset_index(drop=True).values

x2 = df2['x2'].loc[df1['z']==1].reset_index(drop=True).values
y2 = df2['y2'].loc[df1['z']==1].reset_index(drop=True).values


def overlay_plot2(x1,y1,x2,y2,title,xlabel,ylabel):
    
    # define the picker event
    def onpick(event):
        ind = event.ind

        print('n%s:' % xlabel, x1[ind] if event.artist.get_label() == '0' else x2[ind], # use event.artist.getlabel() when labels are made
              '%s:' % ylabel, y1[ind] if event.artist.get_label() == '0' else y2[ind],
              event.artist.get_label())
    # plot
    fig, ax = plt.subplots(figsize=(8, 6), dpi = 120)
    ax1 = ax.scatter(x1, y1, c='b', label = '0',s=14, marker='x', picker=True)
    ax2 = ax.scatter(x2, y2, c='r', label = '1',s=14, marker='o', picker=True)
    #mplcursors.cursor(hover=True)
    @mplcursors.cursor(ax1, hover=2).connect("add")
    def _(sel):
        sel.annotation.set_text('X Value: {}nY Value: {}nLabel: {}'.format(round(sel.target[0],2), round(sel.target[1], 2), sel.artist.get_label()))
        sel.annotation.get_bbox_patch().set(fc="blue", alpha=0.5)
        sel.annotation.arrow_patch.set(arrowstyle="-|>", connectionstyle="angle3", fc="black", alpha=.5)
    @mplcursors.cursor(ax2, hover=2).connect("add")
    def _(sel):
        sel.annotation.set_text('X Value: {}nY Value: {}nLabel: {}'.format(round(sel.target[0],2), round(sel.target[1], 2), sel.artist.get_label()))
        sel.annotation.get_bbox_patch().set(fc="red", alpha=0.5)
        sel.annotation.arrow_patch.set(arrowstyle="-|>", connectionstyle="angle3", fc="black", alpha=.5)
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.legend(
              loc="center left", 
              bbox_to_anchor=(1, 0.5),
              )
    ax.ticklabel_format(useOffset=False)
    ax.tick_params(axis = 'x',labelrotation = 45)
    plt.tight_layout()
    
    # call the event
    fig.canvas.mpl_connect('pick_event', onpick)
    plt.show()
    

overlay_plot2(x1,y1,x2,y2,"title","xlabel","ylabel")

Example Output Graphs (my mouse is hovering over these points):

enter image description here
enter image description here

Answered By: Michael S.
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.