<commit_before>from enum import Enum
from functools import partial

import pyqtgraph as pg

from PyQt5 import QtWidgets, QtGui

from graphysio import ui
from graphysio.writedata import exporter
from graphysio.plotwidgets import PlotWidget
from graphysio.algorithms.filters import savgol
from graphysio.algorithms.waveform import findPOIGreedy


class FixIndex(Enum):
    disabled = 'Disabled'
    minimum = 'Local Minimum'
    maximum = 'Local Maximum'
    sndderiv = '2nd derivative peak'


class POISelectorWidget(ui.Ui_POISelectorWidget, QtWidgets.QWidget):
    @staticmethod
    def buttonClicked(self, qbutton):
        fixval = FixIndex(qbutton.text())
        self.poiselectorwidget.fixvalue = fixval

    def __init__(self, series, parent):
        super().__init__(parent=parent)
        self.setupUi(self)
        self.parent = parent

        self.poiselectorplot = POISelectorPlot(series, parent=self)
        self.horizontalLayout.addWidget(self.poiselectorplot)

        buttonClicked = partial(self.buttonClicked, self)
        self.buttonGroup.buttonClicked.connect(buttonClicked)

    @property
    def menu(self):
        return {
            'Plot': {
                '&Import POIs': partial(
                    self.parent.launchOpenFile, datahandler=self.poiselectorplot.loadPOI
                )
            },
            'Export': {'&POI to CSV': self.poiselectorplot.exporter.poi},
        }


class POISelectorPlot(PlotWidget):
    pointkey = 'point'

    @staticmethod
    def mouseMoved(self, evt):
        pos = evt[0]  ## using signal proxy turns original arguments into a tuple
        if self.sceneBoundingRect().contains(pos):
            mousePoint = self.getViewBox().mapSceneToView(pos)
            self.vLine.setPos(mousePoint.x())

    @staticmethod
    def clicked(self, evt):
        button = evt.button()
        pos = self.vLine.value()
        if button == 1:
            correctedpos = self.fixpos(pos)
            self.curve.feetitem.addPointsByLocation(self.pointkey, [correctedpos])
        elif button == 2:
            self.curve.feetitem.removePointsByLocation(self.pointkey, [pos])

    def __init__(self, series, parent=None):
        super().__init__(name=series.name, parent=parent)
        self.vb.setMouseMode(self.vb.PanMode)
        self.setMenuEnabled(False)

        self.__sndderiv = None
        self.fixvalue = FixIndex.disabled

        self.curve = self.addSeriesAsCurve(series)
        self.exporter = exporter.POIExporter(self, self.name)

        pen = pg.mkPen('k', width=2)
        self.vLine = pg.InfiniteLine(angle=90, movable=False, pen=pen)
        self.addItem(self.vLine, ignoreBounds=True)

        mouseMoved = partial(self.mouseMoved, self)
        self.sigproxy = pg.SignalProxy(
            self.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved
        )
        clicked = partial(self.clicked, self)
        self.scene().sigMouseClicked.connect(clicked)

    def loadPOI(self, plotdata):
        columnname, ok = QtGui.QInputDialog.getItem(
            self, 'Select POI series', 'Load POI', plotdata.data.columns, editable=False
        )
        if not ok:
            return
        poiseries = plotdata.data[columnname].dropna().index
        self.curve.feetitem.addPointsByLocation(self.pointkey, poiseries)

    def fixpos(self, pos):
        # Need to go through get_loc, otherwise strange things happen
        correctedpos = pos
        xmin, xmax = self.vbrange
        if self.fixvalue is FixIndex.minimum:
            s = self.curve.series.loc[xmin:xmax]
            posprop = findPOIGreedy(s, pos, 'min')
            fixedposloc = s.index.get_loc(posprop, method='nearest')
        elif self.fixvalue is FixIndex.maximum:
            s = self.curve.series.loc[xmin:xmax]
            posprop = findPOIGreedy(s, pos, 'max')
            fixedposloc = s.index.get_loc(posprop, method='nearest')
        elif self.fixvalue is FixIndex.sndderiv:
            s = self.sndderiv.loc[xmin:xmax]
            posprop = findPOIGreedy(s, pos, 'max')
            fixedposloc = s.index.get_loc(posprop, method='nearest')
        else:
            s = self.curve.series.loc[xmin:xmax]
            fixedposloc = s.index.get_loc(pos, method='nearest')
        correctedpos = s.index[fixedposloc]
        return correctedpos

    @property
    def sndderiv(self):
        if self.__sndderiv is None:
            sndderiv = self.curve.series.diff().diff().iloc[1:]
            sndderiv, _ = savgol(sndderiv, self.curve.samplerate, (0.16, 2))
            self.__sndderiv = sndderiv.dropna()
        return self.__sndderiv
<commit_msg>Implement POI to new plot<commit_after>from enum import Enum
from functools import partial

import pandas as pd

import pyqtgraph as pg

from PyQt5 import QtWidgets, QtGui

from graphysio import ui
from graphysio.writedata import exporter
from graphysio.structures import PlotData
from graphysio.plotwidgets import PlotWidget
from graphysio.algorithms.filters import savgol
from graphysio.algorithms.waveform import findPOIGreedy


class FixIndex(Enum):
    disabled = 'Disabled'
    minimum = 'Local Minimum'
    maximum = 'Local Maximum'
    sndderiv = '2nd derivative peak'


class POISelectorWidget(ui.Ui_POISelectorWidget, QtWidgets.QWidget):
    @staticmethod
    def buttonClicked(self, qbutton):
        fixval = FixIndex(qbutton.text())
        self.poiselectorwidget.fixvalue = fixval

    def __init__(self, series, parent):
        super().__init__(parent=parent)
        self.setupUi(self)
        self.parent = parent

        self.poiselectorplot = POISelectorPlot(series, parent=self)
        self.horizontalLayout.addWidget(self.poiselectorplot)

        buttonClicked = partial(self.buttonClicked, self)
        self.buttonGroup.buttonClicked.connect(buttonClicked)

    def launchNewPlotFromPOIs(self):
        srcseries = self.poiselectorplot.curve.series
        poiidx = self.poiselectorplot.curve.feetitem.indices[self.poiselectorplot.pointkey]
        pois = srcseries[poiidx.dropna()].rename('poi')
        sname = f'{srcseries.name}-poi'
        df = pd.DataFrame({sname: pois})
        plotdata = PlotData(data=df, name=sname)
        self.parent.createNewPlotWithData(plotdata)

    @property
    def menu(self):
        return {
            'Plot': {
                'Import POIs': partial(
                    self.parent.launchOpenFile, datahandler=self.poiselectorplot.loadPOI
                ),
                'POIs to New Plot': self.launchNewPlotFromPOIs,
            },
            'Export': {'POI to CSV': self.poiselectorplot.exporter.poi},
        }


class POISelectorPlot(PlotWidget):
    pointkey = 'point'

    @staticmethod
    def mouseMoved(self, evt):
        pos = evt[0]  ## using signal proxy turns original arguments into a tuple
        if self.sceneBoundingRect().contains(pos):
            mousePoint = self.getViewBox().mapSceneToView(pos)
            self.vLine.setPos(mousePoint.x())

    @staticmethod
    def clicked(self, evt):
        button = evt.button()
        pos = self.vLine.value()
        if button == 1:
            correctedpos = self.fixpos(pos)
            self.curve.feetitem.addPointsByLocation(self.pointkey, [correctedpos])
        elif button == 2:
            self.curve.feetitem.removePointsByLocation(self.pointkey, [pos])

    def __init__(self, series, parent=None):
        super().__init__(name=series.name, parent=parent)
        self.vb.setMouseMode(self.vb.PanMode)
        self.setMenuEnabled(False)

        self.__sndderiv = None
        self.fixvalue = FixIndex.disabled

        self.curve = self.addSeriesAsCurve(series)
        self.exporter = exporter.POIExporter(self, self.name)

        pen = pg.mkPen('k', width=2)
        self.vLine = pg.InfiniteLine(angle=90, movable=False, pen=pen)
        self.addItem(self.vLine, ignoreBounds=True)

        mouseMoved = partial(self.mouseMoved, self)
        self.sigproxy = pg.SignalProxy(
            self.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved
        )
        clicked = partial(self.clicked, self)
        self.scene().sigMouseClicked.connect(clicked)

    def loadPOI(self, plotdata):
        columnname, ok = QtGui.QInputDialog.getItem(
            self, 'Select POI series', 'Load POI', plotdata.data.columns, editable=False
        )
        if not ok:
            return
        poiseries = plotdata.data[columnname].dropna().index
        self.curve.feetitem.addPointsByLocation(self.pointkey, poiseries)

    def fixpos(self, pos):
        # Need to go through get_loc, otherwise strange things happen
        correctedpos = pos
        xmin, xmax = self.vbrange
        if self.fixvalue is FixIndex.minimum:
            s = self.curve.series.loc[xmin:xmax]
            posprop = findPOIGreedy(s, pos, 'min')
            fixedposloc = s.index.get_loc(posprop, method='nearest')
        elif self.fixvalue is FixIndex.maximum:
            s = self.curve.series.loc[xmin:xmax]
            posprop = findPOIGreedy(s, pos, 'max')
            fixedposloc = s.index.get_loc(posprop, method='nearest')
        elif self.fixvalue is FixIndex.sndderiv:
            s = self.sndderiv.loc[xmin:xmax]
            posprop = findPOIGreedy(s, pos, 'max')
            fixedposloc = s.index.get_loc(posprop, method='nearest')
        else:
            s = self.curve.series.loc[xmin:xmax]
            fixedposloc = s.index.get_loc(pos, method='nearest')
        correctedpos = s.index[fixedposloc]
        return correctedpos

    @property
    def sndderiv(self):
        if self.__sndderiv is None:
            sndderiv = self.curve.series.diff().diff().iloc[1:]
            sndderiv, _ = savgol(sndderiv, self.curve.samplerate, (0.16, 2))
            self.__sndderiv = sndderiv.dropna()
        return self.__sndderiv

{'fraction_non_alphanumeric': 0.10282093213409649, 'fraction_numerical': 0.003066230580539657, 'mean_word_length': 2.577696526508227, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7041577', 'n_tokens_mistral': 3091, 'n_tokens_neox': 2975, 'n_words': 596}