Skip to content

Commit f759de4

Browse files
committed
Adjust ftest
This commit changes the degrees of freedom shown in the table from the number of estimated parameters (dof) to be the number of degrees of freedom for the model (residual_dof). This makes it possible to easily calculate the F-test statistic from the other quantities in the table with the usual formulas. Since nobs now returns floats, residual_dof also generally returns floats, so the degrees of freedom parameters are now stored as floats instead of integers. The commit removes the R-squared quantities. They are not needed and I don't think they add value. The commit also removes numbering of the rows in the output. I don't think it is helpful. The show method is now for MIME"text/plain" since that is more appropriate for a "decorated" show method that span multiple lines. In most cases this won't be visible to users.
1 parent e26c5d5 commit f759de4

File tree

2 files changed

+69
-75
lines changed

2 files changed

+69
-75
lines changed

src/ftest.jl

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@ struct SingleFTestResult
55
pval::Float64
66
end
77

8-
mutable struct FTestResult{N}
8+
struct FTestResult{N}
99
nobs::Int
1010
ssr::NTuple{N,Float64}
11-
dof::NTuple{N,Int}
12-
r2::NTuple{N,Float64}
11+
dof::NTuple{N,Float64}
1312
fstat::NTuple{N,Float64}
1413
pval::NTuple{N,Float64}
1514
end
@@ -79,10 +78,9 @@ For each sequential pair of linear models in `mod...`, perform an F-test to dete
7978
the one model fits significantly better than the other. Models must have been fitted
8079
on the same data, and be nested either in forward or backward direction.
8180
82-
A table is returned containing consumed degrees of freedom (DOF),
83-
absolute difference in DOF from the preceding model, sum of squared residuals (SSR),
84-
absolute difference in SSR from the preceding model, R², absolute difference in R²
85-
from the preceding model, and F-statistic and p-value for the comparison
81+
A table is returned containing residual degrees of freedom (dof),
82+
absolute difference in dof from the preceding model, sum of squared residuals (SSR),
83+
absolute difference in SSR from the preceding model, and F-statistic and p-value for the comparison
8684
between the two models.
8785
8886
!!! note
@@ -114,22 +112,22 @@ julia> bigmodel = lm(@formula(Result ~ 1 + Treatment + Other), dat);
114112
115113
julia> ftest(nullmodel, model)
116114
F-test: 2 models fitted on 12 observations
117-
────────────────────────────────────────────────────────────────
118-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
119-
────────────────────────────────────────────────────────────────
120-
[1] 2 3.2292 0.0000
121-
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
122-
────────────────────────────────────────────────────────────────
115+
───────────────────────────────────────────
116+
SSR dof ΔSSR Δdof F* p(>F)
117+
───────────────────────────────────────────
118+
3.2292 11
119+
0.1283 10 3.1008 1 241.6234 <1e-07
120+
───────────────────────────────────────────
123121
124122
julia> ftest(nullmodel, model, bigmodel)
125123
F-test: 3 models fitted on 12 observations
126-
────────────────────────────────────────────────────────────────
127-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
128-
────────────────────────────────────────────────────────────────
129-
[1] 2 3.2292 0.0000
130-
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
131-
[3] 5 2 0.1017 0.0266 0.9685 0.0082 1.0456 0.3950
132-
────────────────────────────────────────────────────────────────
124+
───────────────────────────────────────────
125+
SSR dof ΔSSR Δdof F* p(>F)
126+
───────────────────────────────────────────
127+
3.2292 11
128+
0.1283 10 3.1008 1 241.6234 <1e-07
129+
0.1017 8 0.0266 2 1.0456 0.3950
130+
───────────────────────────────────────────
133131
```
134132
"""
135133
function ftest(mods::LinearModel...; atol::Real=0.0)
@@ -156,23 +154,22 @@ function ftest(mods::LinearModel...; atol::Real=0.0)
156154
157155
SSR = deviance.(mods)
158156
159-
df = dof.(mods)
157+
df = dof_residual.(mods)
160158
Δdf = abs.(_diff(df))
161-
dfr = Int.(dof_residual.(mods))
162159
MSR1 = _diffn(SSR) ./ Δdf
163-
MSR2 = (SSR ./ dfr)
160+
MSR2 = (SSR ./ df)
164161
if forward
165162
MSR2 = MSR2[2:end]
166-
dfr_big = dfr[2:end]
163+
dfr_big = df[2:end]
167164
else
168165
MSR2 = MSR2[1:(end - 1)]
169-
dfr_big = dfr[1:(end - 1)]
166+
dfr_big = df[1:(end - 1)]
170167
end
171168
172169
fstat = abs.((NaN, (MSR1 ./ MSR2)...))
173170
pval = (NaN, ccdf.(FDist.(Δdf, dfr_big), fstat[2:end])...)
174171
175-
return FTestResult(Int(nobs(mods[1])), SSR, df, r2.(mods), fstat, pval)
172+
return FTestResult(Int(nobs(mods[1])), SSR, float.(df), fstat, pval)
176173
end
177174
178175
function show(io::IO, ftr::SingleFTestResult)
@@ -182,37 +179,34 @@ function show(io::IO, ftr::SingleFTestResult)
182179
return print(io, "p-value: ", PValue(ftr.pval))
183180
end
184181
185-
function show(io::IO, ftr::FTestResult{N}) where {N}
182+
function show(io::IO, ::MIME"text/plain", ftr::FTestResult{N}) where {N}
186183
Δdof = abs.(_diff(ftr.dof))
187184
Δssr = abs.(_diff(ftr.ssr))
188-
ΔR² = abs.(_diff(ftr.r2))
189185
190-
nc = 9
186+
nc = 6
191187
nr = N
192188
outrows = Matrix{String}(undef, nr + 1, nc)
193189
194-
outrows[1, :] = ["", "DOF", "ΔDOF", "SSR", "ΔSSR",
195-
"R²", "ΔR²", "F*", "p(>F)"]
190+
outrows[1, :] = ["SSR", "dof", "ΔSSR", "Δdof", "F*", "p(>F)"]
196191
197-
# get rid of negative zero -- doesn't matter mathematically,
198-
# but messes up doctests and various other things
199-
# cf. Issue #461
200-
r2vals = [replace(@sprintf("%.4f", val), "-0.0000" => "0.0000") for val in ftr.r2]
201-
202-
outrows[2, :] = ["[1]", @sprintf("%.0d", ftr.dof[1]), " ",
203-
@sprintf("%.4f", ftr.ssr[1]), " ",
204-
r2vals[1], " ", " ", " "]
192+
outrows[2, :] = [@sprintf("%.4f", ftr.ssr[1]),
193+
@sprintf("%.0d", ftr.dof[1]),
194+
" ",
195+
" ",
196+
" ",
197+
" "]
205198
206199
for i in 2:nr
207-
outrows[i + 1, :] = ["[$i]",
208-
@sprintf("%.0d", ftr.dof[i]), @sprintf("%.0d", Δdof[i - 1]),
209-
@sprintf("%.4f", ftr.ssr[i]), @sprintf("%.4f", Δssr[i - 1]),
210-
r2vals[i], @sprintf("%.4f", ΔR²[i - 1]),
211-
@sprintf("%.4f", ftr.fstat[i]), string(PValue(ftr.pval[i]))]
200+
outrows[i + 1, :] = [@sprintf("%.4f", ftr.ssr[i]),
201+
@sprintf("%.0d", ftr.dof[i]),
202+
@sprintf("%.4f", Δssr[i - 1]),
203+
@sprintf("%.0d", Δdof[i - 1]),
204+
@sprintf("%.4f", ftr.fstat[i]),
205+
string(PValue(ftr.pval[i]))]
212206
end
213207
colwidths = length.(outrows)
214208
max_colwidths = [maximum(view(colwidths, :, i)) for i in 1:nc]
215-
totwidth = sum(max_colwidths) + 2 * 8
209+
totwidth = sum(max_colwidths) + 2 * (nc - 1)
216210
217211
println(io, "F-test: $N models fitted on $(ftr.nobs) observations")
218212
println(io, ''^totwidth)

test/runtests.jl

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,55 +1590,55 @@ end
15901590
ft1a = ftest(mod, nullmod)
15911591
@test isnan(ft1a.pval[1])
15921592
@test ft1a.pval[2] 2.481215056713184e-8
1593-
@test sprint(show, ft1a) == """
1593+
@test sprint(show, "text/plain", ft1a) == """
15941594
F-test: 2 models fitted on 12 observations
1595-
────────────────────────────────────────────────────────────────
1596-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1597-
────────────────────────────────────────────────────────────────
1598-
[1] 3 0.1283 0.9603
1599-
[2] 2 1 3.2292 3.1008 0.0000 0.9603 241.6234 <1e-07
1600-
────────────────────────────────────────────────────────────────"""
1595+
───────────────────────────────────────────
1596+
SSR dof ΔSSR Δdof F* p(>F)
1597+
───────────────────────────────────────────
1598+
0.1283 10
1599+
3.2292 11 3.1008 1 241.6234 <1e-07
1600+
───────────────────────────────────────────"""
16011601

16021602
ft1b = ftest(nullmod, mod)
16031603
@test isnan(ft1b.pval[1])
16041604
@test ft1b.pval[2] 2.481215056713184e-8
1605-
@test sprint(show, ft1b) == """
1605+
@test sprint(show, "text/plain", ft1b) == """
16061606
F-test: 2 models fitted on 12 observations
1607-
────────────────────────────────────────────────────────────────
1608-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1609-
────────────────────────────────────────────────────────────────
1610-
[1] 2 3.2292 0.0000
1611-
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
1612-
────────────────────────────────────────────────────────────────"""
1607+
───────────────────────────────────────────
1608+
SSR dof ΔSSR Δdof F* p(>F)
1609+
───────────────────────────────────────────
1610+
3.2292 11
1611+
0.1283 10 3.1008 1 241.6234 <1e-07
1612+
───────────────────────────────────────────"""
16131613

16141614
bigmod = lm(@formula(Result ~ Treatment + Other), d)
16151615
ft2a = ftest(nullmod, mod, bigmod)
16161616
@test isnan(ft2a.pval[1])
16171617
@test ft2a.pval[2] 2.481215056713184e-8
16181618
@test ft2a.pval[3] 0.3949973540194818
1619-
@test sprint(show, ft2a) == """
1619+
@test sprint(show, "text/plain", ft2a) == """
16201620
F-test: 3 models fitted on 12 observations
1621-
────────────────────────────────────────────────────────────────
1622-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1623-
────────────────────────────────────────────────────────────────
1624-
[1] 2 3.2292 0.0000
1625-
[2] 3 1 0.1283 3.1008 0.9603 0.9603 241.6234 <1e-07
1626-
[3] 5 2 0.1017 0.0266 0.9685 0.0082 1.0456 0.3950
1627-
────────────────────────────────────────────────────────────────"""
1621+
───────────────────────────────────────────
1622+
SSR dof ΔSSR Δdof F* p(>F)
1623+
───────────────────────────────────────────
1624+
3.2292 11
1625+
0.1283 10 3.1008 1 241.6234 <1e-07
1626+
0.1017 8 0.0266 2 1.0456 0.3950
1627+
───────────────────────────────────────────"""
16281628

16291629
ft2b = ftest(bigmod, mod, nullmod)
16301630
@test isnan(ft2b.pval[1])
16311631
@test ft2b.pval[2] 0.3949973540194818
16321632
@test ft2b.pval[3] 2.481215056713184e-8
1633-
@test sprint(show, ft2b) == """
1633+
@test sprint(show, "text/plain", ft2b) == """
16341634
F-test: 3 models fitted on 12 observations
1635-
────────────────────────────────────────────────────────────────
1636-
DOF ΔDOF SSR ΔSSR R² ΔR² F* p(>F)
1637-
────────────────────────────────────────────────────────────────
1638-
[1] 5 0.1017 0.9685
1639-
[2] 3 2 0.1283 0.0266 0.9603 0.0082 1.0456 0.3950
1640-
[3] 2 1 3.2292 3.1008 0.0000 0.9603 241.6234 <1e-07
1641-
────────────────────────────────────────────────────────────────"""
1635+
───────────────────────────────────────────
1636+
SSR dof ΔSSR Δdof F* p(>F)
1637+
───────────────────────────────────────────
1638+
0.1017 8
1639+
0.1283 10 0.0266 2 1.0456 0.3950
1640+
3.2292 11 3.1008 1 241.6234 <1e-07
1641+
───────────────────────────────────────────"""
16421642

16431643
@test_throws ArgumentError ftest(mod, bigmod, nullmod)
16441644
@test_throws ArgumentError ftest(nullmod, bigmod, mod)

0 commit comments

Comments
 (0)