import pytest
import numpy as np
from linreg_core import (
ols_regression,
ridge_regression,
lasso_regression,
elastic_net_regression,
polynomial_regression,
py_standardized_coefficients as standardized_coefficients,
py_shap_values_linear as shap_values_linear,
py_shap_values_ridge as shap_values_ridge,
py_shap_values_lasso as shap_values_lasso,
py_shap_values_elastic_net as shap_values_elastic_net,
py_shap_values_polynomial as shap_values_polynomial,
py_vif_ranking as vif_ranking,
py_vif_ranking_from_values as vif_ranking_from_values,
py_permutation_importance_ols as permutation_importance_ols,
py_permutation_importance_ridge as permutation_importance_ridge,
py_permutation_importance_lasso as permutation_importance_lasso,
py_permutation_importance_elastic_net as permutation_importance_elastic_net,
py_permutation_importance_loess as permutation_importance_loess,
py_feature_importance_ols as feature_importance_ols,
)
class TestStandardizedCoefficients:
def test_basic(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [10.0, 20.0, 30.0, 40.0, 50.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
names = ["Intercept", "X1", "X2"]
fit = ols_regression(y, [x1, x2], names)
result = standardized_coefficients(fit.coefficients, [x1, x2])
assert result.variable_names == ["X1", "X2"]
assert len(result.standardized_coefficients) == 2
assert result.y_std > 0
def test_with_custom_names(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1], ["Intercept", "X1"])
result = standardized_coefficients(
fit.coefficients,
[x1],
variable_names=["Temperature"]
)
assert result.variable_names == ["Temperature"]
def test_with_custom_y_std(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1], ["Intercept", "X1"])
result = standardized_coefficients(
fit.coefficients,
[x1],
y_std=2.5
)
assert result.y_std == 2.5
def test_ranking(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [3.0, 1.0, 5.0, 2.0, 4.0] y = [2.0, 4.0, 6.0, 8.0, 10.0]
fit = ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
result = standardized_coefficients(fit.coefficients, [x1, x2])
ranking = result.ranking()
assert len(ranking) == 2
assert ranking[0][1] >= ranking[1][1]
def test_summary(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1], ["Intercept", "X1"])
result = standardized_coefficients(fit.coefficients, [x1])
summary = result.summary()
assert "Standardized Coefficients" in summary
assert "X1" in summary
class TestShapValues:
def test_linear_shap(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
names = ["Intercept", "X1", "X2"]
fit = ols_regression(y, [x1, x2], names)
result = shap_values_linear(fit.coefficients, [x1, x2])
assert result.variable_names == ["X1", "X2"]
assert len(result.shap_values) == 5 assert len(result.shap_values[0]) == 2 assert len(result.mean_abs_shap) == 2
def test_shap_with_custom_names(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1], ["Intercept", "X1"])
result = shap_values_linear(
fit.coefficients,
[x1],
variable_names=["Temperature"]
)
assert result.variable_names == ["Temperature"]
def test_local_accuracy(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1], ["Intercept", "X1"])
shap = shap_values_linear(fit.coefficients, [x1])
x_mean = sum(x1) / len(x1)
expected_shap = fit.coefficients[1] * (x1[0] - x_mean)
assert abs(shap.shap_values[0][0] - expected_shap) < 1e-10
def test_observation_contribution(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
shap = shap_values_linear(fit.coefficients, [x1, x2])
contrib = shap.observation_contribution(0)
assert len(contrib) == 2
assert contrib[0][0] == "X1"
assert contrib[1][0] == "X2"
def test_shap_ranking(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [3.0, 1.0, 5.0, 2.0, 4.0] y = [2.0, 4.0, 6.0, 8.0, 10.0]
fit = ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
shap = shap_values_linear(fit.coefficients, [x1, x2])
ranking = shap.ranking()
assert len(ranking) == 2
assert ranking[0][1] >= ranking[1][1]
class TestShapRegularized:
def test_ridge_shap(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ridge_regression(y, [x1, x2], lambda_val=1.0)
result = shap_values_ridge([x1, x2], fit)
assert result.variable_names == ["X1", "X2"]
assert len(result.shap_values) == 5
assert len(result.shap_values[0]) == 2
def test_lasso_shap(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = lasso_regression(y, [x1, x2], lambda_val=0.1)
result = shap_values_lasso([x1, x2], fit)
assert result.variable_names == ["X1", "X2"]
assert len(result.shap_values) == 5
def test_elastic_net_shap(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = elastic_net_regression(y, [x1, x2], lambda_val=0.1, alpha=0.5)
result = shap_values_elastic_net([x1, x2], fit)
assert result.variable_names == ["X1", "X2"]
assert len(result.shap_values) == 5
def test_polynomial_shap(self):
x = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [1.0, 4.0, 9.0, 16.0, 25.0]
fit = polynomial_regression(y, x, degree=2)
result = shap_values_polynomial(x, fit)
assert len(result.variable_names) == 2 assert len(result.shap_values) == 5
assert len(result.shap_values[0]) == 2
class TestVifRanking:
def test_vif_from_ols_result(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [1.0, 2.0, 3.0, 4.0, 5.0] x3 = [2.0, 4.0, 6.0, 8.0, 10.0] y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x1, x2, x3], ["Intercept", "X1", "X2", "X3"])
result = vif_ranking(fit)
assert len(result.variable_names) == 3
assert len(result.vif_values) == 3
assert result.vif_values[2] > 100
def test_vif_from_values(self):
vif_values = [1.2, 3.5, 8.5]
names = ["Low", "Medium", "High"]
result = vif_ranking_from_values(vif_values, variable_names=names)
assert result.variable_names == names
assert result.vif_values == vif_values
def test_vif_default_names(self):
vif_values = [1.2, 3.5]
result = vif_ranking_from_values(vif_values)
assert result.variable_names == ["X1", "X2"]
def test_vif_interpretation(self):
result = vif_ranking_from_values([2.0, 7.0, 15.0])
interpretations = result.interpretations()
assert "Low" in interpretations[0]
assert "Moderate" in interpretations[1]
assert "High" in interpretations[2]
def test_vif_ranking_ascending(self):
vif_values = [8.5, 1.2, 3.5]
result = vif_ranking_from_values(vif_values)
ranking = result.ranking()
assert ranking[0][1] == 1.2
assert ranking[1][1] == 3.5
assert ranking[2][1] == 8.5
class TestPermutationImportance:
def test_ols_permutation_importance(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [0.5, 1.0, 1.5, 2.0, 2.5]
y = [1.0, 2.0, 3.0, 4.0, 5.0]
fit = ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
result = permutation_importance_ols(y, [x1, x2], fit, n_permutations=10, seed=42)
assert len(result.variable_names) == 2
assert len(result.importance) == 2
assert result.baseline_score > 0.8 assert result.n_permutations == 10
def test_ols_permutation_with_names(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [1.0, 2.0, 3.0, 4.0, 5.0]
fit = ols_regression(y, [x1], ["Intercept", "X1"])
result = permutation_importance_ols(
y, [x1], fit,
variable_names=["Temperature"],
n_permutations=10,
seed=42
)
assert result.variable_names == ["Temperature"]
def test_ridge_permutation_importance(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ridge_regression(y, [x1, x2], lambda_val=1.0)
result = permutation_importance_ridge(
y, [x1, x2], fit,
n_permutations=10,
seed=42
)
assert len(result.variable_names) == 2
assert len(result.importance) == 2
def test_lasso_permutation_importance(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = lasso_regression(y, [x1, x2], lambda_val=0.1)
result = permutation_importance_lasso(
y, [x1, x2], fit,
n_permutations=10,
seed=42
)
assert len(result.variable_names) == 2
assert len(result.importance) == 2
def test_elastic_net_permutation_importance(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = elastic_net_regression(y, [x1, x2], lambda_val=0.1, alpha=0.5)
result = permutation_importance_elastic_net(
y, [x1, x2], fit,
n_permutations=10,
seed=42
)
assert len(result.variable_names) == 2
assert len(result.importance) == 2
def test_loess_permutation_importance(self):
x = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
result = permutation_importance_loess(
y, [x],
span=0.75,
degree=1,
n_permutations=5, seed=42
)
assert len(result.variable_names) == 1
assert len(result.importance) == 1
def test_permutation_ranking(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [0.1, 0.2, 0.3, 0.4, 0.5] y = [1.0, 2.0, 3.0, 4.0, 5.0]
fit = ols_regression(y, [x1, x2], ["Intercept", "X1", "X2"])
result = permutation_importance_ols(
y, [x1, x2], fit,
n_permutations=20,
seed=42
)
ranking = result.ranking()
assert len(ranking) == 2
assert ranking[0][1] >= ranking[1][1]
class TestCombinedFeatureImportance:
def test_combined_function(self):
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
x2 = [2.0, 4.0, 5.0, 4.0, 3.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
names = ["X1", "X2"]
result = feature_importance_ols(y, [x1, x2], names, n_permutations=10, seed=42)
assert "standardized_coefficients" in result
assert "shap_values" in result
assert "vif_ranking" in result
assert "permutation_importance" in result
std_coefs = result["standardized_coefficients"]
assert len(std_coefs.standardized_coefficients) == 2
shap = result["shap_values"]
assert len(shap.shap_values) == 5
vif = result["vif_ranking"]
assert len(vif.vif_values) == 2
perm = result["permutation_importance"]
assert len(perm.importance) == 2
class TestEdgeCases:
def test_single_predictor(self):
x = [1.0, 2.0, 3.0, 4.0, 5.0]
y = [2.5, 3.7, 4.2, 5.1, 6.3]
fit = ols_regression(y, [x], ["Intercept", "X"])
shap = shap_values_linear(fit.coefficients, [x])
assert len(shap.shap_values[0]) == 1