Skip to content

Commit 58a8f70

Browse files
Update to StatsModels 0.7 (#51)
* Update Project.toml * Update formula.jl * up * Update Project.toml * update * Update ci.yml * Update .gitignore * up
1 parent 80b4926 commit 58a8f70

File tree

8 files changed

+95
-110
lines changed

8 files changed

+95
-110
lines changed

.DS_Store

-6 KB
Binary file not shown.

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
version:
18-
- '1.3' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
18+
- '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
1919
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
2020
os:
2121
- ubuntu-latest

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ src/other/*
1111
*.synctex.gz
1212
examples/runbenchmark.jl
1313
.DS_Store
14+
.DS_Store
15+
.DS_Store

Project.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "InteractiveFixedEffectModels"
22
uuid = "80307280-efb2-5c5d-af8b-a9c15821677b"
3-
version = "1.1.8"
3+
version = "1.2.0"
44

55
[deps]
66
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
@@ -13,6 +13,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1313
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1414
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1515
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
16+
StatsAPI = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
1617
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
1718
StatsFuns = "4c63d2b9-4356-54db-8cca-17b64c39e42c"
1819
StatsModels = "3eaba693-59b7-5ba5-a881-562e759f1c8d"
@@ -27,12 +28,13 @@ FixedEffects = "2"
2728
GroupedArrays = "0.3"
2829
LeastSquaresOptim = "0.7, 0.8"
2930
Reexport = "0.2, 1"
31+
StatsAPI = "1"
3032
StatsBase = "0.33"
31-
StatsModels = "0.6"
33+
StatsModels = "0.7"
3234
StatsFuns = "0.9, 1"
3335
Tables = "1"
3436
Vcov = "0.5, 0.6, 0.7"
35-
julia = "1.3"
37+
julia = "1.6"
3638

3739
[extras]
3840
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"

src/InteractiveFixedEffectModels.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ using LeastSquaresOptim
1212
using LinearAlgebra
1313
using Printf
1414
using Statistics
15+
using StatsAPI
1516
using StatsBase
1617
using StatsFuns
1718
using StatsModels

src/fit.jl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
function regife(
44
@nospecialize(df),
5-
@nospecialize(f::FormulaTerm),
5+
@nospecialize(formula::FormulaTerm),
66
@nospecialize(vcov::CovarianceEstimator = Vcov.simple());
77
@nospecialize(weights::Union{Symbol, Nothing} = nothing),
88
@nospecialize(subset::Union{AbstractVector, Nothing} = nothing),
@@ -18,13 +18,15 @@ function regife(
1818
## Transform DataFrame -> Matrix
1919
##
2020
##############################################################################
21+
formula_origin = formula
22+
2123
df = DataFrame(df; copycols = false)
22-
if (ConstantTerm(0) eachterm(f.rhs)) & (ConstantTerm(1) eachterm(f.rhs))
23-
formula = FormulaTerm(f.lhs, tuple(ConstantTerm(1), eachterm(f.rhs)...))
24+
if (ConstantTerm(0) eachterm(formula.rhs)) & (ConstantTerm(1) eachterm(formula.rhs))
25+
formula = FormulaTerm(formula.lhs, tuple(ConstantTerm(1), eachterm(formula.rhs)...))
2426
end
2527

2628

27-
m, formula = parse_interactivefixedeffect(df, f)
29+
m, formula = parse_interactivefixedeffect(df, formula)
2830
has_weights = (weights != nothing)
2931

3032

@@ -112,11 +114,11 @@ function regife(
112114

113115

114116
# Compute demeaned X
115-
yname, coef_names = coefnames(formula_schema)
117+
responsename, coef_names = coefnames(formula_schema)
116118
if !isa(coef_names, Vector)
117119
coef_names = [coef_names]
118120
end
119-
yname = Symbol(yname)
121+
responsename = Symbol(responsename)
120122
coef_names = Symbol.(coef_names)
121123

122124

@@ -278,7 +280,6 @@ function regife(
278280
return FactorResult(esample, augmentdf, rss, iterations, converged)
279281
else
280282
return InteractiveFixedEffectModel(fs.b, matrix_vcov, vcov, esample, augmentdf,
281-
coef_names, yname, f, nobs, dof_residual, r2, r2_a, r2_within,
282-
rss, sum(iterations), all(converged))
283+
coef_names, responsename, formula_origin, formula, nobs, dof_residual, rss, tss_total, r2, r2_a, r2_within, sum(iterations), all(converged))
283284
end
284285
end

src/types.jl

Lines changed: 76 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function parse_interactivefixedeffect(df::AbstractDataFrame, formula::FormulaTer
2222
m = nothing
2323
for term in FixedEffectModels.eachterm(formula.rhs)
2424
if term isa FunctionTerm{typeof(ife)}
25-
m = InteractiveFixedEffectTerm(term.args_parsed[1].sym, term.args_parsed[2].sym, term.args_parsed[3].n)
25+
m = InteractiveFixedEffectTerm(term.args[1].sym, term.args[2].sym, term.args[3].n)
2626
elseif term isa InteractiveFixedEffectTerm
2727
m = term
2828
end
@@ -255,30 +255,38 @@ struct InteractiveFixedEffectModel <: RegressionModel
255255
augmentdf::DataFrame
256256

257257
coefnames::Vector # Name of coefficients
258-
yname::Symbol # Name of dependent variable
259-
formula::FormulaTerm # Original formula
258+
responsename::Symbol # Name of dependent variable
259+
formula::FormulaTerm # Original formula
260+
formula_schema::FormulaTerm # Schema for predict
260261

261262
nobs::Int64 # Number of observations
262263
dof_residual::Int64 # degree of freedoms
263264

265+
rss::Float64
266+
tss::Float64
264267
r2::Float64 # R squared
265268
adjr2::Float64 # R squared adjusted
266269
r2_within::Float64 # R within
267270

268-
rss::Float64
269271
iterations::Int # Number of iterations
270272
converged::Bool # Has the demeaning algorithm converged?
271273

272274
end
273-
StatsBase.coef(x::InteractiveFixedEffectModel) = x.coef
274-
StatsBase.coefnames(x::InteractiveFixedEffectModel) = x.coefnames
275-
StatsBase.vcov(x::InteractiveFixedEffectModel) = x.vcov
276-
StatsBase.nobs(x::InteractiveFixedEffectModel) = x.nobs
277-
StatsBase.dof_residual(x::InteractiveFixedEffectModel) = x.dof_residual
278-
StatsBase.r2(x::InteractiveFixedEffectModel) = x.r2
279-
StatsBase.adjr2(x::InteractiveFixedEffectModel) = x.adjr2
280-
StatsBase.islinear(x::InteractiveFixedEffectModel) = false
281-
StatsBase.rss(x::InteractiveFixedEffectModel) = x.rss
275+
StatsAPI.coef(x::InteractiveFixedEffectModel) = x.coef
276+
StatsAPI.coefnames(x::InteractiveFixedEffectModel) = x.coefnames
277+
StatsAPI.responsename(m::InteractiveFixedEffectModel) = m.responsename
278+
StatsAPI.vcov(x::InteractiveFixedEffectModel) = x.vcov
279+
StatsAPI.nobs(x::InteractiveFixedEffectModel) = x.nobs
280+
StatsAPI.dof_residual(x::InteractiveFixedEffectModel) = x.dof_residual
281+
StatsAPI.r2(x::InteractiveFixedEffectModel) = x.r2
282+
StatsAPI.adjr2(x::InteractiveFixedEffectModel) = x.adjr2
283+
StatsAPI.islinear(x::InteractiveFixedEffectModel) = false
284+
StatsAPI.deviance(x::InteractiveFixedEffectModel) = x.tss
285+
StatsAPI.rss(x::InteractiveFixedEffectModel) = x.rss
286+
StatsAPI.mss(m::InteractiveFixedEffectModel) = deviance(m) - rss(m)
287+
StatsModels.formula(m::InteractiveFixedEffectModel) = m.formula_schema
288+
289+
282290
StatsBase.predict(::InteractiveFixedEffectModel, ::AbstractDataFrame) = error("predict is not defined for linear factor models. Use the option save = true")
283291
StatsBase.residuals(::InteractiveFixedEffectModel, ::AbstractDataFrame) = error("residuals is not defined for linear factor models. Use the option save = true")
284292
function StatsBase.confint(x::InteractiveFixedEffectModel; level::Real = 0.95)
@@ -316,97 +324,69 @@ title(::InteractiveFixedEffectModel) = "Interactive Fixed Effect Model"
316324
top(x::InteractiveFixedEffectModel) = [
317325
"Number of obs" sprint(show, nobs(x); context=:compact => true);
318326
"Degree of freedom" sprint(show, nobs(x) - dof_residual(x); context=:compact => true);
319-
"R2" @sprintf("%.3f", x.r2);
320-
"R2 within" @sprintf("%.3f", x.r2_within);
327+
"" @sprintf("%.3f", x.r2);
328+
" within" @sprintf("%.3f", x.r2_within);
321329
"Iterations" sprint(show, x.iterations; context=:compact => true);
322330
"Converged" sprint(show, x.converged; context=:compact => true)
323331
]
324332

325-
format_scientific(x) = @sprintf("%.3f", x)
326-
327-
function Base.show(io::IO, x::InteractiveFixedEffectModel)
328-
ctitle = title(x)
329-
ctop = top(x)
330-
cc = coef(x)
331-
se = stderror(x)
332-
coefnms = coefnames(x)
333-
conf_int = confint(x)
334-
# put (intercept) last
335-
if !isempty(coefnms) && ((coefnms[1] == Symbol("(Intercept)")) || (coefnms[1] == "(Intercept)"))
336-
newindex = vcat(2:length(cc), 1)
337-
cc = cc[newindex]
338-
se = se[newindex]
339-
conf_int = conf_int[newindex, :]
340-
coefnms = coefnms[newindex]
341-
end
342-
tt = cc ./ se
343-
mat = hcat(cc, se, tt, fdistccdf.(Ref(1), Ref(dof_residual(x)), abs2.(tt)), conf_int[:, 1:2])
344-
nr, nc = size(mat)
345-
colnms = ["Estimate","Std.Error","t value", "Pr(>|t|)", "Lower 95%", "Upper 95%"]
346-
rownms = ["$(coefnms[i])" for i = 1:length(cc)]
347-
pvc = 4
348-
349-
350-
# print
333+
import StatsBase: NoQuote, PValue
334+
function Base.show(io::IO, m::InteractiveFixedEffectModel)
335+
ct = coeftable(m)
336+
#copied from show(iio,cf::Coeftable)
337+
cols = ct.cols; rownms = ct.rownms; colnms = ct.colnms;
338+
nc = length(cols)
339+
nr = length(cols[1])
351340
if length(rownms) == 0
352-
rownms = AbstractString[lpad("[$i]",floor(Integer, log10(nr))+3) for i in 1:nr]
353-
end
354-
if length(rownms) > 0
355-
rnwidth = max(4,maximum([length(nm) for nm in rownms]) + 1)
356-
else
357-
# if only intercept, rownms is empty collection, so previous would return error
358-
rnwidth = 4
341+
rownms = [lpad("[$i]",floor(Integer, log10(nr))+3) for i in 1:nr]
359342
end
360-
rownms = [rpad(nm,rnwidth) for nm in rownms]
361-
widths = [length(cn)::Int for cn in colnms]
362-
str = [sprint(show, mat[i,j]; context=:compact => true) for i in 1:nr, j in 1:nc]
363-
if pvc != 0 # format the p-values column
364-
for i in 1:nr
365-
str[i, pvc] = format_scientific(mat[i, pvc])
366-
end
343+
mat = [j == 1 ? NoQuote(rownms[i]) :
344+
j-1 == ct.pvalcol ? NoQuote(sprint(show, PValue(cols[j-1][i]))) :
345+
j-1 in ct.teststatcol ? TestStat(cols[j-1][i]) :
346+
cols[j-1][i] isa AbstractString ? NoQuote(cols[j-1][i]) : cols[j-1][i]
347+
for i in 1:nr, j in 1:nc+1]
348+
io = IOContext(io, :compact=>true, :limit=>false)
349+
A = Base.alignment(io, mat, 1:size(mat, 1), 1:size(mat, 2),
350+
typemax(Int), typemax(Int), 3)
351+
nmswidths = pushfirst!(length.(colnms), 0)
352+
A = [nmswidths[i] > sum(A[i]) ? (A[i][1]+nmswidths[i]-sum(A[i]), A[i][2]) : A[i]
353+
for i in 1:length(A)]
354+
totwidth = sum(sum.(A)) + 2 * (length(A) - 1)
355+
356+
357+
#intert my stuff which requires totwidth
358+
ctitle = string(typeof(m))
359+
halfwidth = div(totwidth - length(ctitle), 2)
360+
print(io, " " ^ halfwidth * ctitle * " " ^ halfwidth)
361+
ctop = top(m)
362+
for i in 1:size(ctop, 1)
363+
ctop[i, 1] = ctop[i, 1] * ":"
367364
end
368-
for j in 1:nc
369-
for i in 1:nr
370-
lij = length(str[i, j])
371-
if lij > widths[j]
372-
widths[j] = lij
373-
end
365+
println(io, '\n', repeat('=', totwidth))
366+
halfwidth = div(totwidth, 2) - 1
367+
interwidth = 2 + mod(totwidth, 2)
368+
for i in 1:(div(size(ctop, 1) - 1, 2)+1)
369+
print(io, ctop[2*i-1, 1])
370+
print(io, lpad(ctop[2*i-1, 2], halfwidth - length(ctop[2*i-1, 1])))
371+
print(io, " " ^interwidth)
372+
if size(ctop, 1) >= 2*i
373+
print(io, ctop[2*i, 1])
374+
print(io, lpad(ctop[2*i, 2], halfwidth - length(ctop[2*i, 1])))
374375
end
376+
println(io)
375377
end
376-
widths .+= 1
377-
totalwidth = sum(widths) + rnwidth
378-
if length(ctitle) > 0
379-
halfwidth = div(totalwidth - length(ctitle), 2)
380-
println(io, " " ^ halfwidth * string(ctitle) * " " ^ halfwidth)
378+
379+
# rest of coeftable code
380+
println(io, repeat('=', totwidth))
381+
print(io, repeat(' ', sum(A[1])))
382+
for j in 1:length(colnms)
383+
print(io, " ", lpad(colnms[j], sum(A[j+1])))
381384
end
382-
if length(ctop) > 0
383-
for i in 1:size(ctop, 1)
384-
ctop[i, 1] = ctop[i, 1] * ":"
385-
end
386-
println(io, "=" ^totalwidth)
387-
halfwidth = div(totalwidth, 2) - 1
388-
interwidth = 2 + mod(totalwidth, 2)
389-
for i in 1:(div(size(ctop, 1) - 1, 2)+1)
390-
print(io, ctop[2*i-1, 1])
391-
print(io, lpad(ctop[2*i-1, 2], halfwidth - length(ctop[2*i-1, 1])))
392-
print(io, " " ^interwidth)
393-
if size(ctop, 1) >= 2*i
394-
print(io, ctop[2*i, 1])
395-
print(io, lpad(ctop[2*i, 2], halfwidth - length(ctop[2*i, 1])))
396-
end
397-
println(io)
398-
end
385+
println(io, '\n', repeat('', totwidth))
386+
for i in 1:size(mat, 1)
387+
Base.print_matrix_row(io, mat, A, i, 1:size(mat, 2), " ")
388+
i != size(mat, 1) && println(io)
399389
end
400-
println(io,"=" ^totalwidth)
401-
println(io," " ^ rnwidth *
402-
join([lpad(string(colnms[i]), widths[i]) for i = 1:nc], ""))
403-
println(io,"-" ^totalwidth)
404-
for i in 1:nr
405-
print(io, rownms[i])
406-
for j in 1:nc
407-
print(io, lpad(str[i,j],widths[j]))
408-
end
409-
println(io)
410-
end
411-
println(io,"=" ^totalwidth)
412-
end
390+
println(io, '\n', repeat('=', totwidth))
391+
nothing
392+
end

src/utils/formula.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66

77
eachterm(@nospecialize(x::AbstractTerm)) = (x,)
88
eachterm(@nospecialize(x::NTuple{N, AbstractTerm})) where {N} = x
9-
TermOrTerms = Union{AbstractTerm, NTuple{N, AbstractTerm} where N}
109

1110
##############################################################################
1211
##
1312
## Parse FixedEffect
1413
##
1514
##############################################################################
1615
fesymbol(t::FixedEffectModels.FixedEffectTerm) = t.x
17-
fesymbol(t::FunctionTerm{typeof(fe)}) = Symbol(t.args_parsed[1])
16+
fesymbol(t::FunctionTerm{typeof(fe)}) = Symbol(t.args[1])

0 commit comments

Comments
 (0)