4

Here is the problem I would like to solve: I would like to be able to interactively (i) remove scatter points (grey dots), (ii) add new scatter points, by clicking on the plot.

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(16,4))
a = np.sin(range(100))*np.random.normal(20,10,100)
b = [ 5, 15, 25, 30, 40, 50, 75, 85]

aa = plt.plot(range(len(a)),a,color='red')
bb = plt.scatter(b,a[b],color='grey',s=50)

enter image description here

themachinist
  • 1,323
  • 2
  • 16
  • 20
  • 1
    Since this is not a code writing service, you may start by looking at the [event handling](http://matplotlib.org/users/event_handling.html) page. – ImportanceOfBeingErnest Mar 22 '17 at 09:45
  • 1
    i think my question is not unlike most questions found on stackoverflow. i think you're being a little harsh. i've been reading the event handling page, but would appreciate some direction from fellow users. – themachinist Mar 22 '17 at 09:51
  • @themachinist - I appreciate that you have at least done some research on event handling, but surely you could have followed that up with an attempt to write some code, however bad. Then you can describe exactly what it isn't doing that you want it to (or what it _is_ doing that you _don't_ want it to ;-)) and you aren't subject to a barrage of opinion. – holdenweb Mar 22 '17 at 16:37
  • Thanks for the advice. My humble opinion is that as long as a Q is clear, it adds value for others, with or without showing failed attempts to solve it, e.g. http://stackoverflow.com/questions/4700614/how-to-put-the-legend-out-of-the-plot. If my Q wasn't clear than this is my fault, and i agree sometimes including an attempt to solve it can help clariy the question. For the record i've decided that i'm going to show my failed attempts from now on, as a means of avoiding any hard feelings. – themachinist Mar 23 '17 at 07:39
  • As for whether SO is a code-writing service, one could construe much of the content as code that was written for the community. I've now uploaded an answer to my initial question. – themachinist Mar 23 '17 at 07:39
  • 1
    Well done! So if you want a better way to do it, accept your own answer and then post a new question asking how to improve the (working, well done again!) code you wrote. – holdenweb Mar 23 '17 at 12:16

1 Answers1

2

This may not be elegant, but it works. Left-click to add point, right-click to remove point.

import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots(figsize=(16,4))
a_input = np.sin(range(100))*np.random.normal(20,10,100)
b_input = [ 5, 15, 25, 30, 40, 50, 75, 85]

a = plt.plot(range(len(a_input)),a_input,color='red')[0]
b = plt.scatter(b_input,a_input[b_input],color='grey',s=50,picker=5)

def add_or_remove_point(event):
    global a
    xydata_a = np.stack(a.get_data(),axis=1)
    xdata_a = a.get_xdata()
    ydata_a = a.get_ydata()
    global b
    xydata_b = b.get_offsets()
    xdata_b = b.get_offsets()[:,0]
    ydata_b = b.get_offsets()[:,1]

    #click x-value
    xdata_click = event.xdata
    #index of nearest x-value in a
    xdata_nearest_index_a = (np.abs(xdata_a-xdata_click)).argmin()
    #new scatter point x-value
    new_xdata_point_b = xdata_a[xdata_nearest_index_a]
    #new scatter point [x-value, y-value]
    new_xydata_point_b = xydata_a[new_xdata_point_b,:]

    if event.button == 1:
        if new_xdata_point_b not in xdata_b:

            #insert new scatter point into b
            new_xydata_b = np.insert(xydata_b,0,new_xydata_point_b,axis=0)
            #sort b based on x-axis values
            new_xydata_b = new_xydata_b[np.argsort(new_xydata_b[:,0])]
            #update b
            b.set_offsets(new_xydata_b)
            plt.draw()
    elif event.button == 3:
        if new_xdata_point_b in xdata_b:
            #remove xdata point b
            new_xydata_b = np.delete(xydata_b,np.where(xdata_b==new_xdata_point_b),axis=0)
            print(new_xdata_point_b)
            #update b
            b.set_offsets(new_xydata_b)
        plt.draw()

fig.canvas.mpl_connect('button_press_event',add_or_remove_point)
themachinist
  • 1,323
  • 2
  • 16
  • 20