From f70be431cfb4ecda5c3e706f4ca95171bbe779b3 Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Sun, 31 Dec 2023 10:12:01 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D1=80=D0=B5=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #41 --------- Co-authored-by: MisterMLiL --- .idea/misc.xml | 2 +- .idea/stat.iml | 2 +- statapp/combo_delegate.py | 63 ++++++++++ statapp/main_window.py | 7 ++ statapp/models/transform_polynom_model.py | 74 ++++++++++++ statapp/polynoms/transform_polynom_window.py | 87 ++++++++++++++ statapp/ui/main_window.ui | 8 +- statapp/ui/transform_polynom_window.ui | 101 ++++++++++++++++ statapp/ui/ui_main_window.py | 6 +- statapp/ui/ui_transform_polynom_window.py | 118 +++++++++++++++++++ 10 files changed, 464 insertions(+), 4 deletions(-) create mode 100644 statapp/combo_delegate.py create mode 100644 statapp/models/transform_polynom_model.py create mode 100644 statapp/polynoms/transform_polynom_window.py create mode 100644 statapp/ui/transform_polynom_window.ui create mode 100644 statapp/ui/ui_transform_polynom_window.py diff --git a/.idea/misc.xml b/.idea/misc.xml index 19c19b7..2f44f69 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,5 +3,5 @@ - + diff --git a/.idea/stat.iml b/.idea/stat.iml index ceec064..7190cc6 100644 --- a/.idea/stat.iml +++ b/.idea/stat.iml @@ -2,7 +2,7 @@ - + diff --git a/statapp/combo_delegate.py b/statapp/combo_delegate.py new file mode 100644 index 0000000..aa7b05e --- /dev/null +++ b/statapp/combo_delegate.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2023 Maxim Slipenko, Eugene Lazurenko. +# +# This file is part of Statapp +# (see https://github.com/shizand/statapp). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +from PySide2 import QtCore +from PySide2.QtWidgets import QComboBox, QItemDelegate + + +class ComboDelegate(QItemDelegate): + commitData = QtCore.Signal(object) + """ + A delegate that places a fully functioning QComboBox in every + cell of the column to which it's applied + """ + def __init__(self, parent, objects, objectNames): + """ + Constructoe + :param parent: QTableView parent object + :param objects: List of objects to set. i.e. [True, False] + :param objectNames: List of Object names to display. i.e. ['True', 'False'] + """ + QItemDelegate.__init__(self, parent) + + # objects to sent to the model associated to the combobox. i.e. [True, False] + self.objects = objects + + # object description to display in the combobox. i.e. ['True', 'False'] + self.objectNames = objectNames + + @QtCore.Slot() + def currentIndexChanged(self): + self.commitData.emit(self.sender()) + + def createEditor(self, parent, option, index): + combo = QComboBox(parent) + combo.addItems(self.objectNames) + combo.currentIndexChanged.connect(self.currentIndexChanged) + return combo + + def setEditorData(self, editor, index): + editor.blockSignals(True) + val = index.model().data(index, role=QtCore.Qt.DisplayRole) + idx = self.objects.index(val) + editor.setCurrentIndex(idx) + editor.blockSignals(False) + + def setModelData(self, editor, model, index): + model.setData(index, self.objects[editor.currentIndex()], QtCore.Qt.EditRole) diff --git a/statapp/main_window.py b/statapp/main_window.py index 4839fb0..a04dacc 100644 --- a/statapp/main_window.py +++ b/statapp/main_window.py @@ -36,6 +36,7 @@ from statapp.ui.ui_main_window import Ui_MainWindow from statapp.utils import buildMessageBox, addIcon, FloatDelegate from statapp.variance_analysis import VarianceAnalysisWindow from statapp.correlation_analysis import CorrelationAnalysisWindow +from statapp.polynoms.transform_polynom_window import TransformPolynomWindow class MainWindow(QMainWindow): @@ -56,6 +57,7 @@ class MainWindow(QMainWindow): self.ui.correlationAnalisisAction, self.ui.linearPolynomAction, self.ui.squaredPolynomAction, + self.ui.transformPolynomAction, ] self.aboutWindow = None @@ -192,6 +194,11 @@ class MainWindow(QMainWindow): dw = SquaredPolynomWindow(self.model.getData()) dw.exec() + @Slot() + def on_transformPolynomAction_triggered(self): + dw = TransformPolynomWindow(self.model.getData()) + dw.exec() + def closeEvent(self, event): if self.isDataChanged: file = '' diff --git a/statapp/models/transform_polynom_model.py b/statapp/models/transform_polynom_model.py new file mode 100644 index 0000000..0568585 --- /dev/null +++ b/statapp/models/transform_polynom_model.py @@ -0,0 +1,74 @@ +# +# Copyright (c) 2023 Maxim Slipenko, Eugene Lazurenko. +# +# This file is part of Statapp +# (see https://github.com/shizand/statapp). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +import numpy as np +from PySide2.QtCore import Qt + +from statapp.models.editable_table_model import EditableTableModel +from statapp.models.regression_result_model import RegressionResultModel + +def defaultX(x): + return x + +TRANSFORMS = { + '-': defaultX, + 'sin(x)': np.sin, + 'cos(x)': np.cos, + 'log(x)': np.log, + 'exp(x)': np.exp, +} + +class TransformPolynomModel(RegressionResultModel, EditableTableModel): + + def __init__(self, result): + self._monomials = None + super().__init__(result) + n = result.paramsAndImportance.shape[0] + + self._transforms = ['-'] * n + + def columnCount(self, index): + return 3 + + def updateAllData(self, data): + d = data.paramsAndImportance + self._monomials = data.monomials + super().updateAllData(d) + + def flags(self, index): + if index.column() == 0 and index.row() != 0: + return EditableTableModel.flags(self, index) + return RegressionResultModel.flags(self, index) + + def data(self, index, role): + if role == Qt.DisplayRole and index.column() == 0: + return self._transforms[index.row()] + return super().data(index, role) + + def setData(self, index, value, role): + if role == Qt.EditRole and index.column() == 0: + self._transforms[index.row()] = value + topLeftIndex = self.createIndex(index.row(), 0) + bottomRightIndex = self.createIndex(index.row(), 0) + self.dataChanged.emit(topLeftIndex, bottomRightIndex) + return True + return super().setData(index, value, role) + + def getHorizontalHeader(self): + return ['Преобразования'] + super().getHorizontalHeader() diff --git a/statapp/polynoms/transform_polynom_window.py b/statapp/polynoms/transform_polynom_window.py new file mode 100644 index 0000000..945f051 --- /dev/null +++ b/statapp/polynoms/transform_polynom_window.py @@ -0,0 +1,87 @@ +# +# Copyright (c) 2023 Maxim Slipenko, Eugene Lazurenko. +# +# This file is part of Statapp +# (see https://github.com/shizand/statapp). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +import numpy as np +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QDialog, QHeaderView + +from statapp.calculations import linearPolynom +from statapp.combo_delegate import ComboDelegate +from statapp.mathtex_header_view import MathTexHeaderView +from statapp.models.transform_polynom_model import TransformPolynomModel, TRANSFORMS +from statapp.ui.ui_transform_polynom_window import Ui_PolynomWindow +from statapp.utils import addIcon + + +class TransformPolynomWindow(QDialog): + def __init__(self, data): + super().__init__() + self.ui = Ui_PolynomWindow() + self.ui.setupUi(self) + addIcon(self) + self.setWindowTitle("Преобразования") + + self.data = data + result = linearPolynom(data) + + # Создание столбца из нулей + zeroCol = np.zeros((result.paramsAndImportance.shape[0], 1)) + # Добавление столбца к исходному массиву + result.paramsAndImportance = np.column_stack((zeroCol, result.paramsAndImportance)) + + # self.ui.tableView.setItemDelegate(FloatDelegate()) + self.ui.tableView.setItemDelegate( + ComboDelegate( + self.ui.tableView, + list(TRANSFORMS.keys()), + list(TRANSFORMS.keys()), + ) + ) + self.model = TransformPolynomModel(result) + self.ui.tableView.setModel(self.model) + self.ui.tableView.setVerticalHeader(MathTexHeaderView(self.ui.tableView)) + header = self.ui.tableView.horizontalHeader() + header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch) + + self.ui.residualVarianceValueLabel.setText(str(result.residualVariance)) + self.ui.scaledResidualVarianceValueLabel.setText(str(result.scaledResidualVariance)) + self.ui.fStatisticValueLabel.setText(str(result.fStatistic)) + self.ui.rSquaredValueLabel.setText(str(result.scaledResidualVariance)) + + self.model.dataChanged.connect(self.on_data_changed) + + def on_data_changed(self): + data = np.copy(self.data) + print(len(data[0:])) + for i in range(len(data[0:])): + for j in range(1, len(data[i])): + tr = self.model.data(self.model.createIndex(j, 0), Qt.DisplayRole) + data[i][j] = TRANSFORMS[tr](data[i][j]) + + self.rebuildData(data) + + def rebuildData(self, data): + result = linearPolynom(data) + zeroCol = np.zeros((result.paramsAndImportance.shape[0], 1)) + result.paramsAndImportance = np.column_stack((zeroCol, result.paramsAndImportance)) + self.model.updateAllData(result) + self.ui.residualVarianceValueLabel.setText(str(result.residualVariance)) + self.ui.scaledResidualVarianceValueLabel.setText(str(result.scaledResidualVariance)) + self.ui.fStatisticValueLabel.setText(str(result.fStatistic)) + self.ui.rSquaredValueLabel.setText(str(result.scaledResidualVariance)) diff --git a/statapp/ui/main_window.ui b/statapp/ui/main_window.ui index a23d2ec..e33b238 100644 --- a/statapp/ui/main_window.ui +++ b/statapp/ui/main_window.ui @@ -40,7 +40,7 @@ 0 0 800 - 27 + 21 @@ -71,6 +71,7 @@ + @@ -135,6 +136,11 @@ Квадратичный полином + + + Преобразования + + diff --git a/statapp/ui/transform_polynom_window.ui b/statapp/ui/transform_polynom_window.ui new file mode 100644 index 0000000..a140630 --- /dev/null +++ b/statapp/ui/transform_polynom_window.ui @@ -0,0 +1,101 @@ + + + PolynomWindow + + + + 0 + 0 + 537 + 444 + + + + Полином + + + + + + 10 + + + + + undefined + + + + + + + undefined + + + + + + + F1 - отношение Фишера + + + + + + + Остаточная дисперсия: + + + + + + + Остаточная дисперсия (масштабированная): + + + + + + + Коэффициент множественной дереминизации + + + + + + + undefined + + + + + + + undefined + + + + + + + + + + + 40 + + + 40 + + + 40 + + + + + + + + + + diff --git a/statapp/ui/ui_main_window.py b/statapp/ui/ui_main_window.py index 6049b84..e5692a5 100644 --- a/statapp/ui/ui_main_window.py +++ b/statapp/ui/ui_main_window.py @@ -58,6 +58,8 @@ class Ui_MainWindow(object): self.linearPolynomAction.setObjectName(u"linearPolynomAction") self.squaredPolynomAction = QAction(MainWindow) self.squaredPolynomAction.setObjectName(u"squaredPolynomAction") + self.transformPolynomAction = QAction(MainWindow) + self.transformPolynomAction.setObjectName(u"transformPolynomAction") self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.gridLayout = QGridLayout(self.centralwidget) @@ -77,7 +79,7 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 800, 27)) + self.menubar.setGeometry(QRect(0, 0, 800, 21)) self.filemenu = QMenu(self.menubar) self.filemenu.setObjectName(u"filemenu") self.generatemenu = QMenu(self.menubar) @@ -107,6 +109,7 @@ class Ui_MainWindow(object): self.analyzemenu.addAction(self.correlationAnalisisAction) self.modelmenu.addAction(self.linearPolynomAction) self.modelmenu.addAction(self.squaredPolynomAction) + self.modelmenu.addAction(self.transformPolynomAction) self.helpmenu.addAction(self.aboutmenuaction) self.retranslateUi(MainWindow) @@ -126,6 +129,7 @@ class Ui_MainWindow(object): self.correlationAnalisisAction.setText(QCoreApplication.translate("MainWindow", u"\u041a\u043e\u0440\u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437", None)) self.linearPolynomAction.setText(QCoreApplication.translate("MainWindow", u"\u041b\u0438\u043d\u0435\u0439\u043d\u044b\u0439 \u043f\u043e\u043b\u0438\u043d\u043e\u043c", None)) self.squaredPolynomAction.setText(QCoreApplication.translate("MainWindow", u"\u041a\u0432\u0430\u0434\u0440\u0430\u0442\u0438\u0447\u043d\u044b\u0439 \u043f\u043e\u043b\u0438\u043d\u043e\u043c", None)) + self.transformPolynomAction.setText(QCoreApplication.translate("MainWindow", u"\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f", None)) self.label.setText(QCoreApplication.translate("MainWindow", u"\u0421\u0422\u0410\u0422\u0418\u0421\u0422\u0418\u0427\u0415\u0421\u041a\u0418\u0415 \u0414\u0410\u041d\u041d\u042b\u0415", None)) self.filemenu.setTitle(QCoreApplication.translate("MainWindow", u"\u0424\u0430\u0439\u043b", None)) self.generatemenu.setTitle(QCoreApplication.translate("MainWindow", u"\u0413\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u0435\u0439", None)) diff --git a/statapp/ui/ui_transform_polynom_window.py b/statapp/ui/ui_transform_polynom_window.py new file mode 100644 index 0000000..459e345 --- /dev/null +++ b/statapp/ui/ui_transform_polynom_window.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +# +# Copyright (c) 2023 Maxim Slipenko, Eugene Lazurenko. +# +# This file is part of Statapp +# (see https://github.com/shizand/statapp). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + + +################################################################################ +## Form generated from reading UI file 'transform_polynom_window.ui' +## +## Created by: Qt User Interface Compiler version 5.15.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide2.QtCore import * +from PySide2.QtGui import * +from PySide2.QtWidgets import * + + +class Ui_PolynomWindow(object): + def setupUi(self, PolynomWindow): + if not PolynomWindow.objectName(): + PolynomWindow.setObjectName(u"PolynomWindow") + PolynomWindow.resize(537, 444) + self.gridLayout_2 = QGridLayout(PolynomWindow) + self.gridLayout_2.setObjectName(u"gridLayout_2") + self.polynomResult = QGridLayout() + self.polynomResult.setObjectName(u"polynomResult") + self.polynomResult.setContentsMargins(-1, 10, -1, -1) + self.residualVarianceValueLabel = QLabel(PolynomWindow) + self.residualVarianceValueLabel.setObjectName(u"residualVarianceValueLabel") + + self.polynomResult.addWidget(self.residualVarianceValueLabel, 0, 1, 1, 1) + + self.scaledResidualVarianceValueLabel = QLabel(PolynomWindow) + self.scaledResidualVarianceValueLabel.setObjectName(u"scaledResidualVarianceValueLabel") + + self.polynomResult.addWidget(self.scaledResidualVarianceValueLabel, 1, 1, 1, 1) + + self.fStatisticLabel = QLabel(PolynomWindow) + self.fStatisticLabel.setObjectName(u"fStatisticLabel") + + self.polynomResult.addWidget(self.fStatisticLabel, 2, 0, 1, 1) + + self.residualVarianceLabel = QLabel(PolynomWindow) + self.residualVarianceLabel.setObjectName(u"residualVarianceLabel") + + self.polynomResult.addWidget(self.residualVarianceLabel, 0, 0, 1, 1) + + self.scaledResidualVarianceLabel = QLabel(PolynomWindow) + self.scaledResidualVarianceLabel.setObjectName(u"scaledResidualVarianceLabel") + + self.polynomResult.addWidget(self.scaledResidualVarianceLabel, 1, 0, 1, 1) + + self.rSquaredLabel = QLabel(PolynomWindow) + self.rSquaredLabel.setObjectName(u"rSquaredLabel") + + self.polynomResult.addWidget(self.rSquaredLabel, 3, 0, 1, 1) + + self.fStatisticValueLabel = QLabel(PolynomWindow) + self.fStatisticValueLabel.setObjectName(u"fStatisticValueLabel") + + self.polynomResult.addWidget(self.fStatisticValueLabel, 2, 1, 1, 1) + + self.rSquaredValueLabel = QLabel(PolynomWindow) + self.rSquaredValueLabel.setObjectName(u"rSquaredValueLabel") + + self.polynomResult.addWidget(self.rSquaredValueLabel, 3, 1, 1, 1) + + + self.gridLayout_2.addLayout(self.polynomResult, 1, 6, 1, 1) + + self.gridLayout = QGridLayout() + self.gridLayout.setObjectName(u"gridLayout") + self.tableView = QTableView(PolynomWindow) + self.tableView.setObjectName(u"tableView") + self.tableView.horizontalHeader().setMinimumSectionSize(40) + self.tableView.verticalHeader().setMinimumSectionSize(40) + self.tableView.verticalHeader().setDefaultSectionSize(40) + + self.gridLayout.addWidget(self.tableView, 1, 3, 1, 1) + + + self.gridLayout_2.addLayout(self.gridLayout, 0, 6, 1, 1) + + + self.retranslateUi(PolynomWindow) + + QMetaObject.connectSlotsByName(PolynomWindow) + # setupUi + + def retranslateUi(self, PolynomWindow): + PolynomWindow.setWindowTitle(QCoreApplication.translate("PolynomWindow", u"\u041f\u043e\u043b\u0438\u043d\u043e\u043c", None)) + self.residualVarianceValueLabel.setText(QCoreApplication.translate("PolynomWindow", u"undefined", None)) + self.scaledResidualVarianceValueLabel.setText(QCoreApplication.translate("PolynomWindow", u"undefined", None)) + self.fStatisticLabel.setText(QCoreApplication.translate("PolynomWindow", u"F1 - \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u0424\u0438\u0448\u0435\u0440\u0430", None)) + self.residualVarianceLabel.setText(QCoreApplication.translate("PolynomWindow", u"\u041e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u0430\u044f \u0434\u0438\u0441\u043f\u0435\u0440\u0441\u0438\u044f:", None)) + self.scaledResidualVarianceLabel.setText(QCoreApplication.translate("PolynomWindow", u"\u041e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u0430\u044f \u0434\u0438\u0441\u043f\u0435\u0440\u0441\u0438\u044f (\u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f):", None)) + self.rSquaredLabel.setText(QCoreApplication.translate("PolynomWindow", u"\u041a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u0434\u0435\u0440\u0435\u043c\u0438\u043d\u0438\u0437\u0430\u0446\u0438\u0438", None)) + self.fStatisticValueLabel.setText(QCoreApplication.translate("PolynomWindow", u"undefined", None)) + self.rSquaredValueLabel.setText(QCoreApplication.translate("PolynomWindow", u"undefined", None)) + # retranslateUi