1

I have the application with the following requirement.

  1. If I click the plot, it will display more information in the below plot.
  2. If I click the plot on few DATAPOINTS, it should open a text/CSV file say C:\Users\Dm\Desktop\sample.txt in notepad/excel.

The first requirement is working fine, but I am quite struggling in the 2nd requirement.

The code I have so far for 1st requirement:

import numpy as np


class PointBrowser(object):
    """
    Click on a point to select and highlight it -- the data that
    generated the point will be shown in the lower axes.  Use the 'n'
    and 'p' keys to browse through the next and previous points
    """

    def __init__(self):
        self.lastind = 0

        self.text = ax.text(0.05, 0.95, 'selected: none',
                            transform=ax.transAxes, va='top')
        self.selected, = ax.plot([xs[0]], [ys[0]], 'o', ms=12, alpha=0.4,
                                 color='yellow', visible=False)

    def onpress(self, event):
        if self.lastind is None:
            return
        if event.key not in ('n', 'p'):
            return
        if event.key == 'n':
            inc = 1
        else:
            inc = -1

        self.lastind += inc
        self.lastind = np.clip(self.lastind, 0, len(xs) - 1)
        self.update()

    def onpick(self, event):

        if event.artist != line:
            return True

        N = len(event.ind)
        if not N:
            return True

        # the click locations
        x = event.mouseevent.xdata
        y = event.mouseevent.ydata

        distances = np.hypot(x - xs[event.ind], y - ys[event.ind])
        indmin = distances.argmin()
        dataind = event.ind[indmin]

        self.lastind = dataind
        self.update()

    def update(self):
        if self.lastind is None:
            return

        dataind = self.lastind

        ax2.cla()
        ax2.plot(X[dataind])

        ax2.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f' % (xs[dataind], ys[dataind]),
                 transform=ax2.transAxes, va='top')
        ax2.set_ylim(-0.5, 1.5)
        self.selected.set_visible(True)
        self.selected.set_data(xs[dataind], ys[dataind])

        self.text.set_text('selected: %d' % dataind)
        fig.canvas.draw()


if __name__ == '__main__':
    import matplotlib.pyplot as plt

    X = np.random.rand(100, 200)
    xs = np.mean(X, axis=1)
    ys = np.std(X, axis=1)

    fig, (ax, ax2) = plt.subplots(2, 1)
    ax.set_title('click on point to plot time series')
    line, = ax.plot(xs, ys, 'o', picker=5)  # 5 points tolerance

    browser = PointBrowser()

    fig.canvas.mpl_connect('pick_event', browser.onpick)
    fig.canvas.mpl_connect('key_press_event', browser.onpress)

    plt.show()

Questions:

  1. Is it possible to achieve this? but I didn't get a single clue after research.

However, before coming up with some sort of convoluted solution to my problem, I figured I would ask the experts and see if there is something I'm missing. Unfortunately, I cannot change this paradigm easily without significant re-factoring.

Thanks in advance. I really appreciate example code snippets.

9
  • What should happen with the csv file? Commented Mar 26, 2016 at 13:28
  • @PadraicCunningham, It should open csv file/text file, when I click the point. Problem I am facing is How to get control data points on clicking the plots Commented Mar 26, 2016 at 14:06
  • So something like the user picks > 1 points maybe using cntrl to signify the group they want, what do you want to put into the csv file? Or do you just want the file to open after the user has picked x amount of data points regardless? Commented Mar 26, 2016 at 14:10
  • @Padraic Cunningham User picks > 1 points maybe using control to signify the group, could please explain this more? After reading matplotlib doc not able to understand clearly, how do I control data points on clicking the plots? Commented Mar 26, 2016 at 14:22
  • I mean in regard to If I click the plot on few DATAPOINTS, do you want the user to be able to select x amount of datapoints and then do something with that data or what is the goal exactly, opening a csv file when the user clicks a few points is very easy to implement but I imagine there is more logic than that. Commented Mar 26, 2016 at 14:42

1 Answer 1

1
+50

This will write the data from each point to a csv file using a threading.Thread with the csv module and open the file with notepad after using a subprocess, we just need to add a new method to do the writing and change update slightly:

   import csv
   import threading
   from subprocess import call


    def write_csv(self, dataind, xs, ys):
        fle = 'mu=%1.3f_sigma=%1.3f.csv' % (xs[dataind], ys[dataind])
        print("Writing to {}".format(fle))
        with open(fle, "w") as f:
            wr = csv.writer(f)
            wr.writerow(X[dataind])
        call(["notepad", fle])


    def update(self):
        if self.lastind is None:
            return

        dataind = self.lastind
        ax2.cla()
        ax2.plot(X[dataind])
        ax2.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f' % (xs[dataind], ys[dataind]),
                 transform=ax2.transAxes, va='top')

        ax2.set_ylim(-0.5, 1.5)
        self.selected.set_visible(True)
        self.selected.set_data(xs[dataind], ys[dataind])
        self.text.set_text('selected: %d' % dataind)
        t = threading.Thread(target=self.write_csv, args=(dataind, xs, ys))
        t.start()
        fig.canvas.draw()

If you want the option of selecting multiple point and writing all those to a single file, this will allow you to choose a new file for each or if you hit ctrl while picking the points each point you pick while it is held will have it's data written to a single file, you can change the naming convention and how it is written to the csv, this is just a runnable example, I have added a few prints that should hopefully help to see what is happening:

import csv
import threading
from subprocess import call


class PointBrowser(object):
    """
    Click on a point to select and highlight it -- the data that
    generated the point will be shown in the lower axes.  Use the 'n'
    and 'p' keys to browse through the next and previous points
    """

    def __init__(self):
        self.lastind = 0

        self.text = ax.text(0.05, 0.95, 'selected: none',
                            transform=ax.transAxes, va='top')
        self.selected, = ax.plot([xs[0]], [ys[0]], 'o', ms=12, alpha=0.4,
                                 color='yellow', visible=False)
        self.cntrl = False
        self.lock = threading.Lock()

    def onpress(self, event):
        if event.key == "control":
            self.cntrl = True
            with self.lock:
                self.mult = []
        if self.lastind is None:
            return
        if event.key not in ('n', 'p'):
            return
        if event.key == 'n':
            inc = 1
        else:
            inc = -1

        self.lastind += inc
        self.lastind = np.clip(self.lastind, 0, len(xs) - 1)
        self.update()

    def onpick(self, event):
        if event.artist != line:
            return True

        N = len(event.ind)
        if not N:
            return True

        # the click locations
        x = event.mouseevent.xdata
        y = event.mouseevent.ydata

        distances = np.hypot(x - xs[event.ind], y - ys[event.ind])
        indmin = distances.argmin()
        dataind = event.ind[indmin]

        self.lastind = dataind
        self.update()

    def write_csv(self, dataind, xs, ys, rows=False):
        fle = 'mu=%1.3f_sigma=%1.3f.csv' % (xs[dataind], ys[dataind])
        print("Writing to {}".format(fle))
        with open(fle, "w") as f:
            wr = csv.writer(f)
            wr.writerow(X[dataind]) if not rows else wr.writerows(X[dataind])
        call(["gedit", fle])

    def write_csv_mult(self):
        print("Writing multiple data")
        with self.lock:
            with open('multi.csv', "w") as f:
                wr = csv.writer(f)
                wr.writerows(self.mult)
        call(["gedit", 'multi.csv'])

    def update(self):
        if self.lastind is None:
            return

        dataind = self.lastind
        ax2.cla()
        ax2.plot(X[dataind])
        ax2.text(0.05, 0.9, 'mu=%1.3f\nsigma=%1.3f' % (xs[dataind], ys[dataind]),
                 transform=ax2.transAxes, va='top')

        if self.cntrl:
            with self.lock:
                self.mult.append(X[dataind])

        ax2.set_ylim(-0.5, 1.5)
        self.selected.set_visible(True)
        self.selected.set_data(xs[dataind], ys[dataind])
        self.text.set_text('selected: %d' % dataind)

        if not self.cntrl:
            t = threading.Thread(target=self.write_csv, args=(dataind, xs, ys))
            t.start()

        fig.canvas.draw()

    def release(self, event):
        if event.key == "control":
            print("Finished selecting")
            t = threading.Thread(target=self.write_csv_mult)
            t.start()
            self.cntrl = False


if __name__ == '__main__':
    import matplotlib.pyplot as plt
    import numpy as np

    X = np.random.rand(100, 200)
    xs = np.mean(X, axis=1)
    ys = np.std(X, axis=1)

    fig, (ax, ax2) = plt.subplots(2, 1)
    ax.set_title('click on point to plot time series')
    line, = ax.plot(xs, ys, 'o', picker=5)  # 5 points tolerance

    browser = PointBrowser()
    fig.canvas.mpl_connect('pick_event', browser.onpick)
    fig.canvas.mpl_connect('key_release_event', browser.release)
    fig.canvas.mpl_connect('key_press_event', browser.onpress)

    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.