1

I am doing a scatterplot where I want to click on the individual points to do something. This is like the existing example code.

https://matplotlib.org/examples/event_handling/pick_event_demo.html

I have implemented an on_pick method

def on_pick(event):
    ind = event.ind
    for i in ind:
        ...do something here with blue or red data...

However, I am stuck because I putting multiple series (red and blue) in the same plot

fig, ax = plt.subplots()
ax.set_title('click on a point...')
line, = ax.plot(red_xs, red_ys, 'o', picker=5, color='red') 
line, = ax.plot(blue_xs, blue_ys, 'o', picker=5, color='blue')  

The event.ind is a collection of integers. They are indexes into a series. However, there seems to be no way to determine which series they are an index into.

There must be a way to do this. Does anyone know the trick?

Thank you Peter

2 Answers 2

2

Here is the solution I found. It seems clunky, but maybe it is the best one.

By writing a "custom picker" one can add meta data to the event. Here I add "ind" and "series"

def picker(self,line,mouseevent,series):
    if mouseevent.xdata is None:
        return False, dict()
    xdata = line.get_xdata()
    ydata = line.get_ydata()
    maxd = 0.05
    d = np.sqrt((xdata - mouseevent.xdata) ** 2. + (ydata - mouseevent.ydata) ** 2.)

    ind = np.nonzero(np.less_equal(d, maxd))
    if len(ind):
        props = dict(ind=ind, series=series)
        return True, props
    else:
        return False, dict()

One one can then attach a different "custom picker" to each scatter plot

    line, = ax.plot(self.red['False'], self.red['True'], 'o', picker=lambda line,mouseevent: self.picker(line,mouseevent,self.red), color='red')
    line, = ax.plot(self.blue['False'], self.blue['True'], 'o', picker=lambda line,mouseevent: self.picker(line,mouseevent,self.blue), color='blue')

And then pull the metadata off in the on_pick() function

fig.canvas.mpl_connect('pick_event', lambda e: self.on_pick(e))

...

def on_pick(self,event):
    for i in event.ind:
        for j in i:
            series = event.series
            ...do something with item j of series...
Sign up to request clarification or add additional context in comments.

Comments

2

The pick_event_demo you link to acutally tells you how to know which line is which. It says

thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind

So ind will index whatever thisline is.

To give a more thorough example

line1, = ax.plot(self.red['False'], self.red['True'], 'o', picker=5, color='red') 
line2, = ax.plot(self.blue['False'], self.blue['True'], 'o', picker=5, color='blue') 

dic = {line1 : self.red, line2 : self.blue}

def on_pick(self,event):
    series = dic[event.artist]
    # do something with series
    for i in event.ind:
         print(series[i])


fig.canvas.mpl_connect('pick_event', on_pick)

3 Comments

Thank you IOBE. I think I wasn't clear in my question. I need to attach additional meta-data to the event, which in my case if the Pandas dataframe that generated the x's and y's. I found one way to do it. Maybe you know of a better way.
If your solution works, that's fine. My point was that you already have the data available as xdata and ydata inside the on_pick function. If instead you need the original data, you may use a simple mapping of the artist to the original data. I updated the answer to show how that could be done.
This is a good answer, event.artist actually shows which series the event is called on.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.