Select n data points from plot - python

I want to select points by clicking om them in a plot and store the point in an array. I want to stop selecting points after n selections, by for example pressing a key. How can I do this? This is what I have so far.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click on points')
line, = ax.plot(np.random.rand(100), 'o', picker=5) # 5 points tolerance
def onpick(event):
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
points = tuple(zip(xdata[ind], ydata[ind]))
print('onpick points:', points)
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()

To have GUI functionality, you will have to embed the plot in a GUI frame; however, there is a simple way to limit the number of selected items:
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click on points')
line, = ax.plot(np.random.rand(100), 'o', picker=5) # 5 points tolerance
points = []
n = 5
def onpick(event):
if len(points) < n:
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
point = tuple(zip(xdata[ind], ydata[ind]))
points.append(point)
print('onpick point:', point)
else:
print('already have {} points'.format(len(points)))
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
Example output:
onpick point: ((54.0, 0.68482963903475647),)
onpick point: ((54.0, 0.68482963903475647),)
onpick point: ((54.0, 0.68482963903475647),)
onpick point: ((54.0, 0.68482963903475647),)
onpick point: ((54.0, 0.68482963903475647),)
already have 5 points
If you want to select unique points, you can use a set to store them instead of a list.

Related

Drawing separate lines on a Matplotlib canvas and writing coordinates to a dataframe

I took the solution from here: matplotlib onclick event repeating
I want to change this code so when I click the canvas twice, the line is finished on the second click. The third click should be the first point of a completely new line rather than a continuation.
Also, is there any way to write the coordinates of x,y and x1,y1 of each line that is created to a dataframe?
code:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
# Plot some random data
#values = np.random.rand(4,1);
#graph_1, = ax.plot(values, label='original curve')
graph_2, = ax.plot([], marker='o')
# Keep track of x/y coordinates
xcoords = []
ycoords = []
def onclick(event):
xcoords.append(event.xdata)
ycoords.append(event.ydata)
# Update plotted coordinates
graph_2.set_xdata(xcoords)
graph_2.set_ydata(ycoords)
# Refresh the plot
fig.canvas.draw()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()
Thanks in advance.
This should get you started:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
coords = []
current_coords = []
def onclick(event):
global current_coords
if event.inaxes:
current_coords.append((event.xdata, event.ydata))
if len(current_coords) == 2:
ax.plot([current_coords[0][0], current_coords[1][0]],
[current_coords[0][1], current_coords[1][1]], 'ro-')
coords.append([current_coords[0], current_coords[1]])
current_coords[:] = []
# Refresh the plot
fig.canvas.draw()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()
print(coords)
the variable coords contains a list of coordinates. Each item is a list [(x0,y0),(x1,y1)]
EDIT
This version of the code only shows one line, and responds to both mouse clicks and pressing the 'a' key on the keyboad
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
coords = []
current_coords = []
l, = ax.plot([], [], 'ro-')
def onclick(event):
global current_coords
if event.inaxes:
current_coords.append((event.xdata, event.ydata))
if len(current_coords) == 2:
l.set_data([current_coords[0][0], current_coords[1][0]],
[current_coords[0][1], current_coords[1][1]])
coords.append([current_coords[0], current_coords[1]])
current_coords[:] = []
# Refresh the plot
fig.canvas.draw()
def onkey(event):
if event.key == 'a':
onclick(event)
cid = fig.canvas.mpl_connect('button_press_event', onclick)
cid2 = fig.canvas.mpl_connect('key_press_event', onkey)
plt.show()
print(coords)

expert's opinion needed to solve a problem while using mouse click Event or on_key 'delete' in Matplolib to delete selected points in a scatter plot

I am trying to write a script to interactively plot a scatter plot using matplotlib. It is important to me to delete some points by mouse click event or delete button on the keyboard.
My goal is to clean the plot from the undesired points and generate a new dataframe with the clean points. I spent the whole day trying to figure it out and could write this script.
I would appreciate any suggestions.
import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [2, 5, 6, 5]
def onpick(event):
this_artist = event.artist
print(this_artist)
plt.gca().picked_object = this_artist
def on_key(event):
if event.key == u'delete':
ax = plt.gca()
if ax.picked_object:
ax.picked_object.remove()
ax.picked_object = None
ax.figure.canvas.draw()
fig, ax = plt.subplots()
ax= plt.scatter(x,y)
fig.canvas.mpl_connect('pick_event', onpick)
cid = fig.canvas.mpl_connect('key_press_event', on_key)
plt.show()
Somehow I used (ind) to delete points according to the index. But it is still not working.
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
x=[1,2,3,4]
y=[2,4,5,8]
frame = { 'x': x, 'y' : y}
df = pd.DataFrame(frame)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click on points')
line, = ax.plot(df.x, df.y, 'o', picker=5)
def onpick(event):
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
points = tuple(zip(xdata[ind], ydata[ind]))
print('onpick points:', ind)
line.remove(ind)
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
any solution?

Using button_press_event to draw seperate lines on a plot

I would like to use button_press_events to draw lines on a plot.
The following code does that, but the line coordinates follows on each other.
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
# Plot some random data
values = np.random.rand(4,1);
graph_1, = ax.plot(values, label='original curve')
graph_2, = ax.plot([], marker='.')
# Keep track of x/y coordinates
xcoords = []
ycoords = []
def onclick(event):
xcoords.append(event.xdata)
ycoords.append(event.ydata)
# Update plotted coordinates
graph_2.set_xdata(xcoords)
graph_2.set_ydata(ycoords)
# Refresh the plot
fig.canvas.draw()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()
How do I separate the events so every second click results in a separate line?
Something like this should do the trick.
Note that there are many things that should be improved in this code, such as detecting whether the click was detected inside or outside the axes, but it should put you on the right track.
fig = plt.figure()
ax = fig.add_subplot(111)
# Plot some random data
values = np.random.rand(4,1);
graph_1, = ax.plot(values, label='original curve')
# Keep track of x/y coordinates
lines = []
xcoords = []
ycoords = []
def onclick(event):
xcoords.append(event.xdata)
ycoords.append(event.ydata)
if len(xcoords)==2:
lines.append(ax.plot(xcoords,ycoords,'.-'))
xcoords[:] = []
ycoords[:] = []
# Refresh the plot
fig.canvas.draw()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

Remove annotation while keeping plot matplotlib

I'm producing a series of scatterplots, where I keep most of the plot (besides the scatter plot) between each plot. This is done like so: Keeping map overlay between plots in matplotlib
Now I want to add annotation to the plot:
for j in range(len(n)):
plt.annotate(n[j], xy = (x[j],y[j]), color = "#ecf0f1", fontsize = 4)
However, this annotation stays on the plot between plots. How can I clear the annotation after each figure is saved?
You can remove an artist using remove().
ann = plt.annotate (...)
ann.remove()
After removal it may be necessary to redraw the canvas.
Here is a complete example, removing several annotations within an animation:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
f = lambda x: np.sin(x)
line, = ax.plot(x, f(x))
scat = plt.scatter([], [], s=20, alpha=1, color="purple", edgecolors='none')
ann_list = []
def animate(j):
for i, a in enumerate(ann_list):
a.remove()
ann_list[:] = []
n = np.random.rand(5)*6
scat.set_offsets([(r, f(r)) for r in n])
for j in range(len(n)):
ann = plt.annotate("{:.2f}".format(n[j]), xy = (n[j],f(n[j])), color = "purple", fontsize = 12)
ann_list.append(ann)
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=20, interval=360)
ani.save(__file__+".gif",writer='imagemagick', fps=3)
plt.show()

Preventing execution until points are selected on matplotlib graph

I want to select 4 points on a matplotlib graph and operate on those points as soon as 4 four points are clicked on.
The code below will indeed store the 4 points in the variable points but does not wait for the four points to be selected. I tried adding a for loop and tried threading here but neither option worked. How can I solve this problem?
fig = plt.figure()
ax = fig.add_subplot(111)
image = np.load('path-to-file.npy')
tfig = ax.imshow(image)
points = []
def onclick(event):
global points
points.append((event.xdata, event.ydata))
cid = fig.canvas.mpl_connect('button_press_event', onclick)
# this line will cause an error because the four points haven't been
# selected yet
firstPoint = points[0]
In this case, you might consider using plt.ginput instead of "rolling your own".
As a quick example:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set(title='Select 4 points')
xy = plt.ginput(4)
x, y = zip(*xy)
ax.fill(x, y, color='lightblue')
ax.plot(x, y, ls='', mfc='red', marker='o')
plt.show()

Categories