Matplotlib / python clickable points
Asked Answered
E

3

12

I have a bunch of time series data with points every 5 seconds. So, I can create a line plot and even smooth the data to have a smoother plot. The question is, is there any way in matplotlib or anything in python that will allow me to click on a valid point to do something? So, for example, I would be able to click on (10, 75) if that datum exists in my original data and then I would be able to do something in Python.

Any thoughts? Thanks.

Ecclesiastical answered 26/2, 2014 at 20:27 Comment(1)
yes, see the picker demosJemimah
P
20

To expand on what @tcaswell said, see the documentation here: http://matplotlib.org/users/event_handling.html

However, you might find a quick demo of pick events useful:

import matplotlib.pyplot as plt

def on_pick(event):
    artist = event.artist
    xmouse, ymouse = event.mouseevent.xdata, event.mouseevent.ydata
    x, y = artist.get_xdata(), artist.get_ydata()
    ind = event.ind
    print 'Artist picked:', event.artist
    print '{} vertices picked'.format(len(ind))
    print 'Pick between vertices {} and {}'.format(min(ind), max(ind)+1)
    print 'x, y of mouse: {:.2f},{:.2f}'.format(xmouse, ymouse)
    print 'Data point:', x[ind[0]], y[ind[0]]
    print

fig, ax = plt.subplots()

tolerance = 10 # points
ax.plot(range(10), 'ro-', picker=tolerance)

fig.canvas.callbacks.connect('pick_event', on_pick)

plt.show()

Exactly how you approach this will depend on what artist you're using (In other words, did you use ax.plot vs. ax.scatter vs. ax.imshow?).

Pick events will have different attributes depending on the artist selected. There will always be event.artist and event.mouseevent. Most artists that have individual elements (e.g. Line2Ds, Collections, etc) will have a list of the index of the items selected as event.ind.

If you'd like to draw a polygon and select points inside, see: http://matplotlib.org/examples/event_handling/lasso_demo.html#event-handling-example-code-lasso-demo-py

Paramorphism answered 26/2, 2014 at 21:26 Comment(0)
I
4

If in case you want to bind extra property to your artist object, for example you are plotting IMDB ratings of several movies, and you want to see by clicking on a point which movie it corresponds to, you can do that by adding a custom object to the point that you plotted, like this:

import matplotlib.pyplot as plt

class custom_objects_to_plot:
    def __init__(self, x, y, name):
        self.x = x
        self.y = y
        self.name = name

a = custom_objects_to_plot(10, 20, "a")
b = custom_objects_to_plot(30, 5, "b")
c = custom_objects_to_plot(40, 30, "c")
d = custom_objects_to_plot(120, 10, "d")

def on_pick(event):
    print(event.artist.obj.name)

fig, ax = plt.subplots()
for obj in [a, b, c, d]:
    artist = ax.plot(obj.x, obj.y, 'ro', picker=5)[0]
    artist.obj = obj

fig.canvas.callbacks.connect('pick_event', on_pick)

plt.show()

Now when you click one of the points on the plot, the name property of the corresponding object will be printed.

Interposition answered 10/9, 2018 at 15:23 Comment(1)
I am getting the following error: x, y = artist.get_xdata(), artist.get_ydata() AttributeError: 'PathCollection' object has no attribute 'get_xdata'Aeneas
Y
0

With respect to "a bunch of" in the question, I tested whether picking works with twin axes (no), multiple axes (yes), multiple figures (yes):

import numpy as np
from matplotlib.pyplot import subplots

def on_pick(e):
    print(e.artist.s, e.ind)

x = np.linspace(0, np.pi)

fig, ax = subplots(2)
ax[0].plot(x, np.cos(x), picker=5)[0].s = 'cos'
ax[1].plot(x, np.sin(x), picker=5)[0].s = 'sin'
fig.canvas.callbacks.connect('pick_event', on_pick)
fig.tight_layout()
fig.show()

fig, ax0 = subplots()
ax0.plot(x, np.tan(x), picker=5)[0].s = 'tan' # Won't work, masked by ax1.
ax0.set_ylim(-3, 3)
ax1 = ax0.twinx()
ax1.plot(x, np.sqrt(x), picker=5)[0].s = 'sqrt'
fig.canvas.callbacks.connect('pick_event', on_pick)
fig.tight_layout()
fig.show()

Example output:

cos [6]
sin [32]
sqrt [30]
Yumuk answered 17/5, 2023 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.