Beispiel #1
0
class Chart(QChartView):
    def __init__(self, barCount: int):
        super().__init__()

        self.chart = QChart()
        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)
        self.chart.setAnimationOptions(QChart.SeriesAnimations)
        self.chart.setBackgroundVisible(True)
        self.chart.legend().setVisible(False)

        self.series = QBarSeries()
        self.chart.addSeries(self.series)

        self.barValues = QBarSet('')
        self.series.append(self.barValues)
        for i in range(barCount):
            self.barValues << 0.

        self.xAxis = QBarCategoryAxis()
        self.chart.addAxis(self.xAxis, Qt.AlignBottom)
        self.series.attachAxis(self.xAxis)
        self.xAxis.setTitleText('yPlus ranges')

        self.yAxis = QValueAxis()
        self.chart.addAxis(self.yAxis, Qt.AlignLeft)
        self.series.attachAxis(self.yAxis)
        self.yAxis.setTitleText('% of surface area')
        self.yAxis.setRange(0, 100)

    def setBarRanges(self, pois: List[float]):
        for i in range(len(pois)):
            if i == 0:
                tag = 'lt ' + str(pois[0])
            elif i == len(pois) - 1:
                tag = 'gt ' + str(pois[-1])
            else:
                tag = str(pois[i]) + ' - ' + str(pois[i + 1])

            if not self.xAxis.count():
                self.xAxis.append(tag)
            else:
                self.xAxis.replace(self.xAxis.at(i), tag)

    def setBarValues(self, values: List[float]):
        assert len(values) == self.barValues.count()

        for i in range(len(values)):
            if not self.barValues.count():
                self.barValues.insert(i, 0.)
            else:
                self.barValues.replace(i, values[i])
Beispiel #2
0
class QtBarChart(QChartView):
    def __init__(self, spec):
        super().__init__(None)
        self.spec = spec
        self.chart = QChart()
        # self.chart.setTitle(str(self.spec.variables))
        self.chart.legend().hide()
        self.mainset = QBarSet("")
        self.mainset.append([0 for i in range(len(spec.variables))])
        self.mainset.setColor(
            QColor(spec.color[0], spec.color[1], spec.color[2]))
        self.series = QBarSeries()
        self.series.append(self.mainset)

        self.setMinimumWidth(400)
        self.setMinimumHeight(230)

        self.axis_x = QBarCategoryAxis()
        self.axis_y = QValueAxis()
        self.axis_x.append(spec.variables)
        self.chart.addSeries(self.series)
        self.chart.setAxisX(self.axis_x, self.series)
        self.chart.setAxisY(self.axis_y, self.series)

        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)

        self._updates_per_second = 10
        self._dataset = []

    def clear(self):
        self._dataset = []

    def update_data(self, dataset):
        data = []
        for d in dataset:
            data.append(d)
        self._dataset = data

    def redraw(self):
        if len(self._dataset) > 0:
            for i in range(len(self._dataset)):
                self.mainset.replace(i, self._dataset[i])
            self.axis_y.setRange(0, max(self._dataset))
Beispiel #3
0
class QtHistogram(QChartView):
    def __init__(self, spec):
        super().__init__(None)
        self.spec = spec
        self.chart = QChart()
        self.chart.setTitle(self.spec.variable)
        self.chart.legend().hide()

        self.mainset = QBarSet("")
        self.mainset.append([0] * len(spec.bins))
        self.mainset.setColor(
            QColor(spec.color[0], spec.color[1], spec.color[2]))
        self.series = QBarSeries()
        self.series.append(self.mainset)

        self.setMinimumWidth(400)
        self.setMinimumHeight(230)

        self.y_ranges = [0, 1, 5, 10, 25, 50, 100, 250, 500, 1000]
        self.max_y = 1000
        self.max_y_range = 1000
        self.lookback = 30
        self.recent_max_y = deque([self.max_y_range] * self.lookback)

        font = QtGui.QFont()
        font.setPixelSize(10)

        self.axis_x = QBarCategoryAxis()
        self.axis_x.setLabelsAngle(-90)
        self.axis_x.setLabelsFont(font)

        self.axis_y = QValueAxis()
        self.axis_y.setRange(0, self.max_y)
        self.axis_x.append(map(str, spec.bins))
        self.chart.addSeries(self.series)
        self.chart.setAxisX(self.axis_x, self.series)
        self.chart.setAxisY(self.axis_y, self.series)

        self.setChart(self.chart)
        self.setRenderHint(QPainter.Antialiasing)

        self._updates_per_second = 10
        self._dataset = []

    def clear(self):
        self._dataset = []

    def update_data(self, dataset):
        data = []
        for d in dataset:
            data.append(d)
        self._dataset = data

    def redraw(self):
        if len(self._dataset) > 0:
            for i in range(len(self._dataset)):
                self.mainset.replace(i, self._dataset[i])

            # Calculate max of current values
            max_y_range = max(self._dataset)

            # Store max value
            self.recent_max_y.appendleft(max_y_range)
            if len(self.recent_max_y) > self.lookback:
                self.recent_max_y.pop()

            # Set max based on the last 30 max values,
            # to avoid flickering
            self.max_y_range = max(self.recent_max_y)

            y_range = bisect.bisect_left(self.y_ranges, self.max_y_range)
            if y_range < len(self.y_ranges):
                self.max_y = self.y_ranges[y_range]
            elif max_y_range > self.max_y:
                self.max_y += self.max_y
            elif max_y_range < self.max_y / 2:
                self.max_y = self.max_y / 2

            self.axis_y.setRange(0, self.max_y)
class initial(QDialog):
    def __init__(self):
        super(initial, self).__init__()
        loadUi("main.ui", self)
        # definir elementos de la UI y acciones/funciones asociadas

        # creamos sets para mostrar resultados en el barchart

        self.setRecall = QBarSet("Recalls")
        self.setRecall.append([0, 0, 0])
        self.setAccurracy = QBarSet("Accurracy")
        self.setAccurracy.append([0, 0, 0])

        self.series = QBarSeries()

        # Elementos Tab Entrenamiento:
        # ===========================

        # Btn Insertar primeros datos entrenamiento
        self.trainingAddFilesBtn.clicked.connect(
            self.insertarNoticiasEntrenamientoDespoblacion)

        # Btn Insertar segundos datos entrenamiento
        self.trainingAddFilesBtn2.clicked.connect(
            self.insertarNoticiasEntrenamientoNoDespoblacion)

        # Btn Preprocesamiento de texto
        self.procesarTextoBtn.clicked.connect(self.procesarTextoEntrenamiento)

        # ComboBox selector de Modelo a entrenar
        # self.chooseModelComboBox.activated.connect(self.elegirModeloEntrenamiento)

        # añadir elementos al comboBox y sus valores asociados
        self.chooseModelComboBox.addItem("KNN", 1)
        self.chooseModelComboBox.addItem("Naive Bayes", 2)
        self.chooseModelComboBox.addItem("Decision Tree", 3)

        # Btn para entrenar el modelo seleccionado
        self.trainModelBtn.clicked.connect(self.entrenarModelo)

        # Elementos Tab Testeo:
        # ====================

        # Btn Insertar Datos Testeo
        self.testingFilesBtn.clicked.connect(self.insertarNoticiasTesteo)

        # Btn Seleccionar Modelo
        self.selectTestModelBtn.clicked.connect(self.elegirModeloTesteo)

        # Btn Mostrar Resultados
        self.testBtn.clicked.connect(self.mostrarResultados)

        # Tab Testeo
        #self.tabTest.clicked.connect(self.abrirTabTesteo)

        # nombre excel
        self.nombreresultadoexcel = ':)'

    # funciones
    # =========

    # abrir tab testeo
    def abrirTabTesteo(self):
        self.stepTipsField.setPlainText(
            "En esta pestaña puede realizar el testeo sobre un nuevo set de datos para un modelo ya existente."
        )

    # abrir dialog window para seleccionar los datos de entrenamiento
    def insertarNoticiasEntrenamientoDespoblacion(self):

        del desp[:]
        print(desp)
        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "Seleccionamos los directorios donde tenemos los archivos de texto que utilizaremos para entrenar nuestro modelo."
        )

        # abrir ventana seleccion archivos
        desp.append(self.openDialogBox())

        print(desp)
        #cambiar self.procesarTextoBtn a habilitado
        self.trainingAddFilesBtn2.setEnabled(True)

    # abrir dialog window para seleccionar los segundos datos de entrenamiento
    def insertarNoticiasEntrenamientoNoDespoblacion(self):

        del no_despoblacion[:]

        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "Seleccionamos los directorios donde tenemos los archivos de texto que utilizaremos para entrenar nuestro modelo."
        )

        # abrir ventana seleccion archivos
        no_despoblacion.append(self.openDialogBox())

        #cambiar self.procesarTextoBtn a habilitado
        self.procesarTextoBtn.setEnabled(True)

    # aplicar preprocesamiento de texto
    def procesarTextoEntrenamiento(self):
        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "El preprocesamiento a realizar consta de 4 etapas:\n1. Tokenizar: separar las palabras que componen un texto, obteniendo como resultado una secuencia de tokens.\n2. Normalización: se pasa a minúsculas tdoos los tokens.\n3.Filtrado de stopwords: en esta etapa eliminamos  aquellas palabras con poco valor semántico, denominadas stopwords.\n4.Stemming: extraemos el lexema de los tokens restantes  (un ejemplo sería ‘cas-’ para la palabra ‘casero’)"
        )

        del noticias[:]
        del clases[:]
        # bucle inserción de noticias mediante open
        ingresar_noticias(desp[0], noticias)
        ingresar_noticias(no_despoblacion[0], noticias)
        ingresar_noticias(desp[0], despoblacion)

        # Procesamiento de texto
        texto_procesado(processed_text_entrenamiento, noticias)

        # Creación de arreglo de clases
        crea_clases(clases, processed_text_entrenamiento, despoblacion)

        # cambiar self.trainModelBtn a habilitado
        self.trainModelBtn.setEnabled(True)

        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "El preprocesamiento a realizar consta de 4 etapas:\n1. Tokenizar: separar las palabras que componen un texto, obteniendo como resultado una secuencia de tokens.\n2. Normalización: se pasa a minúsculas tdoos los tokens.\n3.Filtrado de stopwords: en esta etapa eliminamos  aquellas palabras con poco valor semántico, denominadas stopwords.\n4.Stemming: extraemos el lexema de los tokens restantes  (un ejemplo sería ‘cas-’ para la palabra ‘casero’).\n====================\nEl preprocesamiento ha acabado"
        )

        # cambiar self.procesarTextoBtn a deshabilitado
        self.procesarTextoBtn.setEnabled(False)

    # abrir ventana seleccion archivos
    def openDialogBox(self):
        filenames = QFileDialog.getOpenFileNames()
        return filenames[0]

    # mostrar resultados testeo en nueva tabla
    def mostrarResultados(self):
        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "A continuación se muestra una tabla con los resultados de la clasificación realizada por el modelo seleccionado."
        )

        # para ocupar toda la tabla
        self.tableWidgetshowTest.horizontalHeader().setSectionResizeMode(
            QtWidgets.QHeaderView.Stretch)

        # resetear tabla
        self.tableWidgetshowTest.setRowCount(0)
        nombre = self.nombreresultadoexcel

        # mostrar contenido xlsx
        documento = xlrd.open_workbook(nombre + '.xlsx')
        df = documento.sheet_by_index(0)
        self.tableWidgetshowTest.setRowCount((df.nrows) - 1)
        self.tableWidgetshowTest.setColumnCount(2)
        for x in range(1, df.nrows):
            for y in range(2):
                print('x: ' + df.cell_value(x, y - 1))
                item = QTableWidgetItem()
                nombreArchivo = df.cell_value(x, y - 1).split("/")
                item.setText(nombreArchivo[len(nombreArchivo) - 1])
                self.tableWidgetshowTest.setItem(x - 1, y - 1, item)

    # insertar archivos fase testeo
    def insertarNoticiasTesteo(self):
        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "Seleccione los archivos que utilizará durante la fase de testeo.")

        # abrir ventana seleccion archivos
        filepaths = self.openDialogBox()

        #ingresar noticias
        ingresar_noticias(filepaths, nuevas)

        #Procesamiento de texto
        texto_procesado(processed_text_testeo, nuevas)

        # cambiar self.selectTestModelBtn a deshabilitado
        self.selectTestModelBtn.setEnabled(True)

        # cambiar self.testingFilesBtn a habilitado
        # self.testingFilesBtn.setEnabled(False)

    # seleccionar modelo fase testeo
    def elegirModeloTesteo(self):
        # cambiar texto campo descripcion
        self.stepTipsField.setPlainText(
            "Seleccione el diccionario .pk y modelo correspondiente .pk1.")

        # abrir ventana seleccion archivos
        modelopath = self.openDialogBox()

        if (len(modelopath) == 2):
            # cargar diccionario
            cv1 = cargar_modelo(modelopath[0])

            # cargar modelo
            modelo = cargar_modelo(modelopath[1])

            # aplicar tfidf
            X_testcv = tfid_fit(processed_text_testeo, cv1)

            # insertar predicciones
            predicciones = []
            for i in X_testcv:
                predicciones.append(prediccion(modelo, i))

            # crear dataframe
            df = pd.DataFrame(data=predicciones, index=nuevas)

            # nombrar archivo y exportar a excel
            archivo = modelopath[0]
            new_archivo = archivo.replace('modelos', 'resultados')
            nombre = new_archivo[:len(new_archivo) - 3]
            self.nombreresultadoexcel = nombre
            df.to_excel(nombre + ".xlsx", "Sheet1")

            # cambiar self.testBtn a habilitado
            self.testBtn.setEnabled(True)

            # cambiar texto campo descripcion
            self.stepTipsField.setPlainText(
                "Resultados exportados a la carpeta resultados en formato Excel."
            )

    # aplicar modelo NaiveBayes entrenamiento
    def entrenamientoNaiveBayes(self):
        # Proceso TFIDF
        X_traincv = cv.fit_transform(processed_text_entrenamiento)
        # Partición de datos
        X_train, X_test, Y_train, Y_test = train_test_split(X_traincv,
                                                            clases,
                                                            test_size=0.15,
                                                            random_state=324)

        #Modelos
        naive = naive_bayes(X_train, Y_train)
        print(naive)
        print(
            "####################### Test Score ##############################\n"
        )
        test_score(naive, X_train, Y_train)

        # Creamos los datos a testear
        Y_true_naive, Y_pred_naive = datos_test(Y_test, naive, X_test)

        # Datos de los modelos
        print(
            "###################### Accuracy ###############################\n"
        )
        accuracy(Y_true_naive, Y_pred_naive)

        self.setAccurracy.replace(
            1,
            accuracy_score(Y_true_naive, Y_pred_naive) * 100)

        print(
            "####################### Recall ##############################\n")
        print(recall_score(Y_true_naive, Y_pred_naive, average='macro'))
        self.setRecall.replace(
            1,
            recall_score(Y_true_naive, Y_pred_naive, average='macro') * 100)

        a = "Modelo Naive-Bayes\n==================\nRecall:" + str(
            recall_score(Y_true_naive, Y_pred_naive,
                         average='macro')) + "\nAccuracy: " + str(
                             accuracy_score(Y_true_naive, Y_pred_naive))
        self.stepTipsField.setPlainText(a)
        #llamar a funcion para actualizar los valores del Barchart
        self.appendResults()

        print(
            "\n###################### Matriz de confusion ###############################\n"
        )
        matrizconf(Y_true_naive, Y_pred_naive)

        #Guardamos modelo
        now = datetime.now()
        # dd/mm/YY H:M:S
        dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S")
        guardar_modelo('modelos/naive_' + dt_string, naive)
        with open('modelos/naive_' + dt_string + '.pk', 'wb') as f:
            pickle.dump(cv, f)

    # aplicar modelo Decision Tree entrenamiento
    def entrenamientoArbolDecision(self):
        # Proceso TFIDF
        X_traincv = cv.fit_transform(processed_text_entrenamiento)
        #Partición de datos
        X_train, X_test, Y_train, Y_test = train_test_split(X_traincv,
                                                            clases,
                                                            test_size=0.15,
                                                            random_state=324)

        #Modelos
        tree = decision_tree(X_train, Y_train)
        print(
            "####################### Test Score ##############################\n"
        )
        test_score(tree, X_train, Y_train)

        #Creamos los datos a testear
        Y_true_tree, Y_pred_tree = datos_test(Y_test, tree, X_test)

        #Datos de los modelos
        print(
            "###################### Accuracy ###############################\n"
        )
        accuracy(Y_true_tree, Y_pred_tree)

        #incluir nueva accurracy al set de resultados de NaiveBayes
        #self.setDTrees.append(accuracy_score(Y_true_tree, Y_pred_tree)*100)
        self.setAccurracy.replace(
            2,
            accuracy_score(Y_true_tree, Y_pred_tree) * 100)
        #llamar a funcion para actualizar los valores del Barchart

        print(
            "####################### Recall ##############################\n")
        print(recall_score(Y_true_tree, Y_pred_tree, average='macro'))
        #self.setDTrees.append(recall_score(Y_true_tree, Y_pred_tree, average='macro')*100)
        self.setRecall.replace(
            2,
            recall_score(Y_true_tree, Y_pred_tree, average='macro') * 100)
        a = "Modelo Arbol Decision\n=====================\nRecall:" + str(
            recall_score(Y_true_tree, Y_pred_tree,
                         average='macro')) + "\nAccuracy: " + str(
                             accuracy_score(Y_true_tree, Y_pred_tree))
        self.stepTipsField.setPlainText(a)
        self.appendResults()
        #Matriz confusion
        print(
            "\n###################### Matriz de confusion ###############################\n"
        )
        matrizconf(Y_true_tree, Y_pred_tree)

        now = datetime.now()
        # dd/mm/YY H:M:S
        dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S")
        guardar_modelo('modelos/tree_' + dt_string, tree)
        with open('modelos/tree_' + dt_string + '.pk', 'wb') as f:
            pickle.dump(cv, f)

    # aplicar modelo KNN
    def entrenamientoKnn(self):
        # Proceso TFIDF
        X_traincv = cv.fit_transform(processed_text_entrenamiento)
        #Partición de datos
        X_train, X_test, Y_train, Y_test = train_test_split(X_traincv,
                                                            clases,
                                                            test_size=0.15,
                                                            random_state=324)

        #Modelos
        modknn = knn(X_train, Y_train)

        print(
            "####################### Test Score ##############################\n"
        )
        test_score(modknn, X_train, Y_train)

        #Creamos los datos a testear
        Y_true_knn, Y_pred_knn = datos_test(Y_test, modknn, X_test)

        #Datos de los modelos
        print(
            "###################### Accuracy ###############################\n"
        )

        accuracy(Y_true_knn, Y_pred_knn)

        #llamar a funcion para actualizar los valores del Barchart
        self.setAccurracy.replace(0,
                                  accuracy_score(Y_true_knn, Y_pred_knn) * 100)

        print(
            "####################### Recall ##############################\n")
        print(recall_score(Y_true_knn, Y_pred_knn, average='macro'))

        #self.setDTrees.append(recall_score(Y_true_knn, Y_pred_knn, average='macro')*100)
        self.setRecall.replace(
            0,
            recall_score(Y_true_knn, Y_pred_knn, average='macro') * 100)
        a = "Modelo KNN\n===============\nRecall:" + str(
            recall_score(Y_true_knn, Y_pred_knn,
                         average='macro')) + "\nAccuracy: " + str(
                             accuracy_score(Y_true_knn, Y_pred_knn))
        self.stepTipsField.setPlainText(a)
        self.appendResults()
        #Matriz confusion
        print(
            "\n###################### Matriz de confusion ###############################\n"
        )
        matrizconf(Y_true_knn, Y_pred_knn)

        #Guardamos modelo
        now = datetime.now()
        # dd/mm/YY H:M:S
        dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S")
        guardar_modelo('modelos/knn_' + dt_string, modknn)
        with open('modelos/knn_' + dt_string + '.pk', 'wb') as f:
            pickle.dump(cv, f)

    # comprobar modelo seleccionado en comboBox
    def entrenarModelo(self):
        #cambiar texto en self.stepTipsField
        self.stepTipsField.setPlainText("Entrenando el modelo seleccionado...")

        #tomar valor actual del comboBox
        modelSelect = self.chooseModelComboBox.currentData()

        #no existe switch en python (o.o)
        if modelSelect == 1:
            self.entrenamientoKnn()

        if modelSelect == 2:
            self.entrenamientoNaiveBayes()

        if modelSelect == 3:
            self.entrenamientoArbolDecision()

    # add resultados entrenamiento y actualizar barchart
    def appendResults(self):
        #clear de series
        self.series = QBarSeries()

        #add sets de Accurracy y Recall de todos los modelos procesados a series
        self.series.append(self.setAccurracy)
        self.series.append(self.setRecall)

        # crear nuevo Chart
        chart = QChart()

        # add series al nuevo Chart
        chart.addSeries(self.series)
        chart.setTitle("Precisiones de Modelos")
        chart.setAnimationOptions(QChart.SeriesAnimations)

        # parametro QChart
        modelosEjeX = ('KNN', 'Naive Bayes', 'Decision Trees')

        # parametros ejeX
        ejeX = QBarCategoryAxis()
        ejeX.append(modelosEjeX)

        # parametros ejeY
        ejeY = QValueAxis()
        chart.addAxis(ejeX, Qt.AlignBottom)
        chart.addAxis(ejeY, Qt.AlignLeft)

        # leyenda Barchart
        chart.legend().setVisible(True)
        chart.legend().setAlignment(Qt.AlignBottom)

        # Mostrar ventana Barchart
        self.QChartView = QChartView(chart)
        self.QChartView.resize(600, 600)
        self.QChartView.show()
Beispiel #5
0
class BandPowerGraph(QWidget):

	def __init__(self, name: str):
		super().__init__()

		self.band_power_chart = QChart()
		self.band_power_chart.setAnimationOptions(QChart.SeriesAnimations)

		self.channel_band_power_set = QBarSet("Band Power")
		self.channel_band_power_set.append(1)
		self.channel_band_power_set.append(1)
		self.channel_band_power_set.append(1)
		self.channel_band_power_set.append(1)
		self.channel_band_power_set.append(1)

		self.bands_axis = QBarCategoryAxis()
		self.bands_axis.append("Delta (1 - 3 Hz)")
		self.bands_axis.append("Theta (4 - 7 Hz)")
		self.bands_axis.append("Alpha (8 - 13 Hz)")
		self.bands_axis.append("Beta (13 - 30 Hz)")
		self.bands_axis.append("Gamma (30 - 100)")

		self.power_axis = QValueAxis()

		self.chart_series = QBarSeries()
		self.chart_series.append(self.channel_band_power_set)

		self.band_power_chart.addSeries(self.chart_series)
		self.band_power_chart.setTitle(f"<h1>Band Power For {name}</h1>")
		self.band_power_chart.addAxis(self.bands_axis, Qt.AlignBottom)
		self.band_power_chart.addAxis(self.power_axis, Qt.AlignLeft)

		self.chart_series.attachAxis(self.bands_axis)
		self.chart_series.attachAxis(self.power_axis)

		self.chart_view = QChartView(self.band_power_chart)
		self.chart_view.setRenderHint(QPainter.Antialiasing)

		self.root_layout = QStackedLayout()
		self.setLayout(self.root_layout)

		self.root_layout.addWidget(self.chart_view)

	def set_name(self, name: str):
		self.band_power_chart.setTitle(f"<h1>Band Power For {name}</h1>")

	def update_values(self, data: np.ndarray, fft_window_size: float = 0):
		eeg_data = utils.EegData(data)

		feature_extractor = eeg_data.feature_extractor(0, global_config.SAMPLING_RATE)

		self.channel_band_power_set.replace(
			0, feature_extractor.average_band_amplitude(utils.FrequencyBand.delta_freq_band(), fft_window_size)
		)

		self.channel_band_power_set.replace(
			1, feature_extractor.average_band_amplitude(utils.FrequencyBand.theta_freq_band(), fft_window_size)
		)

		self.channel_band_power_set.replace(
			2, feature_extractor.average_band_amplitude(utils.FrequencyBand.alpha_freq_band(), fft_window_size)
		)

		self.channel_band_power_set.replace(
			3, feature_extractor.average_band_amplitude(utils.FrequencyBand.beta_freq_band(), fft_window_size)
		)

		self.channel_band_power_set.replace(
			4, feature_extractor.average_band_amplitude(utils.FrequencyBand.gama_freq_band(), fft_window_size)
		)

	def auto_adjust_axis(self):
		utils.auto_adjust_axis(self.power_axis, [self.channel_band_power_set], 0.1)
class ResonanceFrequencyFinder(QMainWindow):

	# AVAILABLE_WINDOW_SIZES = ["5 Sec", "8 Sec", "10 Sec", "15 Sec", "20 Sec"]

	DEFAULT_GRAPH_PADDING = 2

	DEFAULT_FFT_WINDOW_SIZE = 5

	DEFAULT_SAMPLE_PADDING = 2

	DEFAULT_RECORDING_DURATION = 30 + DEFAULT_SAMPLE_PADDING

	DEFAULT_MIN_FREQUENCY = 17

	DEFAULT_MAX_FREQUENCY = 35

	DEFAULT_FREQUENCY_STEP = 2

	# Used to create a band for which the average frequency amplitude is computed
	DEFAULT_FREQUENCY_PADDING = 0.2

	DEFAULT_BANDPASS_MIN = 8

	DEFAULT_BANDPASS_MAX = 40

	DEFAULT_C3_CHANNEL_INDEX = 4

	DEFAULT_CZ_CHANNEL_INDEX = 2

	DEFAULT_C4_CHANNEL_INDEX = 0

	def __init__(self, board: BoardShim):
		super().__init__()
		self.setGeometry(0, 0, 1800, 900)
		self.setWindowTitle("Resonance-Like Frequency")

		self.board = board

		self.recording_progress_dialog = None
		self.eeg_data_buffer = utils.EegData()
		self.reading_timer = QTimer()
		self.recording = False

		self.recording_reference = False
		self.reference_eeg_data = utils.EegData()

		self.index_generator = utils.FrequencyIndexGenerator(global_config.SAMPLING_RATE)
		self.eeg_sample_count = 0

		self.root_widget = QWidget()
		self.root_layout = QGridLayout()
		self.root_widget.setLayout(self.root_layout)
		self.setCentralWidget(self.root_widget)

		title = QLabel("<h1>Resonance Frequency Finder</h1>")
		title.setAlignment(Qt.AlignCenter)
		self.root_layout.addWidget(title, 0, 0, 1, 3)

		# window_size_label = QLabel("window size: ")
		# window_size_label.setAlignment(Qt.AlignRight)

		# self.window_size_combo_box = QComboBox()
		# self.window_size_combo_box.addItems(self.AVAILABLE_WINDOW_SIZES)

		self.root_directory_label = QLabel()
		self.select_root_directory = QPushButton("Select/Change")
		self.select_root_directory.clicked.connect(self.pick_root_directory)

		self.record_btn = QPushButton("Record")
		self.record_btn.setEnabled(False)
		self.record_btn.clicked.connect(self.record_clicked)

		self.record_reference_btn = QPushButton("Record Reference")
		self.record_reference_btn.clicked.connect(self.record_reference_clicked)

		# self.root_layout.addWidget(utils.construct_horizontal_box([
		# 	window_size_label, self.window_size_combo_box, self.record_btn
		# ]), 1, 0, 1, 3)

		self.load_results_btn = QPushButton("Load Existing Data")
		self.load_results_btn.clicked.connect(self.load_existing_data)

		self.root_layout.addWidget(utils.construct_horizontal_box([
			self.record_btn, self.record_reference_btn, self.root_directory_label,
			self.select_root_directory, self.load_results_btn
		]), 1, 0, 1, 3)

		self.current_freq_label = QLabel()

		self.root_layout.addWidget(utils.construct_horizontal_box([self.current_freq_label]), 2, 0, 1, 3)

		self.frequency_slider = QSlider()
		self.frequency_slider.setRange(self.DEFAULT_MIN_FREQUENCY, self.DEFAULT_MAX_FREQUENCY)
		self.frequency_slider.setSingleStep(self.DEFAULT_FREQUENCY_STEP)
		self.frequency_slider.setTickInterval(self.DEFAULT_FREQUENCY_STEP)
		self.frequency_slider.valueChanged.connect(self.update_freq_label)
		self.frequency_slider.setTickPosition(QSlider.TicksBelow)
		self.frequency_slider.setOrientation(Qt.Horizontal)

		min_freq_label = QLabel(f"<b>{self.DEFAULT_MIN_FREQUENCY} Hz</b>")
		max_freq_label = QLabel(f"<b>{self.DEFAULT_MAX_FREQUENCY} Hz</b>")

		self.root_layout.addWidget(utils.construct_horizontal_box([
			min_freq_label, self.frequency_slider, max_freq_label
		]), 3, 0, 1, 3)

		self.c3_amplitude_bar_set = QBarSet("Electrode C3")
		self.cz_amplitude_bar_set = QBarSet("Electrode Cz")
		self.c4_amplitude_bar_set = QBarSet("Electrode C4")

		self.frequencies = []

		for freq in range(self.DEFAULT_MIN_FREQUENCY, self.DEFAULT_MAX_FREQUENCY + 1, self.DEFAULT_FREQUENCY_STEP):
			self.frequencies.append(f"{freq} Hz")
			self.c3_amplitude_bar_set.append(1)
			self.cz_amplitude_bar_set.append(1)
			self.c4_amplitude_bar_set.append(1)

		self.freq_axis = QBarCategoryAxis()
		self.freq_axis.append(self.frequencies)

		self.amplitude_axis = QValueAxis()
		self.amplitude_axis.setRange(0, 4)

		self.freq_chart = QChart()
		self.freq_chart.setAnimationOptions(QChart.SeriesAnimations)

		self.electrodes_data_series = QBarSeries()
		self.electrodes_data_series.append(self.c3_amplitude_bar_set)
		self.electrodes_data_series.append(self.cz_amplitude_bar_set)
		self.electrodes_data_series.append(self.c4_amplitude_bar_set)

		self.freq_chart.addSeries(self.electrodes_data_series)
		self.freq_chart.setTitle("<h1>Frequency Amplitude Increase</h1>")
		self.freq_chart.addAxis(self.freq_axis, Qt.AlignBottom)
		self.freq_chart.addAxis(self.amplitude_axis, Qt.AlignLeft)

		self.electrodes_data_series.attachAxis(self.amplitude_axis)
		self.electrodes_data_series.attachAxis(self.freq_axis)

		self.frequency_amplitude_graph = QChartView(self.freq_chart)
		self.frequency_amplitude_graph.setRenderHint(QPainter.Antialiasing)

		self.root_layout.addWidget(self.frequency_amplitude_graph, 4, 0, 15, 3)

		self.auto_adjust_axis()

	def update_freq_label(self):
		self.current_freq_label.setText("Selected Frequency: {} Hz".format(self.frequency_slider.value()))

	def pick_root_directory(self):
		path = QFileDialog.getExistingDirectory(self, "Root Directory...")
		self.root_directory_label.setText(path)

	def record_clicked(self, reference: bool = False):
		# selected_window_text = self.window_size_combo_box.currentText()
		# window_size_text = selected_window_text.replace(" Sec", "")

		# window_size = -1
		#
		# if utils.is_integer(window_size_text):
		# 	window_size = int(window_size_text)
		# else:
		# 	print("Invalid window size...")
		# 	return

		# window_size_in_samples = window_size * SAMPLING_RATE

		recording_duration_in_samples = self.DEFAULT_RECORDING_DURATION * global_config.SAMPLING_RATE

		if not reference and (self.frequency_slider.value() - self.DEFAULT_MIN_FREQUENCY) % self.DEFAULT_FREQUENCY_STEP != 0:
			err = QErrorMessage(self)
			err.showMessage("Invalid Frequency Selected")
			err.exec()
			return

		self.recording_progress_dialog = \
			QProgressDialog("Reading EEG data from board...", "Stop Recording", 0, int(recording_duration_in_samples), self)
		self.recording_progress_dialog.setWindowTitle("Reading Data, Please Wait...")
		self.recording_progress_dialog.setWindowModality(Qt.WindowModal)
		self.recording_progress_dialog.show()

		if reference:
			self.recording_reference = True
		else:
			self.recording = True
			self.eeg_data_buffer.clear()

		self.board.start_stream()

		self.reading_timer = QTimer()
		self.reading_timer.timeout.connect(self.read_data)
		self.reading_timer.start(100)

	def record_reference_clicked(self):
		print("Record reference clicked")
		if self.reference_eeg_data.get_channel_data(0).shape[0] > 0:
			self.reference_eeg_data.clear()
		self.record_clicked(reference=True)

	def read_data(self):
		if not self.recording and not self.recording_reference:
			return

		recording_duration_in_samples = self.recording_progress_dialog.maximum()

		if self.recording_reference:
			if self.reference_eeg_data.get_channel_data(0).shape[0] > recording_duration_in_samples or\
					self.recording_progress_dialog.wasCanceled():
				self.stop_recording(True)
				return

		if self.recording:
			if self.recording_progress_dialog.wasCanceled() or\
					self.eeg_data_buffer.get_channel_data(0).shape[0] > recording_duration_in_samples:
				self.stop_recording(self.recording_reference)
				return

		if self.board.get_board_data_count() > 0:
			raw_data = self.board.get_board_data()
			raw_eeg_data = utils.extract_eeg_data(raw_data, global_config.BOARD_ID)

			self.eeg_sample_count += raw_eeg_data.shape[1]

			path = self.root_directory_label.text()

			if path != "":
				full_path = path + "/" + global_config.RESONANCE_DATA_FILE_NAME
				DataFilter.write_file(raw_eeg_data, full_path, "a")

			# c3 = raw_eeg_data[self.DEFAULT_C3_CHANNEL_INDEX, :]
			# cz = raw_eeg_data[self.DEFAULT_CZ_CHANNEL_INDEX, :]
			# c4 = raw_eeg_data[self.DEFAULT_C4_CHANNEL_INDEX, :]

			if self.recording_reference:
				self.reference_eeg_data.append_data(raw_eeg_data)
				print(f"reference size: {self.reference_eeg_data.sample_count()}")
				self.recording_progress_dialog.setValue(self.reference_eeg_data.get_channel_data(0).shape[0])
			else:
				self.eeg_data_buffer.append_data(raw_eeg_data)
				print(f"data size: {self.eeg_data_buffer.sample_count()}")
				self.recording_progress_dialog.setValue(self.eeg_data_buffer.get_channel_data(0).shape[0])

	def load_existing_data(self):
		path = QFileDialog.getExistingDirectory(self, "Root Directory...")

		if path == "":
			return

		filter_settings = utils.FilterSettings(global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX)

		frequencies, eeg_data, reference_data = utils.load_slice_and_filter_resonance_data(path, filter_settings)

		print(frequencies)

		size = len(frequencies)

		x = np.arange(size)

		x_ticks = []

		plot_data = np.zeros((3, size))

		for i in range(size):
			current_eeg_data = eeg_data[i]
			freq = frequencies[i]
			x_ticks.append(f"{freq} Hz")
			freq_band = utils.FrequencyBand(
				freq - self.DEFAULT_FREQUENCY_PADDING,
				freq + self.DEFAULT_FREQUENCY_PADDING)

			reference_c3_extractor = reference_data.feature_extractor(
				self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			reference_cz_extractor = reference_data.feature_extractor(
				self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			reference_c4_extractor = reference_data.feature_extractor(
				self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			data_c3_extractor = current_eeg_data.feature_extractor(
				self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			data_cz_extractor = current_eeg_data.feature_extractor(
				self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			data_c4_extractor = current_eeg_data.feature_extractor(
				self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			c3_diff, cz_diff, c4_diff = self.amplitude_diff(freq_band, reference_c3_extractor, reference_cz_extractor,
															reference_c4_extractor, data_c3_extractor,
															data_cz_extractor, data_c4_extractor)

			plot_data[0, i] = c3_diff
			plot_data[1, i] = cz_diff
			plot_data[2, i] = c4_diff

		plt.figure()
		plt.title("Amplitude Increase")

		plt.bar(x, plot_data[0], width=0.25, label="C3 amplitude increase")
		plt.bar(x + 0.25, plot_data[1], width=0.25, label="Cz amplitude increase")
		plt.bar(x + 0.50, plot_data[2], width=0.25, label="C4 amplitude increase")

		plt.xticks(x + 0.25, x_ticks)
		plt.ylabel("Average Band Amplitude")

		plt.legend(loc="best")
		plt.show()

	def amplitude_diff(self, freq_band, ref_c3_extractor, ref_cz_extractor, ref_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor):
		ref_c3_amplitude = ref_c3_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE)
		ref_cz_amplitude = ref_cz_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE)
		ref_c4_amplitude = ref_c4_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE)

		data_c3_amplitude = data_c3_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE)
		data_cz_amplitude = data_cz_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE)
		data_c4_amplitude = data_c4_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE)

		c3_diff = data_c3_amplitude - ref_c3_amplitude
		cz_diff = data_cz_amplitude - ref_cz_amplitude
		c4_diff = data_c4_amplitude - ref_c4_amplitude

		return c3_diff, cz_diff, c4_diff
		# return data_c3_amplitude, data_cz_amplitude, data_c4_amplitude

	def stop_recording(self, reference: bool = False):
		if self.reading_timer is not None:
			self.reading_timer.deleteLater()

		self.board.stop_stream()
		self.recording = False
		self.recording_reference = False
		self.recording_progress_dialog.setValue(self.recording_progress_dialog.maximum())

		recording_duration_in_samples = self.recording_progress_dialog.maximum()
		selected_freq = self.frequency_slider.value()

		if reference:
			sample_count = min(self.reference_eeg_data.get_channel_data(0).shape[0], recording_duration_in_samples)
			sample_count -= global_config.SAMPLING_RATE * self.DEFAULT_SAMPLE_PADDING
			self.index_generator.add_slice(0, self.eeg_sample_count - sample_count, self.eeg_sample_count)
		else:
			sample_count = min(self.eeg_data_buffer.get_channel_data(0).shape[0], recording_duration_in_samples)
			sample_count -= global_config.SAMPLING_RATE * self.DEFAULT_SAMPLE_PADDING
			self.index_generator.add_slice(selected_freq, self.eeg_sample_count - sample_count, self.eeg_sample_count)

		if self.root_directory_label.text() != "":
			self.index_generator.write_to_file(self.root_directory_label.text())

		QApplication.beep()

		start = self.DEFAULT_SAMPLE_PADDING * global_config.SAMPLING_RATE

		if reference:
			print(f"reference size: {self.reference_eeg_data.sample_count()}")
			self.record_btn.setEnabled(True)
			# self.record_reference_btn.setEnabled(False)
			self.reference_eeg_data.filter_all_channels(
				global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX, True
			)
			self.reference_eeg_data = utils.EegData(self.reference_eeg_data.to_row_array()[:, start:])
			print("Reference data saved...")
		else:
			print("Stopping the recording...")

			print("Filtering data...")

			self.eeg_data_buffer.filter_all_channels(
				global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX, subtract_average=True
			)

			self.eeg_data_buffer = utils.EegData(self.eeg_data_buffer.to_row_array()[:, start:])

			print(f"data size: {self.eeg_data_buffer.sample_count()}")

			reference_c3_extractor = self.reference_eeg_data.feature_extractor(
				self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			reference_cz_extractor = self.reference_eeg_data.feature_extractor(
				self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			reference_c4_extractor = self.reference_eeg_data.feature_extractor(
				self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			data_c3_extractor = self.eeg_data_buffer.feature_extractor(
				self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			data_cz_extractor = self.eeg_data_buffer.feature_extractor(
				self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			data_c4_extractor = self.eeg_data_buffer.feature_extractor(
				self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE
			)

			for i in range(self.c3_amplitude_bar_set.count()):
				current_freq = int(self.frequencies[i].replace(" Hz", ""))
				if current_freq == selected_freq:
					freq_band = utils.FrequencyBand(
						current_freq - self.DEFAULT_FREQUENCY_PADDING,
						current_freq + self.DEFAULT_FREQUENCY_PADDING)

					c3_diff, cz_diff, c4_diff = self.amplitude_diff(freq_band, reference_c3_extractor,reference_cz_extractor, reference_c4_extractor,
																	data_c3_extractor, data_cz_extractor, data_c4_extractor)

					print(f"C3 diff = {c3_diff}")
					print(f"Cz diff = {cz_diff}")
					print(f"C4 diff = {c4_diff}")

					self.c3_amplitude_bar_set.replace(i, c3_diff)
					self.cz_amplitude_bar_set.replace(i, cz_diff)
					self.c4_amplitude_bar_set.replace(i, c4_diff)

			utils.auto_adjust_axis(self.amplitude_axis,
								[self.c3_amplitude_bar_set, self.cz_amplitude_bar_set, self.c4_amplitude_bar_set], self.DEFAULT_GRAPH_PADDING)

	def auto_adjust_axis(self):
		# Adjust the range so that everything is visible and add some gaps

		c3_min = sys.maxsize
		cz_min = sys.maxsize
		c4_min = sys.maxsize

		c3_max = -sys.maxsize
		cz_max = -sys.maxsize
		c4_max = -sys.maxsize

		for i in range(self.c3_amplitude_bar_set.count()):
			c3_min = min(c3_min, self.c3_amplitude_bar_set.at(i))
			cz_min = min(cz_min, self.cz_amplitude_bar_set.at(i))
			c4_min = min(c4_min, self.c4_amplitude_bar_set.at(i))

			c3_max = max(c3_max, self.c3_amplitude_bar_set.at(i))
			cz_max = max(cz_max, self.cz_amplitude_bar_set.at(i))
			c4_max = max(c4_max, self.c4_amplitude_bar_set.at(i))

		print("c3 min = {}, cz min = {}, c4 min = {}".format(c3_min, cz_min, c4_min))
		print("c3 max = {}, cz max = {}, c4 max = {}".format(c3_max, cz_max, c4_max))

		axis_min = min(0, c3_min, cz_min, c4_min) - self.DEFAULT_GRAPH_PADDING
		axis_max = max(0, c3_max, cz_max, c4_max) + self.DEFAULT_GRAPH_PADDING

		print("axis min = {}, axis max = {}".format(axis_min, axis_max))

		self.amplitude_axis.setMin(axis_min)
		self.amplitude_axis.setMax(axis_max)