2

I am trying to use matplotlib LassoSelector to select some points from a scatter plot and produce a separate figure for selected points only. When I try to use another matplotlib widget on the second plot it doesn't work but there is no error or warning message. Below is a minimal example with LassoSelector and SpanSelector used.

I tried other widgets too; the Button widget displays the button but the action on the button press is not performed.

import numpy as np
from matplotlib.pyplot import *
from matplotlib.widgets import SpanSelector, LassoSelector
from matplotlib.path import Path

def onselect(verts):
    global xys,data

    #get indexes of selected points
    path = Path(verts)
    xysn = xys.get_offsets()
    ind = np.nonzero([path.contains_point(xy) for xy in xysn])[0]

    #plot the second figure
    fig=figure(2)
    ax=fig.add_subplot(111)
    ax.hist(data[:,0][ind],10)

    #this should be executed when SpanSelector is used
    def action(min,max):
        print min,max

    #try to do SpanSelector (this fails)
    span=SpanSelector(ax,action,'horizontal')

    show()

#initialize a figure
fig=figure(1)
ax=fig.add_subplot(111)

#create data
data=np.array([[1,6], [4,8],[0,4],[4,2],[9,6],[10,8],[2,2],[5,5],[0,4],[4,5]])

#plot data
xys=ax.scatter(data[:,0],data[:,1])

#select point by drawing a path around them
lasso = LassoSelector(ax, onselect=onselect)

show()
Ed Smith
  • 12,716
  • 2
  • 43
  • 55
janez
  • 21
  • 1

1 Answers1

0

matplotlib widgets are event driven, so wait for user input. The problem with you code is you are trying to create a new figure with a new event handler SpanSelector. I'm not sure if you can add new events as a result of previous ones and with SpanSelector commented out, I get the following error,

QCoreApplication::exec: The event loop is already running

So the new event, LassoSelector is not registered and user input is not picked up (and the new figure doesn't appear). It is better to create all figures and register all possible events at the beginning of the code. The following should be closer to what you want to do,

import numpy as np
from matplotlib.pyplot import *
from matplotlib.widgets import SpanSelector, LassoSelector
from matplotlib.path import Path

#this should be executed when LassoSelector is used
def onselect(verts):
    global xys,data

    #get indexes of selected points
    path = Path(verts)
    xysn = xys.get_offsets()
    ind = np.nonzero([path.contains_point(xy) for xy in xysn])[0]

    #Clear and update bar chart
    h, b = np.histogram(data[:,0][ind],10)
    for rect, bars in zip(rects, h):
        rect.set_height(bars)
    ax2.bar(mb, h, align='center')
    draw()

#this should be executed when SpanSelector is used
def action(min,max):
    print min,max

#initialize figures
fig1=figure(1)
ax1=fig1.add_subplot(111)

fig2=figure(2)
ax2=fig2.add_subplot(111)

#create data
data=np.array([[1,6],[4,8],[0,4],[4,2],[9,6],[10,8],[2,2],[5,5],[0,4],[4,5]])

#plot data
xys=ax1.scatter(data[:,0],data[:,1])

#Plot initial histogram of all data
h, b = np.histogram(data[:,0],10)
mb = [0.5*(b[i]+b[i+1]) for i in range(b.shape[0]-1)]
rects = ax2.bar(mb, h, align='center')

#Register lasso selector
lasso = LassoSelector(ax1, onselect=onselect)

#Register SpanSelector 
span=SpanSelector(ax2,action,'horizontal')

show()

Note, in order to update bar charts, it's a little more tricky than plots so I used this answer here Dynamically updating a bar plot in matplotlib

For some reason, the histogram figure 2 only updates when you click on it. I would consider using a single figure with two axes for this which may be easier to work with,

fig, ax = subplots(2,1)
ax1 = ax[0]; ax2 = ax[1]
Community
  • 1
  • 1
Ed Smith
  • 12,716
  • 2
  • 43
  • 55