Compare commits

...

3 Commits

Author SHA1 Message Date
github-actions[bot]
08fd512a79
chore(main): release 0.13.0 (#125)
🤖 I have created a release *beep* *boop*
---


##
[0.13.0](https://github.com/shizand/statapp/compare/v0.12.5...v0.13.0)
(2024-02-29)


### Новые функции

* добавлены весовые коэффициенты
([#126](https://github.com/shizand/statapp/issues/126))
([1900785](1900785502))


### Исправления

* исправлена "Остаточная дисперсия (масштабированная)"
([#124](https://github.com/shizand/statapp/issues/124))
([babfd48](babfd48ee1))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-29 21:39:28 +03:00
1900785502
feat: добавлены весовые коэффициенты (#126) 2024-02-29 21:16:55 +03:00
babfd48ee1
fix: исправлена "Остаточная дисперсия (масштабированная)" (#124) 2024-02-29 19:14:03 +03:00
5 changed files with 74 additions and 65 deletions

View File

@ -1,5 +1,17 @@
# Changelog
## [0.13.0](https://github.com/shizand/statapp/compare/v0.12.5...v0.13.0) (2024-02-29)
### Новые функции
* добавлены весовые коэффициенты ([#126](https://github.com/shizand/statapp/issues/126)) ([1900785](https://github.com/shizand/statapp/commit/1900785502d9066b31ed5311fab334f21eb9454e))
### Исправления
* исправлена "Остаточная дисперсия (масштабированная)" ([#124](https://github.com/shizand/statapp/issues/124)) ([babfd48](https://github.com/shizand/statapp/commit/babfd48ee15504342b01d9a1a98fcc1aafc890dc))
## [0.12.5](https://github.com/shizand/statapp/compare/v0.12.4...v0.12.5) (2024-02-22)

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "statapp"
version = "0.12.5"
version = "0.13.0"
description = ""
authors = [
"Maxim Slipenko <statapp@maks1ms.addy.io>"

View File

@ -25,6 +25,7 @@ import sympy as sp
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
DIRECT_LINK = 0
@ -81,93 +82,82 @@ class RegressionResult:
monomials: list
def linearPolynom(data):
def _prepareDataAndFeatures(data, degree):
y = data[:, 0]
x = data[:, 1:]
polyFeatures = PolynomialFeatures(degree=1, include_bias=False)
polyFeatures = PolynomialFeatures(degree=degree, include_bias=False)
xPoly = polyFeatures.fit_transform(x)
return y, x, xPoly, polyFeatures
def _trainModelAndPredict(y, xPoly):
model = LinearRegression(fit_intercept=True)
model.fit(xPoly, y)
params = np.hstack([model.intercept_, model.coef_])
predictions = model.predict(xPoly)
residuals = y - predictions
return model, predictions
def _calculateStatistics(y, x, xPoly, predictions, model, polyFeatures):
# Рассчитываем Среднеквадратическую ошибку (MSE) между фактическими и прогнозируемыми значениями
mse = mean_squared_error(y, predictions)
rSquared = model.score(xPoly, y)
# Рассчитываем коэффициент детерминации R^2, который
# показывает долю вариации зависимой переменной, объясненную моделью
rSquared = r2_score(y, predictions)
# Определяем количество наблюдений
n = xPoly.shape[0]
# Определяем количество предикторов (признаков) плюс один для свободного члена
k = xPoly.shape[1] + 1
# Рассчитываем F-статистику для оценки значимости всей регрессионной модели
fStatistic = (rSquared / (k - 1)) / ((1 - rSquared) / (n - k))
xWithIntercept = np.hstack([np.ones((n, 1)), xPoly])
varB = mse * np.linalg.inv(xWithIntercept.T @ xWithIntercept).diagonal()
seB = np.sqrt(varB)
tStats = params / seB
monomials = ['c'] + ['x' + str(i) for i in range(1, x.shape[1] + 1)]
residualVariance = np.var(residuals, ddof=k)
scaledResidualVariance = residualVariance / (n - k)
paramsAndTStats = np.vstack((params, tStats)).T
return RegressionResult(
paramsAndTStats,
residualVariance,
scaledResidualVariance,
rSquared,
fStatistic,
monomials
)
def squaredPolynom(data):
y = data[:, 0]
x = data[:, 1:]
polyFeatures = PolynomialFeatures(degree=2, include_bias=False)
xPoly = polyFeatures.fit_transform(x)
model = LinearRegression(fit_intercept=True)
model.fit(xPoly, y)
# Собираем параметры модели, включая свободный член и коэффициенты перед переменными
params = np.hstack([model.intercept_, model.coef_])
predictions = model.predict(xPoly)
# Вычисляем остатки модели как разницу между фактическими и прогнозируемыми значениями
residuals = y - predictions
mse = mean_squared_error(y, predictions)
rSquared = model.score(xPoly, y)
n = xPoly.shape[0]
k = xPoly.shape[1] + 1
fStatistic = (rSquared / (k - 1)) / ((1 - rSquared) / (n - k))
# Добавляем столбец единиц к матрице признаков для учета свободного члена в регрессионной модели
xWithIntercept = np.hstack([np.ones((n, 1)), xPoly])
# Рассчитываем дисперсии коэффициентов модели
varB = mse * np.linalg.pinv(xWithIntercept.T @ xWithIntercept).diagonal()
# Вычисляем стандартные ошибки коэффициентов, берем корень из дисперсий
seB = np.sqrt(np.maximum(varB, 0))
# Рассчитываем t-статистики для каждого коэффициента
tStats = params / seB
# Рассчитываем дисперсию остатков с поправкой на количество параметров
residualVariance = np.var(residuals, ddof=k)
# Рассчитываем скорректированную дисперсию остатков
scaledResidualVariance = 1 - rSquared
# Генерируем список мономов (названий признаков после
# полиномиализации), добавляя константу для свободного члена
monomials = ['c'] + list(
polyFeatures.get_feature_names_out(['x' + str(i) for i in range(1, x.shape[1] + 1)])
)
# Заменяем пробелы на звездочки для представления умножения в названиях мономов
monomials = [monomial.replace(' ', '*') for monomial in monomials]
weightsCoef = np.concatenate((np.array([0]), tStats[1:] / np.sum(tStats[1:])))
# Возвращаем рассчитанные статистики и названия мономов
return (params, tStats, weightsCoef,
residualVariance, scaledResidualVariance,
rSquared, fStatistic, monomials)
residualVariance = np.var(residuals, ddof=k)
scaledResidualVariance = residualVariance / (n - k)
paramsAndTStats = np.vstack((params, tStats)).T
def _regressionAnalysis(data, degree):
y, x, xPoly, polyFeatures = _prepareDataAndFeatures(
data, degree
)
model, predictions = _trainModelAndPredict(y, xPoly)
(params, tStats, weightsCoef, residualVariance,
scaledResidualVariance, rSquared, fStatistic, monomials) = (
_calculateStatistics(
y,
x,
xPoly,
predictions,
model,
polyFeatures
))
return RegressionResult(
paramsAndTStats,
np.vstack((params, tStats, weightsCoef)).T,
residualVariance,
scaledResidualVariance,
rSquared,
@ -175,6 +165,13 @@ def squaredPolynom(data):
monomials
)
def linearPolynom(data):
return _regressionAnalysis(data, 1)
def squaredPolynom(data):
return _regressionAnalysis(data, 2)
def prediction(inputData, result: RegressionResult):
inputs = inputData[:, 1:]

View File

@ -28,7 +28,7 @@ class RegressionResultModel(ROTableModel):
self._monomials = result.monomials
def getHorizontalHeader(self):
return ['Коэффициент регрессии', 'Коэффициент значимости']
return ['Коэффициент регрессии', 'Коэффициент значимости', 'Весовые коэффициенты']
def getVerticalHeader(self):
return self._monomials

View File

@ -116,4 +116,4 @@ class TransformPolynomWindow(QDialog):
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.ui.rSquaredValueLabel.setText(str(result.rSquared))