2022-10-13 15:57:56 +11:00
|
|
|
# scipy-yli: Helpful SciPy utilities and recipes
|
|
|
|
# Copyright © 2022 Lee Yingtong Li (RunasSudo)
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
from pytest import approx
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
import pandas as pd
|
|
|
|
import statsmodels.api as sm
|
|
|
|
|
|
|
|
import yli
|
|
|
|
|
|
|
|
def test_regress_ols_ol11_4():
|
|
|
|
"""Compare yli.regress for Ott & Longnecker (2016) example 11.4/11.7"""
|
|
|
|
|
|
|
|
df = pd.DataFrame({
|
|
|
|
'SoilPh': [3.3, 3.4, 3.4, 3.5, 3.6, 3.6, 3.7, 3.7, 3.8, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 5.0, 5.1, 5.2],
|
|
|
|
'GrowthRet': [17.78, 21.59, 23.84, 15.13, 23.45, 20.87, 17.78, 20.09, 17.78, 12.46, 14.95, 15.87, 17.45, 14.35, 14.64, 17.25, 12.57, 7.15, 7.50, 4.34]
|
|
|
|
})
|
|
|
|
|
|
|
|
result = yli.regress(sm.OLS, df, 'GrowthRet', 'SoilPh')
|
|
|
|
|
|
|
|
assert result.dof_model == 1
|
|
|
|
assert result.dof_resid == 18
|
|
|
|
assert result.f_statistic == approx(52.01, abs=0.01)
|
|
|
|
assert result.ftest().pvalue < 0.0005
|
|
|
|
assert result.rsquared == approx(0.7429, abs=0.0001)
|
|
|
|
|
|
|
|
assert result.beta['Intercept'].point == approx(47.48, abs=0.01)
|
|
|
|
assert result.pvalues['Intercept'] < 0.0005
|
|
|
|
assert result.beta['SoilPh'].point == approx(-7.86, abs=0.01)
|
|
|
|
assert result.pvalues['SoilPh'] < 0.0005
|
|
|
|
|
|
|
|
assert result.beta['SoilPh'].ci_lower == approx(-10.15, abs=0.01)
|
|
|
|
assert result.beta['SoilPh'].ci_upper == approx(-5.57, abs=0.01)
|
|
|
|
|
|
|
|
def test_regress_ols_ol13_5():
|
|
|
|
"""Compare yli.regress for Ott & Longnecker (2016) chapter 13.5"""
|
|
|
|
|
|
|
|
df = pd.DataFrame({
|
|
|
|
'C': [460.05, 452.99, 443.22, 652.32, 642.23, 345.39, 272.37, 317.21, 457.12, 690.19, 350.63, 402.59, 412.18, 495.58, 394.36, 423.32, 712.27, 289.66, 881.24, 490.88, 567.79, 665.99, 621.45, 608.8, 473.64, 697.14, 207.51, 288.48, 284.88, 280.36, 217.38, 270.71],
|
|
|
|
'D': [68.58, 67.33, 67.33, 68, 68, 67.92, 68.17, 68.42, 68.42, 68.33, 68.58, 68.75, 68.42, 68.92, 68.92, 68.42, 69.5, 68.42, 69.17, 68.92, 68.75, 70.92, 69.67, 70.08, 70.42, 71.08, 67.25, 67.17, 67.83, 67.83, 67.25, 67.83],
|
|
|
|
'T1': [14, 10, 10, 11, 11, 13, 12, 14, 15, 12, 12, 13, 15, 17, 13, 11, 18, 15, 15, 16, 11, 22, 16, 19, 19, 20, 13, 9, 12, 12, 13, 7],
|
|
|
|
'T2': [46, 73, 85, 67, 78, 51, 50, 59, 55, 71, 64, 47, 62, 52, 65, 67, 60, 76, 67, 59, 70, 57, 59, 58, 44, 57, 63, 48, 63, 71, 72, 80],
|
|
|
|
'S': [687, 1065, 1065, 1065, 1065, 514, 822, 457, 822, 792, 560, 790, 530, 1050, 850, 778, 845, 530, 1090, 1050, 913, 828, 786, 821, 538, 1130, 745, 821, 886, 886, 745, 886],
|
|
|
|
'PR': [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1],
|
|
|
|
'NE': [1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
|
|
'CT': [0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0],
|
|
|
|
'BW': [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1],
|
|
|
|
'N': [14, 1, 1, 12, 12, 3, 5, 1, 5, 2, 3, 6, 2, 7, 16, 3, 17, 2, 1, 8, 15, 20, 18, 3, 19, 21, 8, 7, 11, 11, 8, 11],
|
|
|
|
'PT': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]
|
|
|
|
})
|
|
|
|
df['LNC'] = np.log(df['C'])
|
|
|
|
|
|
|
|
result = yli.regress(sm.OLS, df, 'LNC', 'D + T1 + T2 + S + PR + NE + CT + BW + N + PT')
|
|
|
|
|
|
|
|
assert result.dof_model == 10
|
|
|
|
assert result.dof_resid == 21
|
|
|
|
assert result.f_statistic == approx(13.28, abs=0.01)
|
|
|
|
assert result.ftest().pvalue < 0.00005
|
|
|
|
assert result.rsquared == approx(0.8635, abs=0.0001)
|
|
|
|
|
|
|
|
assert result.beta['Intercept'].point == approx(-10.63398, abs=0.00001)
|
|
|
|
assert result.pvalues['Intercept'] == approx(0.0766, abs=0.0001)
|
|
|
|
assert result.beta['D'].point == approx(0.22760, abs=0.00001)
|
|
|
|
assert result.pvalues['D'] == approx(0.0157, abs=0.0001)
|
|
|
|
assert result.beta['T1'].point == approx(0.00525, abs=0.00001)
|
|
|
|
assert result.pvalues['T1'] == approx(0.8161, abs=0.0001)
|
|
|
|
assert result.beta['T2'].point == approx(0.00561, abs=0.00001)
|
|
|
|
assert result.pvalues['T2'] == approx(0.2360, abs=0.0001)
|
|
|
|
assert result.beta['S'].point == approx(0.00088369, abs=0.00000001)
|
|
|
|
assert result.pvalues['S'] < 0.0001
|
|
|
|
assert result.beta['PR'].point == approx(-0.10813, abs=0.00001)
|
|
|
|
assert result.pvalues['PR'] == approx(0.2094, abs=0.0001)
|
|
|
|
assert result.beta['NE'].point == approx(0.25949, abs=0.00001)
|
|
|
|
assert result.pvalues['NE'] == approx(0.0036, abs=0.0001)
|
|
|
|
assert result.beta['CT'].point == approx(0.11554, abs=0.00001)
|
|
|
|
assert result.pvalues['CT'] == approx(0.1150, abs=0.0001)
|
|
|
|
assert result.beta['BW'].point == approx(0.03680, abs=0.00001)
|
|
|
|
assert result.pvalues['BW'] == approx(0.7326, abs=0.0001)
|
|
|
|
assert result.beta['N'].point == approx(-0.01203, abs=0.00001)
|
|
|
|
assert result.pvalues['N'] == approx(0.1394, abs=0.0001)
|
|
|
|
assert result.beta['PT'].point == approx(-0.22197, abs=0.00001)
|
|
|
|
assert result.pvalues['PT'] == approx(0.1035, abs=0.0001)
|
|
|
|
|
|
|
|
def test_regress_logit_ol12_23():
|
|
|
|
"""Compare yli.regress for Ott & Longnecker (2016) chapter 12.23"""
|
|
|
|
|
|
|
|
df = pd.DataFrame({
|
|
|
|
'Unhealthy': [False, False, False, False, False, False, False, True, False, False, False, True, False, False, False, False, False, False, True, False, True, False, False, False, False, False, True, False, False, True, False, False],
|
|
|
|
'Fibrinogen': [2.52, 2.46, 2.29, 3.15, 2.88, 2.29, 2.99, 2.38, 2.56, 3.22, 2.35, 3.53, 2.65, 2.15, 3.32, 2.23, 2.19, 2.21, 5.06, 2.68, 2.09, 2.54, 2.18, 2.68, 3.41, 3.15, 3.35, 2.60, 2.28, 3.93, 2.60, 3.34],
|
|
|
|
'GammaGlobulin': [38, 36, 36, 36, 30, 31, 36, 37, 31, 38, 29, 46, 46, 31, 35, 37, 33, 37, 37, 34, 44, 28, 31, 39, 37, 39, 32, 38, 36, 32, 41, 30]
|
|
|
|
})
|
|
|
|
|
|
|
|
result = yli.regress(sm.Logit, df, 'Unhealthy', 'Fibrinogen + GammaGlobulin')
|
|
|
|
|
|
|
|
# Some numerical differences as intercept term is very negative
|
|
|
|
lrtest_result = result.lrtest_null()
|
|
|
|
assert lrtest_result.statistic == approx(7.9138, rel=0.01)
|
|
|
|
assert lrtest_result.dof == 2
|
|
|
|
assert lrtest_result.pvalue == approx(0.0191, rel=0.02)
|
|
|
|
|
|
|
|
expbeta_fib = np.exp(result.beta['Fibrinogen'])
|
|
|
|
assert expbeta_fib.point == approx(6.756, rel=0.01)
|
|
|
|
assert expbeta_fib.ci_lower == approx(1.007, rel=0.01)
|
|
|
|
assert expbeta_fib.ci_upper == approx(45.308, rel=0.02)
|
|
|
|
|
|
|
|
expbeta_gam = np.exp(result.beta['GammaGlobulin'])
|
|
|
|
assert expbeta_gam.point == approx(1.169, abs=0.001)
|
|
|
|
assert expbeta_gam.ci_lower == approx(0.924, abs=0.001)
|
|
|
|
assert expbeta_gam.ci_upper == approx(1.477, abs=0.001)
|
2022-10-13 17:23:29 +11:00
|
|
|
|
|
|
|
def test_regress_penalisedlogit_kleinman():
|
|
|
|
"""Compare yli.regress with yli.PenalisedLogit for http://sas-and-r.blogspot.com/2010/11/example-815-firth-logistic-regression.html"""
|
|
|
|
|
|
|
|
df = pd.DataFrame({
|
|
|
|
'Pred': [1] * 20 + [0] * 220,
|
|
|
|
'Outcome': [1] * 40 + [0] * 200
|
|
|
|
})
|
|
|
|
|
|
|
|
result = yli.regress(yli.PenalisedLogit, df, 'Outcome', 'Pred', exp=False)
|
|
|
|
|
|
|
|
assert result.dof_model == 1
|
|
|
|
assert result.beta['(Intercept)'].point == approx(-2.280389)
|
|
|
|
assert result.beta['(Intercept)'].ci_lower == approx(-2.765427)
|
|
|
|
assert result.beta['(Intercept)'].ci_upper == approx(-1.851695)
|
|
|
|
assert result.pvalues['(Intercept)'] < 0.0001
|
|
|
|
assert result.beta['Pred'].point == approx(5.993961)
|
|
|
|
assert result.beta['Pred'].ci_lower == approx(3.947048)
|
|
|
|
assert result.beta['Pred'].ci_upper == approx(10.852893)
|
|
|
|
assert result.pvalues['Pred'] < 0.0001
|
|
|
|
|
|
|
|
lrtest_result = result.lrtest_null()
|
|
|
|
assert lrtest_result.statistic == approx(78.95473)
|
|
|
|
assert lrtest_result.dof == 1
|
|
|
|
assert lrtest_result.pvalue < 0.0001
|