feat: добавлен дисперсионный анализ (#46)

Closes #30
This commit is contained in:
Maxim Slipenko 2023-10-01 21:53:06 +03:00 committed by GitHub
parent 7f01052aa5
commit c77ed6a82f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 204 additions and 15 deletions

7
statapp/calculations.py Normal file
View File

@ -0,0 +1,7 @@
import numpy as np
def variance_analysis(data):
return np.array([
[np.mean(col), np.std(col), np.min(col), np.max(col)] for col in data.T
])

View File

@ -9,7 +9,8 @@ from statapp.generate_window import GenerateWindow
from statapp.about_window import AboutWindow from statapp.about_window import AboutWindow
from statapp.models.fileslc_model import FileSLCModel from statapp.models.fileslc_model import FileSLCModel
from statapp.ui.ui_main_window import Ui_MainWindow from statapp.ui.ui_main_window import Ui_MainWindow
from statapp.utils import resource_path from statapp.utils import resource_path, buildMessageBox
from statapp.variance_analysis import VarianceAnalysisWindow
class MainWindow(QMainWindow): class MainWindow(QMainWindow):
@ -36,7 +37,7 @@ class MainWindow(QMainWindow):
if self.fileModel.file_name: if self.fileModel.file_name:
file = '\nФайл сохранения:' + self.fileModel.file_name file = '\nФайл сохранения:' + self.fileModel.file_name
msgBox = self.createMessageBox \ msgBox = buildMessageBox \
('Сохранение данных', ('Сохранение данных',
"Сохранить данные?" + file, "Сохранить данные?" + file,
QMessageBox.Question, QMessageBox.Question,
@ -119,16 +120,10 @@ class MainWindow(QMainWindow):
about_window = AboutWindow() about_window = AboutWindow()
about_window.show() about_window.show()
def createMessageBox(self, title, text, icon, buttons, defaultButton): @Slot()
msgBox = QMessageBox() def on_varianceAnalysisAction_triggered(self):
dw = VarianceAnalysisWindow(self.model.getData())
msgBox.setIcon(icon) dw.exec()
msgBox.setWindowTitle(title)
msgBox.setText(text)
msgBox.setStandardButtons(buttons)
msgBox.setDefaultButton(defaultButton)
return msgBox
def closeEvent(self, event): def closeEvent(self, event):
if self.isDataChanged: if self.isDataChanged:
@ -136,7 +131,7 @@ class MainWindow(QMainWindow):
if self.fileModel.file_name: if self.fileModel.file_name:
file = '\nФайл сохранения:' + self.fileModel.file_name file = '\nФайл сохранения:' + self.fileModel.file_name
msgBox = self.createMessageBox \ msgBox = buildMessageBox \
('Завершение работы', ('Завершение работы',
"Сохранить данные?" + file, "Сохранить данные?" + file,
QMessageBox.Question, QMessageBox.Question,

View File

@ -0,0 +1,53 @@
import PySide2
import numpy as np
from PySide2 import QtCore
from PySide2.QtCore import Qt
from statapp.utils import safe_list_get
class ROTableModel(QtCore.QAbstractTableModel):
def __init__(self,
data=np.array([[]], dtype=np.float32),
):
super().__init__()
self._data = data
self._headers = {
Qt.Vertical: self.getVerticalHeader,
Qt.Horizontal: self.getHorizontalHeader,
}
def updateAllData(self, data):
self.layoutAboutToBeChanged.emit()
self._data = data
self.layoutChanged.emit()
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def getVerticalHeader(self):
return []
def getHorizontalHeader(self):
return []
def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...):
if role == Qt.DisplayRole:
return safe_list_get(self._headers[orientation](), section, None)
return None
def getData(self):
return self._data
def getY(self):
return self._data[:, 0]
def data(self, index, role):
if role == Qt.DisplayRole:
return float(self._data[index.row(), index.column()])
return None

View File

@ -0,0 +1,14 @@
from PySide2.QtCore import QModelIndex
from statapp.models.ro_table_model import ROTableModel
class VarianceAnalysisModel(ROTableModel):
def __init__(self, data):
super().__init__(data)
def getHorizontalHeader(self):
return ['Мат. ожидание', 'Среднекв. отклонение', 'Минимум', 'Максимум']
def getVerticalHeader(self):
return ['Y'] + [f'X{i}' for i in range(1, self.rowCount(QModelIndex()))]

View File

@ -40,7 +40,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>800</width>
<height>21</height> <height>27</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="filemenu"> <widget class="QMenu" name="filemenu">
@ -62,6 +62,7 @@
<property name="title"> <property name="title">
<string>Анализ данных</string> <string>Анализ данных</string>
</property> </property>
<addaction name="varianceAnalysisAction"/>
</widget> </widget>
<widget class="QMenu" name="modelmenu"> <widget class="QMenu" name="modelmenu">
<property name="title"> <property name="title">
@ -111,6 +112,12 @@
<string>Закрыть</string> <string>Закрыть</string>
</property> </property>
</action> </action>
<action name="varianceAnalysisAction">
<property name="text">
<string>Дисперсионный анализ</string>
</property>
</action>
</widget> </widget>
<resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -12,6 +12,7 @@ from PySide2.QtCore import *
from PySide2.QtGui import * from PySide2.QtGui import *
from PySide2.QtWidgets import * from PySide2.QtWidgets import *
class Ui_MainWindow(object): class Ui_MainWindow(object):
def setupUi(self, MainWindow): def setupUi(self, MainWindow):
if not MainWindow.objectName(): if not MainWindow.objectName():
@ -29,6 +30,8 @@ class Ui_MainWindow(object):
self.savefileaction.setObjectName(u"savefileaction") self.savefileaction.setObjectName(u"savefileaction")
self.closefileaction = QAction(MainWindow) self.closefileaction = QAction(MainWindow)
self.closefileaction.setObjectName(u"closefileaction") self.closefileaction.setObjectName(u"closefileaction")
self.varianceAnalysisAction = QAction(MainWindow)
self.varianceAnalysisAction.setObjectName(u"varianceAnalysisAction")
self.centralwidget = QWidget(MainWindow) self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget") self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout = QGridLayout(self.centralwidget) self.gridLayout = QGridLayout(self.centralwidget)
@ -48,7 +51,7 @@ class Ui_MainWindow(object):
MainWindow.setCentralWidget(self.centralwidget) MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow) self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar") self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 800, 21)) self.menubar.setGeometry(QRect(0, 0, 800, 27))
self.filemenu = QMenu(self.menubar) self.filemenu = QMenu(self.menubar)
self.filemenu.setObjectName(u"filemenu") self.filemenu.setObjectName(u"filemenu")
self.generatemenu = QMenu(self.menubar) self.generatemenu = QMenu(self.menubar)
@ -74,6 +77,7 @@ class Ui_MainWindow(object):
self.filemenu.addAction(self.closefileaction) self.filemenu.addAction(self.closefileaction)
self.generatemenu.addAction(self.generateYaction) self.generatemenu.addAction(self.generateYaction)
self.generatemenu.addAction(self.generateXaction) self.generatemenu.addAction(self.generateXaction)
self.analyzemenu.addAction(self.varianceAnalysisAction)
self.helpmenu.addAction(self.aboutmenuaction) self.helpmenu.addAction(self.aboutmenuaction)
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
@ -89,6 +93,7 @@ class Ui_MainWindow(object):
self.openfileaction.setText(QCoreApplication.translate("MainWindow", u"\u041e\u0442\u043a\u0440\u044b\u0442\u044c", None)) self.openfileaction.setText(QCoreApplication.translate("MainWindow", u"\u041e\u0442\u043a\u0440\u044b\u0442\u044c", None))
self.savefileaction.setText(QCoreApplication.translate("MainWindow", u"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", None)) self.savefileaction.setText(QCoreApplication.translate("MainWindow", u"\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c", None))
self.closefileaction.setText(QCoreApplication.translate("MainWindow", u"\u0417\u0430\u043a\u0440\u044b\u0442\u044c", None)) self.closefileaction.setText(QCoreApplication.translate("MainWindow", u"\u0417\u0430\u043a\u0440\u044b\u0442\u044c", None))
self.varianceAnalysisAction.setText(QCoreApplication.translate("MainWindow", u"\u0414\u0438\u0441\u043f\u0435\u0440\u0441\u0438\u043e\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437", 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.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.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)) 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))

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'variance_analysis_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_VarianceAnalysisWindow(object):
def setupUi(self, VarianceAnalysisWindow):
if not VarianceAnalysisWindow.objectName():
VarianceAnalysisWindow.setObjectName(u"VarianceAnalysisWindow")
VarianceAnalysisWindow.resize(942, 606)
self.gridLayout_2 = QGridLayout(VarianceAnalysisWindow)
self.gridLayout_2.setObjectName(u"gridLayout_2")
self.gridLayout = QGridLayout()
self.gridLayout.setObjectName(u"gridLayout")
self.tableView = QTableView(VarianceAnalysisWindow)
self.tableView.setObjectName(u"tableView")
self.gridLayout.addWidget(self.tableView, 0, 0, 1, 1)
self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 1, 1)
self.retranslateUi(VarianceAnalysisWindow)
QMetaObject.connectSlotsByName(VarianceAnalysisWindow)
# setupUi
def retranslateUi(self, VarianceAnalysisWindow):
VarianceAnalysisWindow.setWindowTitle(QCoreApplication.translate("VarianceAnalysisWindow", u"\u0414\u0438\u0441\u043f\u0435\u0440\u0441\u0438\u043e\u043d\u043d\u044b\u0439 \u0430\u043d\u0430\u043b\u0438\u0437", None))
# retranslateUi

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VarianceAnalysisWindow</class>
<widget class="QDialog" name="VarianceAnalysisWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>942</width>
<height>606</height>
</rect>
</property>
<property name="windowTitle">
<string>Дисперсионный анализ</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,6 +1,8 @@
import os import os
import sys import sys
from PySide2.QtWidgets import QMessageBox
def resource_path(relative): def resource_path(relative):
if getattr(sys, 'frozen', False): if getattr(sys, 'frozen', False):
@ -9,3 +11,22 @@ def resource_path(relative):
# we are running in a normal Python environment # we are running in a normal Python environment
bundle_dir = os.path.dirname(os.path.abspath(__file__)) bundle_dir = os.path.dirname(os.path.abspath(__file__))
return os.path.join(bundle_dir, relative) return os.path.join(bundle_dir, relative)
def safe_list_get(l, idx, default):
try:
return l[idx]
except IndexError:
return default
def buildMessageBox(title, text, icon, buttons, defaultButton):
msgBox = QMessageBox()
msgBox.setIcon(icon)
msgBox.setWindowTitle(title)
msgBox.setText(text)
msgBox.setStandardButtons(buttons)
msgBox.setDefaultButton(defaultButton)
return msgBox

View File

@ -0,0 +1,18 @@
from PySide2.QtWidgets import QDialog, QHeaderView
from statapp.calculations import variance_analysis
from statapp.models.variance_analysis_model import VarianceAnalysisModel
from statapp.ui.ui_variance_analysis_window import Ui_VarianceAnalysisWindow
class VarianceAnalysisWindow(QDialog):
def __init__(self, data):
super().__init__()
self.ui = Ui_VarianceAnalysisWindow()
self.ui.setupUi(self)
res = variance_analysis(data)
self.model = VarianceAnalysisModel(res.round(2))
self.ui.tableView.setModel(self.model)
header = self.ui.tableView.horizontalHeader()
header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)