Skip to content

Commit ae2943f

Browse files
committed
Avoid negative differences in ftest
We already enforce that all models are nested in the same direction so the sign doesn't add any information. Fixes #576.
1 parent 1460fff commit ae2943f

File tree

3 files changed

+57
-56
lines changed

3 files changed

+57
-56
lines changed

docs/src/index.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,12 +138,12 @@ julia> ols_sq = lm(@formula(y ~ x + x^2), data);
138138
139139
julia> ftest(ols_lin, ols_sq)
140140
F-test: 2 models fitted on 50 observations
141-
────────────────────────────────────────────────────────────────────────────────
142-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
143-
────────────────────────────────────────────────────────────────────────────────
144-
[1] 3 1731979.2266 0.9399
145-
[2] 4 1 40.7581 -1731938.4685 1.0000 0.0601 1997177.0357 <1e-99
146-
────────────────────────────────────────────────────────────────────────────────
141+
────────────────────────────────────────────────────────────────────────────────
142+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
143+
────────────────────────────────────────────────────────────────────────────────
144+
[1] 3 1731979.2266 0.9399
145+
[2] 4 1 40.7581 1731938.4685 1.0000 0.0601 1997177.0357 <1e-99
146+
────────────────────────────────────────────────────────────────────────────────
147147
```
148148

149149
## Methods applied to fitted models

src/ftest.jl

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ function ftest(mod::LinearModel)
6363

6464
n = Int(nobs(mod))
6565
p = dof(mod) - 2 # -2 for intercept and dispersion parameter
66-
fstat = ((tss - rss) / rss) * ((n - p - 1) / p)
66+
fstat = abs(((tss - rss) / rss) * ((n - p - 1) / p))
6767
fdist = FDist(p, dof_residual(mod))
6868

69-
return SingleFTestResult(n, p, promote(fstat, ccdf(fdist, abs(fstat)))...)
69+
return SingleFTestResult(n, p, promote(fstat, ccdf(fdist, fstat))...)
7070
end
7171

7272
"""
@@ -77,9 +77,10 @@ the one model fits significantly better than the other. Models must have been fi
7777
on the same data, and be nested either in forward or backward direction.
7878
7979
A table is returned containing consumed degrees of freedom (DOF),
80-
difference in DOF from the preceding model, sum of squared residuals (SSR), difference in
81-
SSR from the preceding model, R², difference in R² from the preceding model, and F-statistic
82-
and p-value for the comparison between the two models.
80+
absolute difference in DOF from the preceding model, sum of squared residuals (SSR),
81+
absolute difference in SSR from the preceding model, R², absolute difference in R²
82+
from the preceding model, and F-statistic and p-value for the comparison
83+
between the two models.
8384
8485
!!! note
8586
This function can be used to perform an ANOVA by testing the relative fit of two models
@@ -110,22 +111,22 @@ julia> bigmodel = lm(@formula(Result ~ 1 + Treatment + Other), dat);
110111
111112
julia> ftest(nullmodel, model)
112113
F-test: 2 models fitted on 12 observations
113-
────────────────────────────────────────────────────────────────
114-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
115-
────────────────────────────────────────────────────────────────
116-
[1] 2 3.2292 0.0000
117-
[2] 3 1 0.1283 -3.1008 0.9603 0.9603 241.6234 <1e-07
118-
────────────────────────────────────────────────────────────────
114+
────────────────────────────────────────────────────────────────
115+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
116+
────────────────────────────────────────────────────────────────
117+
[1] 2 3.2292 0.0000
118+
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
119+
────────────────────────────────────────────────────────────────
119120
120121
julia> ftest(nullmodel, model, bigmodel)
121122
F-test: 3 models fitted on 12 observations
122-
────────────────────────────────────────────────────────────────
123-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
124-
────────────────────────────────────────────────────────────────
125-
[1] 2 3.2292 0.0000
126-
[2] 3 1 0.1283 -3.1008 0.9603 0.9603 241.6234 <1e-07
127-
[3] 5 2 0.1017 -0.0266 0.9685 0.0082 1.0456 0.3950
128-
────────────────────────────────────────────────────────────────
123+
────────────────────────────────────────────────────────────────
124+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
125+
────────────────────────────────────────────────────────────────
126+
[1] 2 3.2292 0.0000
127+
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
128+
[3] 5 2 0.1017 0.0266 0.9685 0.0082 1.0456 0.3950
129+
────────────────────────────────────────────────────────────────
129130
```
130131
"""
131132
function ftest(mods::LinearModel...; atol::Real=0.0)
@@ -153,7 +154,7 @@ function ftest(mods::LinearModel...; atol::Real=0.0)
153154
SSR = deviance.(mods)
154155

155156
df = dof.(mods)
156-
Δdf = _diff(df)
157+
Δdf = abs.(_diff(df))
157158
dfr = Int.(dof_residual.(mods))
158159
MSR1 = _diffn(SSR) ./ Δdf
159160
MSR2 = (SSR ./ dfr)
@@ -165,8 +166,8 @@ function ftest(mods::LinearModel...; atol::Real=0.0)
165166
dfr_big = dfr[1:(end - 1)]
166167
end
167168

168-
fstat = (NaN, (MSR1 ./ MSR2)...)
169-
pval = (NaN, ccdf.(FDist.(abs.(Δdf), dfr_big), abs.(fstat[2:end]))...)
169+
fstat = abs.((NaN, (MSR1 ./ MSR2)...))
170+
pval = (NaN, ccdf.(FDist.(Δdf, dfr_big), fstat[2:end])...)
170171

171172
return FTestResult(Int(nobs(mods[1])), SSR, df, r2.(mods), fstat, pval)
172173
end
@@ -179,9 +180,9 @@ function show(io::IO, ftr::SingleFTestResult)
179180
end
180181

181182
function show(io::IO, ftr::FTestResult{N}) where {N}
182-
Δdof = _diff(ftr.dof)
183-
Δssr = _diff(ftr.ssr)
184-
ΔR² = _diff(ftr.r2)
183+
Δdof = abs.(_diff(ftr.dof))
184+
Δssr = abs.(_diff(ftr.ssr))
185+
ΔR² = abs.(_diff(ftr.r2))
185186

186187
nc = 9
187188
nr = N

test/runtests.jl

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,24 +1420,24 @@ end
14201420
@test ft1a.pval[2] 2.481215056713184e-8
14211421
@test sprint(show, ft1a) == """
14221422
F-test: 2 models fitted on 12 observations
1423-
────────────────────────────────────────────────────────────────
1424-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1425-
────────────────────────────────────────────────────────────────
1426-
[1] 3 0.1283 0.9603
1427-
[2] 2 -1 3.2292 3.1008 0.0000 -0.9603 241.6234 <1e-07
1428-
────────────────────────────────────────────────────────────────"""
1423+
────────────────────────────────────────────────────────────────
1424+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1425+
────────────────────────────────────────────────────────────────
1426+
[1] 3 0.1283 0.9603
1427+
[2] 2 1 3.2292 3.1008 0.0000 0.9603 241.6234 <1e-07
1428+
────────────────────────────────────────────────────────────────"""
14291429

14301430
ft1b = ftest(nullmod, mod)
14311431
@test isnan(ft1b.pval[1])
14321432
@test ft1b.pval[2] 2.481215056713184e-8
14331433
@test sprint(show, ft1b) == """
14341434
F-test: 2 models fitted on 12 observations
1435-
────────────────────────────────────────────────────────────────
1436-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1437-
────────────────────────────────────────────────────────────────
1438-
[1] 2 3.2292 0.0000
1439-
[2] 3 1 0.1283 -3.1008 0.9603 0.9603 241.6234 <1e-07
1440-
────────────────────────────────────────────────────────────────"""
1435+
────────────────────────────────────────────────────────────────
1436+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1437+
────────────────────────────────────────────────────────────────
1438+
[1] 2 3.2292 0.0000
1439+
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
1440+
────────────────────────────────────────────────────────────────"""
14411441

14421442
bigmod = lm(@formula(Result~Treatment+Other), d)
14431443
ft2a = ftest(nullmod, mod, bigmod)
@@ -1446,27 +1446,27 @@ end
14461446
@test ft2a.pval[3] 0.3949973540194818
14471447
@test sprint(show, ft2a) == """
14481448
F-test: 3 models fitted on 12 observations
1449-
────────────────────────────────────────────────────────────────
1450-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1451-
────────────────────────────────────────────────────────────────
1452-
[1] 2 3.2292 0.0000
1453-
[2] 3 1 0.1283 -3.1008 0.9603 0.9603 241.6234 <1e-07
1454-
[3] 5 2 0.1017 -0.0266 0.9685 0.0082 1.0456 0.3950
1455-
────────────────────────────────────────────────────────────────"""
1449+
────────────────────────────────────────────────────────────────
1450+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1451+
────────────────────────────────────────────────────────────────
1452+
[1] 2 3.2292 0.0000
1453+
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
1454+
[3] 5 2 0.1017 0.0266 0.9685 0.0082 1.0456 0.3950
1455+
────────────────────────────────────────────────────────────────"""
14561456

14571457
ft2b = ftest(bigmod, mod, nullmod)
14581458
@test isnan(ft2b.pval[1])
14591459
@test ft2b.pval[2] 0.3949973540194818
14601460
@test ft2b.pval[3] 2.481215056713184e-8
14611461
@test sprint(show, ft2b) == """
14621462
F-test: 3 models fitted on 12 observations
1463-
────────────────────────────────────────────────────────────────
1464-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1465-
────────────────────────────────────────────────────────────────
1466-
[1] 5 0.1017 0.9685
1467-
[2] 3 -2 0.1283 0.0266 0.9603 -0.0082 1.0456 0.3950
1468-
[3] 2 -1 3.2292 3.1008 0.0000 -0.9603 241.6234 <1e-07
1469-
────────────────────────────────────────────────────────────────"""
1463+
────────────────────────────────────────────────────────────────
1464+
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1465+
────────────────────────────────────────────────────────────────
1466+
[1] 5 0.1017 0.9685
1467+
[2] 3 2 0.1283 0.0266 0.9603 0.0082 1.0456 0.3950
1468+
[3] 2 1 3.2292 3.1008 0.0000 0.9603 241.6234 <1e-07
1469+
────────────────────────────────────────────────────────────────"""
14701470

14711471
@test_throws ArgumentError ftest(mod, bigmod, nullmod)
14721472
@test_throws ArgumentError ftest(nullmod, bigmod, mod)

0 commit comments

Comments
 (0)