2

I would like to implement a programe/function, where I could select the data from a scatter plot points and then store them into an array. Any tips on how to start or where to find more about the topic would be nice :).

3
  • 2
    matplotlib.org/1.3.1/api/widgets_api.html Making points selectable would be a start. This question will probably be closed unless you have anything more specific you need help with. Commented May 14, 2014 at 12:56
  • Also see mpldatacursor Commented May 14, 2014 at 13:50
  • a similar question, with an answer based on the backend wx, is here. It would be however nice to see an example of it that is backend independent, using widgets. This is after all a very common task. Commented May 14, 2014 at 13:53

1 Answer 1

0

Selection by either lasso or rectangle selection. Should be fairly easily extendable. Using only matplotlib widgets.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button, LassoSelector, RectangleSelector
from matplotlib.path import Path
from matplotlib.patches import Rectangle
from functools import partial

class LassoSelect(object):

    def __init__(self, ax, collection):
        self.canvas = ax.figure.canvas
        self.collection = collection
        self.xys = collection.get_offsets()
        self.lasso = LassoSelector(ax, onselect=self.onselect)
        self.setActive(False)

    def onselect(self, verts):
        path = Path(verts)
        ind = np.nonzero([path.contains_point(xy) for xy in self.xys])[0]
        fc = self.collection.get_facecolors()
        fc[ind, -1] = 1
        self.collection.set_facecolors(fc)
        self.canvas.draw_idle()

    def setActive(self, activate):
        if activate:
            self.lasso.active = True
        else:
            self.lasso.active = False


class RectangleSelect(object):

    def __init__(self, ax, collection):
        super(RectangleSelect, self).__init__()
        self.RS = RectangleSelector(ax, self.onselect,
                                    drawtype='box', useblit=True,
                                    button=[1, 3],  # don't use middle button
                                    minspanx=5, minspany=5,
                                    spancoords='pixels')

        self.canvas = ax.figure.canvas
        self.collection = collection
        self.xys = collection.get_offsets()
        self.setActive(False)

    def onselect(self, eclick, erelease):
        'eclick and erelease are the press and release events'
        x1, y1 = eclick.xdata, eclick.ydata
        x2, y2 = erelease.xdata, erelease.ydata
        x0 = min(x1, x2)
        y0 = min(y1, y2)
        width = abs(x2-x1)
        height = abs(y2-y1)
        rect = Rectangle((x0, y0), width, height)

        ind = np.nonzero([rect.contains_point(xy) for xy in self.xys])[0]
        fc = self.collection.get_facecolors()
        fc[ind, -1] = 1
        self.collection.set_facecolors(fc)
        self.canvas.draw_idle()

    def setActive(self, activate):
        if activate:
            self.RS.set_active(True)
        else:
            self.RS.set_active(False)


class DataSelector(object):

    activeColor = (0, 1, 0)
    inActiveColor = (1, 1, 1)

    def __init__(self, x, y):
        super(DataSelector, self).__init__()
        self.x, self.y = x, y
        self.fig, self.ax = plt.subplots()
        self.pts = self.ax.scatter(x, y)
        self.selected_pts = []
        self.addButtons()
        self.selectorWidgets = {'Rectangle': RectangleSelect(self.ax, self.pts),
                                'Lasso': LassoSelect(self.ax, self.pts)}

        self.fc = self.pts.get_facecolors()
        if len(self.fc) == 0:
            raise ValueError('Collection must have a facecolor')
        elif len(self.fc) == 1:
            self.fc = np.tile(self.fc, len(x)).reshape(len(x), -1)
        self.fc[:, -1] = 0.3
        self.pts.set_facecolors(self.fc)

    def resetSelection(self, event=None):
        self.fc[:, -1] = 0.3
        self.fig.canvas.draw()

    def exportSelection(self, event=None):
        ind = np.nonzero(self.fc[:, -1] != 0.3)[0]
        print self.x[ind], self.y[ind]

    def activateWidget(self, key, event):
        for widgetKey, widget in self.selectorWidgets.iteritems():
            if widgetKey == key:
                widget.setActive(True)
            else:
                widget.setActive(False)
        for ax, button in self.buttonAxes.iteritems():
            if ax == event.inaxes:
                button.color = self.activeColor
            else:
                button.color = self.inActiveColor
            button.hovercolor = button.color

    def addButtons(self):
        fig = self.fig
        fig.subplots_adjust(bottom=0.25)

        #Button text and callback
        buttons = [("Lasso", None), ("Rectangle", None), ('Reset', self.resetSelection), ('Export Selection', self.exportSelection)]

        nButtons = len(buttons)
        axBorders = np.linspace(0.25, 0.90, nButtons+1, endpoint=False)
        spacing = axBorders[1] - axBorders[0]
        self.buttonAxes = {}
        for i, btn in zip(axBorders, buttons):
            #[left, bottom, width, height]
            buttonAx = plt.axes([i, 0.1, spacing, 0.04])
            button = Button(buttonAx, btn[0], color=self.inActiveColor)
            if btn[1] is None:
                button.on_clicked(partial(self.activateWidget, btn[0]))
            else:
                button.on_clicked(btn[1])
            self.buttonAxes[buttonAx] = button

x, y = np.random.random((2, 25))*10

dataSelector = DataSelector(x, y)
plt.show()
Sign up to request clarification or add additional context in comments.

Comments

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.