Skip to content

Commit

Permalink
feat: добавлен "Линейный полином" (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
Maks1mS authored Oct 5, 2023
1 parent e653c3d commit 232de9f
Show file tree
Hide file tree
Showing 12 changed files with 309 additions and 26 deletions.
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ readme = "README.md"
python = ">=3.8,<3.9"
pre-commit = "^3.4.0"
pyinstaller = "^6.0.0"
pandas = "^2.0"
pyside2 = "^5.15.2.1"
pylint = "^2"
pandas = { version = "^2", markers = "python_version < '3.9'" }
pylint = { version = "^2", markers = "python_version < '3.9'" }
# scipy = { version = "^1", markers = "python_version < '3.9'" }
# openpyxl = "^3.1.2"


[build-system]
Expand Down
1 change: 0 additions & 1 deletion statapp/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,5 @@ def main():
window.show()
return app.exec_()


if __name__ == "__main__":
sys.exit(main())
45 changes: 44 additions & 1 deletion statapp/calculations.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from dataclasses import dataclass

import numpy as np
import pandas as pd

DIRECT_LINK = 0
INDIRECT_LINK = 1


def generateYValues(mean, std, count):
return np.random.normal(mean, std, size=(count, 1))


def generateXValues(mean, std, typeConnection, yColumn):
yMean = np.mean(yColumn)
values = []
Expand All @@ -38,7 +44,9 @@ def generateXValues(mean, std, typeConnection, yColumn):
else:
x = mean
values.append(x)
return np.array(values)

res = np.array(values)
return res.reshape(len(res), 1)


def varianceAnalysis(data):
Expand All @@ -49,3 +57,38 @@ def varianceAnalysis(data):

def correlationAnalysis(data):
return pd.DataFrame(data).corr().to_numpy()

@dataclass()
class LinearPolynomResult:
paramsAndImportance: np.ndarray
residualVariance: np.float64


def linearPolynom(inputData) -> LinearPolynomResult:
x = inputData[:, 1:]
y = inputData[:, 0]
data = pd.DataFrame(x)
data.insert(0, 'const', 1)
# ---
result = np.linalg.lstsq(data, y, rcond=None)
# Коэффициенты регрессии
params = result[0]
# Остатки
residues = result[1]

# Степень свободы
dof = len(data) - len(params)
mse = residues / dof
cov = mse * np.diagonal(np.linalg.inv(data.T @ data))
se = np.sqrt(cov)
tStatistics = params / se

# возможно стоит сделать через np.reshape + np.concatenate
out = pd.DataFrame()
out[0] = params
out[1] = tStatistics

return LinearPolynomResult(
out.to_numpy(),
np.float64(mse[0])
)
42 changes: 42 additions & 0 deletions statapp/linear_polynom_window.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#
# 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 <http://www.gnu.org/licenses/>.
#
from PySide2.QtWidgets import QDialog, QHeaderView

from statapp.calculations import linearPolynom
from statapp.models.linear_polynom_model import LinearPolynomModel
from statapp.ui.ui_linear_polynom_window import Ui_LinearPolynomWindow
from statapp.utils import addIcon


class LinearPolynomWindow(QDialog):
def __init__(self, data):
super().__init__()
self.ui = Ui_LinearPolynomWindow()
self.ui.setupUi(self)
addIcon(self)

result = linearPolynom(data)

self.model = LinearPolynomModel(result.paramsAndImportance.round(2))
self.ui.tableView.setModel(self.model)
header = self.ui.tableView.horizontalHeader()
header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch)

self.ui.residualVarianceValueLabel.setText(str(result.residualVariance))
64 changes: 45 additions & 19 deletions statapp/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
from PySide2.QtCore import Slot
from PySide2.QtWidgets import QMainWindow, QMessageBox

from statapp.calculations import generateXValues
from statapp.calculations import generateXValues, generateYValues
from statapp.generate_factor_window import GenerateFactorWindow
from statapp.linear_polynom_window import LinearPolynomWindow
from statapp.models.input_values_model import InputValuesModel
from statapp.generate_window import GenerateWindow
from statapp.about_window import AboutWindow
Expand All @@ -46,12 +47,48 @@ def __init__(self):
self.ui.varianceAnalysisAction.setEnabled(False)
self.ui.correlationAnalisisAction.setEnabled(False)

self.mainActions = [
self.ui.varianceAnalysisAction,
self.ui.correlationAnalisisAction,
self.ui.linearPolynomAction,
]

self.aboutWindow = None

self.isDataChanged = False
self.model = InputValuesModel()
self.fileModel = FileSLCModel()
self.ui.tableView.setModel(self.model)
self.model.layoutChanged.connect(self.updateActionsEnabled)
self.updateActionsEnabled()
#
# Для быстрой отладки
# n = 10
# y = generateYValues(100, 5, n)
# x1 = generateXValues(20, 2, 0, y)
# x2 = generateXValues(10, 1, 0, y)
# self.model.updateAllData(np.concatenate([y, x1, x2], axis=1))


def updateActionsEnabled(self):
data = self.model.getData()

# есть только отклик
if data.shape[1] == 1:
self.ui.generateXaction.setEnabled(True)
self.setEnabledMainActions(False)
# есть отклик и фактор(ы)
elif data.shape[1] > 1:
self.ui.generateXaction.setEnabled(True)
self.setEnabledMainActions(True)
else:
self.ui.generateXaction.setEnabled(False)
self.setEnabledMainActions(False)


def setEnabledMainActions(self, enabled):
for action in self.mainActions:
action.setEnabled(enabled)

@Slot()
def on_openfileaction_triggered(self):
Expand Down Expand Up @@ -85,18 +122,6 @@ def on_openfileaction_triggered(self):
self.model.updateAllData(data)
self.isDataChanged = False

if data.shape[1] == 1:
self.ui.generateXaction.setEnabled(True)
self.ui.varianceAnalysisAction.setEnabled(False)
self.ui.correlationAnalisisAction.setEnabled(False)
elif data.shape[1] > 1:
self.ui.generateXaction.setEnabled(True)
self.ui.varianceAnalysisAction.setEnabled(True)
self.ui.correlationAnalisisAction.setEnabled(True)
else:
self.ui.generateXaction.setEnabled(False)
self.ui.varianceAnalysisAction.setEnabled(False)
self.ui.correlationAnalisisAction.setEnabled(False)

@Slot()
def on_savefileaction_triggered(self):
Expand All @@ -112,10 +137,9 @@ def on_generateYaction_triggered(self):
gw = GenerateWindow()

if gw.exec():
y = np.random.normal(gw.mat, gw.deviation, size=(gw.count, 1))
y = generateYValues(gw.mat, gw.deviation, gw.count)
self.model.updateAllData(y.round(2))
self.isDataChanged = True
self.ui.generateXaction.setEnabled(True)

@Slot()
def on_generateXaction_triggered(self):
Expand All @@ -125,11 +149,8 @@ def on_generateXaction_triggered(self):
data = self.model.getData()
y = self.model.getY()
xValues = generateXValues(gfw.mat, gfw.deviation, gfw.typeConnection, y)
xValues = xValues.reshape(len(xValues), 1).round(2)
data = np.concatenate((data, xValues), axis=1)
data = np.concatenate((data, xValues.round(2)), axis=1)
self.model.updateAllData(data)
self.ui.varianceAnalysisAction.setEnabled(True)
self.ui.correlationAnalisisAction.setEnabled(True)
self.isDataChanged = True

@Slot()
Expand All @@ -147,6 +168,11 @@ def on_correlationAnalisisAction_triggered(self):
dw = CorrelationAnalysisWindow(self.model.getData())
dw.exec()

@Slot()
def on_linearPolynomAction_triggered(self):
dw = LinearPolynomWindow(self.model.getData())
dw.exec()

def closeEvent(self, event):
if self.isDataChanged:
file = ''
Expand Down
31 changes: 31 additions & 0 deletions statapp/models/linear_polynom_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#
# 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 <http://www.gnu.org/licenses/>.
#
from PySide2.QtCore import QModelIndex

from statapp.models.ro_table_model import ROTableModel


class LinearPolynomModel(ROTableModel):
def getHorizontalHeader(self):
return ['Коэффициент регрессии', 'Коэффициент значимости']

def getVerticalHeader(self):
count = (self.rowCount(QModelIndex()))
return ['Свободный член'] + [f'X{i}' for i in range(1, count)]
49 changes: 49 additions & 0 deletions statapp/ui/linear_polynom_window.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LinearPolynomWindow</class>
<widget class="QDialog" name="LinearPolynomWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>630</width>
<height>400</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>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout_3">
<property name="topMargin">
<number>10</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="residualVarianceLabel">
<property name="text">
<string>Остаточная дисперсия:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="residualVarianceValueLabel">
<property name="text">
<string>undefined</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
8 changes: 7 additions & 1 deletion statapp/ui/main_window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="filemenu">
Expand Down Expand Up @@ -69,6 +69,7 @@
<property name="title">
<string>Моделирование</string>
</property>
<addaction name="linearPolynomAction"/>
</widget>
<widget class="QMenu" name="helpmenu">
<property name="title">
Expand Down Expand Up @@ -123,6 +124,11 @@
<string>Корреляционный анализ</string>
</property>
</action>
<action name="linearPolynomAction">
<property name="text">
<string>Линейный полином</string>
</property>
</action>
</widget>
<resources/>
<connections/>
Expand Down
Loading

0 comments on commit 232de9f

Please sign in to comment.