Skip to content

Commit 399845f

Browse files
Merge pull request #207 from alyst/vars_api
Cleanup Variables API
2 parents 55f9087 + 2f6e8b7 commit 399845f

38 files changed

Lines changed: 1003 additions & 714 deletions

src/StructuralEquationModels.jl

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ include("additional_functions/commutation_matrix.jl")
3030
# fitted objects
3131
include("frontend/fit/SemFit.jl")
3232
# specification of models
33+
include("frontend/common.jl")
3334
include("frontend/specification/checks.jl")
3435
include("frontend/specification/ParameterTable.jl")
3536
include("frontend/specification/RAMMatrices.jl")
@@ -39,7 +40,7 @@ include("frontend/fit/summary.jl")
3940
# pretty printing
4041
include("frontend/pretty_printing.jl")
4142
# observed
42-
include("observed/get_colnames.jl")
43+
include("observed/abstract.jl")
4344
include("observed/covariance.jl")
4445
include("observed/data.jl")
4546
include("observed/missing.jl")
@@ -48,6 +49,7 @@ include("observed/EM.jl")
4849
include("frontend/specification/Sem.jl")
4950
include("frontend/specification/documentation.jl")
5051
# imply
52+
include("imply/abstract.jl")
5153
include("imply/RAM/symbolic.jl")
5254
include("imply/RAM/generic.jl")
5355
include("imply/empty.jl")
@@ -80,10 +82,8 @@ include("frontend/fit/fitmeasures/BIC.jl")
8082
include("frontend/fit/fitmeasures/chi2.jl")
8183
include("frontend/fit/fitmeasures/df.jl")
8284
include("frontend/fit/fitmeasures/minus2ll.jl")
83-
include("frontend/fit/fitmeasures/n_obs.jl")
8485
include("frontend/fit/fitmeasures/p.jl")
8586
include("frontend/fit/fitmeasures/RMSEA.jl")
86-
include("frontend/fit/fitmeasures/n_man.jl")
8787
include("frontend/fit/fitmeasures/fit_measures.jl")
8888
# standard errors
8989
include("frontend/fit/standard_errors/hessian.jl")
@@ -126,6 +126,10 @@ export AbstractSem,
126126
SemObservedCovariance,
127127
SemObservedMissing,
128128
observed,
129+
obs_cov,
130+
obs_mean,
131+
nsamples,
132+
samples,
129133
sem_fit,
130134
SemFit,
131135
minimum,
@@ -138,6 +142,8 @@ export AbstractSem,
138142
objective_hessian!,
139143
gradient_hessian!,
140144
objective_gradient_hessian!,
145+
SemSpecification,
146+
RAMMatrices,
141147
ParameterTable,
142148
EnsembleParameterTable,
143149
update_partable!,
@@ -150,9 +156,14 @@ export AbstractSem,
150156
start,
151157
Label,
152158
label,
159+
nvars,
160+
vars,
161+
nlatent_vars,
162+
latent_vars,
163+
nobserved_vars,
164+
observed_vars,
153165
sort_vars!,
154166
sort_vars,
155-
RAMMatrices,
156167
params,
157168
nparams,
158169
param_indices,
@@ -163,10 +174,8 @@ export AbstractSem,
163174
df,
164175
fit_measures,
165176
minus2ll,
166-
n_obs,
167177
p_value,
168178
RMSEA,
169-
n_man,
170179
EmMVNModel,
171180
se_hessian,
172181
se_bootstrap,

src/additional_functions/start_val/start_fabin3.jl

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,26 +31,26 @@ function start_fabin3(observed::SemObservedMissing, imply, optimizer, args...; k
3131
end
3232

3333
function start_fabin3(ram_matrices::RAMMatrices, Σ, μ)
34-
A_ind, S_ind, F_ind, M_ind, params = ram_matrices.A_ind,
34+
A_ind, S_ind, F_ind, M_ind, n_par = ram_matrices.A_ind,
3535
ram_matrices.S_ind,
3636
ram_matrices.F_ind,
3737
ram_matrices.M_ind,
38-
ram_matrices.params
38+
nparams(ram_matrices)
3939

40-
n_par = length(params)
4140
start_val = zeros(n_par)
42-
n_var, n_nod = ram_matrices.size_F
43-
n_latent = n_nod - n_var
41+
n_obs = nobserved_vars(ram_matrices)
42+
n_var = nvars(ram_matrices)
43+
n_latent = nlatent_vars(ram_matrices)
4444

45-
C_indices = CartesianIndices((n_nod, n_nod))
45+
C_indices = CartesianIndices((n_var, n_var))
4646

4747
# check in which matrix each parameter appears
4848

4949
indices = Vector{CartesianIndex{2}}(undef, n_par)
5050

5151
#= in_S = length.(S_ind) .!= 0
5252
in_A = length.(A_ind) .!= 0
53-
A_ind_c = [linear2cartesian(ind, (n_nod, n_nod)) for ind in A_ind]
53+
A_ind_c = [linear2cartesian(ind, (n_var, n_var)) for ind in A_ind]
5454
in_Λ = [any(ind[2] .∈ F_ind) for ind in A_ind_c]
5555
5656
if !isnothing(M)
@@ -77,7 +77,7 @@ function start_fabin3(ram_matrices::RAMMatrices, Σ, μ)
7777

7878
# set loadings
7979
constants = ram_matrices.constants
80-
A_ind_c = [linear2cartesian(ind, (n_nod, n_nod)) for ind in A_ind]
80+
A_ind_c = [linear2cartesian(ind, (n_var, n_var)) for ind in A_ind]
8181
# ind_Λ = findall([is_in_Λ(ind_vec, F_ind) for ind_vec in A_ind_c])
8282

8383
function calculate_lambda(
@@ -99,7 +99,7 @@ function start_fabin3(ram_matrices::RAMMatrices, Σ, μ)
9999
end
100100
end
101101

102-
for i in setdiff(1:n_nod, F_ind)
102+
for i in setdiff(1:n_var, F_ind)
103103
reference = Int64[]
104104
indicators = Int64[]
105105
indicator2parampos = Dict{Int, Int}()

src/additional_functions/start_val/start_simple.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,17 @@ function start_simple(
6262
start_means = 0.0,
6363
kwargs...,
6464
)
65-
A_ind, S_ind, F_ind, M_ind, params = ram_matrices.A_ind,
65+
A_ind, S_ind, F_ind, M_ind, n_par = ram_matrices.A_ind,
6666
ram_matrices.S_ind,
6767
ram_matrices.F_ind,
6868
ram_matrices.M_ind,
69-
ram_matrices.params
69+
nparams(ram_matrices)
7070

71-
n_par = length(params)
7271
start_val = zeros(n_par)
73-
n_var, n_nod = ram_matrices.size_F
72+
n_obs = nobserved_vars(ram_matrices)
73+
n_var = nvars(ram_matrices)
7474

75-
C_indices = CartesianIndices((n_nod, n_nod))
75+
C_indices = CartesianIndices((n_var, n_var))
7676

7777
for i in 1:n_par
7878
if length(S_ind[i]) != 0

src/frontend/common.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# API methods supported by multiple SEM.jl types
2+
3+
"""
4+
nparams(semobj)
5+
6+
Return the number of parameters in a SEM model associated with `semobj`.
7+
8+
See also [`params`](@ref).
9+
"""
10+
nparams(semobj) = length(params(semobj))
11+
12+
"""
13+
nvars(semobj)
14+
15+
Return the number of variables in a SEM model associated with `semobj`.
16+
17+
See also [`vars`](@ref).
18+
"""
19+
nvars(semobj) = length(vars(semobj))
20+
21+
"""
22+
nobserved_vars(semobj)
23+
24+
Return the number of observed variables in a SEM model associated with `semobj`.
25+
"""
26+
nobserved_vars(semobj) = length(observed_vars(semobj))
27+
28+
"""
29+
nlatent_vars(semobj)
30+
31+
Return the number of latent variables in a SEM model associated with `semobj`.
32+
"""
33+
nlatent_vars(semobj) = length(latent_vars(semobj))
34+
35+
"""
36+
param_indices(semobj)
37+
38+
Returns a dict of parameter names and their indices in `semobj`.
39+
40+
# Examples
41+
```julia
42+
parind = param_indices(my_fitted_sem)
43+
parind[:param_name]
44+
```
45+
46+
See also [`params`](@ref).
47+
"""
48+
param_indices(semobj) = Dict(par => i for (i, par) in enumerate(params(semobj)))
49+
50+
"""
51+
nsamples(semobj)
52+
53+
Return the number of samples (observed data points).
54+
55+
For ensemble models, return the sum over all submodels.
56+
"""
57+
function nsamples end

src/frontend/fit/SemFit.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ end
4848

4949
params(fit::SemFit) = params(fit.model)
5050
nparams(fit::SemFit) = nparams(fit.model)
51+
nsamples(fit::SemFit) = nsamples(fit.model)
5152

5253
# access fields
5354
minimum(sem_fit::SemFit) = sem_fit.minimum

src/frontend/fit/fitmeasures/BIC.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
44
Return the bayesian information criterion.
55
"""
6-
BIC(sem_fit::SemFit) = minus2ll(sem_fit) + log(n_obs(sem_fit)) * nparams(sem_fit)
6+
BIC(sem_fit::SemFit) = minus2ll(sem_fit) + log(nsamples(sem_fit)) * nparams(sem_fit)

src/frontend/fit/fitmeasures/RMSEA.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ Return the RMSEA.
66
function RMSEA end
77

88
RMSEA(sem_fit::SemFit{Mi, So, St, Mo, O} where {Mi, So, St, Mo <: AbstractSemSingle, O}) =
9-
RMSEA(df(sem_fit), χ²(sem_fit), n_obs(sem_fit))
9+
RMSEA(df(sem_fit), χ²(sem_fit), nsamples(sem_fit))
1010

1111
RMSEA(sem_fit::SemFit{Mi, So, St, Mo, O} where {Mi, So, St, Mo <: SemEnsemble, O}) =
12-
sqrt(length(sem_fit.model.sems)) * RMSEA(df(sem_fit), χ²(sem_fit), n_obs(sem_fit))
12+
sqrt(length(sem_fit.model.sems)) * RMSEA(df(sem_fit), χ²(sem_fit), nsamples(sem_fit))
1313

14-
function RMSEA(df, chi2, n_obs)
15-
rmsea = (chi2 - df) / (n_obs * df)
14+
function RMSEA(df, chi2, nsamples)
15+
rmsea = (chi2 - df) / (nsamples * df)
1616
rmsea > 0 ? nothing : rmsea = 0
1717
return sqrt(rmsea)
1818
end

src/frontend/fit/fitmeasures/chi2.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ function χ² end
2020

2121
# RAM + SemML
2222
χ²(sem_fit::SemFit, observed, imp::Union{RAM, RAMSymbolic}, optimizer, loss_ml::SemML) =
23-
(n_obs(sem_fit) - 1) * (sem_fit.minimum - logdet(observed.obs_cov) - observed.n_man)
23+
(nsamples(sem_fit) - 1) *
24+
(sem_fit.minimum - logdet(observed.obs_cov) - nobserved_vars(observed))
2425

2526
# bollen, p. 115, only correct for GLS weight matrix
2627
χ²(sem_fit::SemFit, observed, imp::Union{RAM, RAMSymbolic}, optimizer, loss_ml::SemWLS) =
27-
(n_obs(sem_fit) - 1) * sem_fit.minimum
28+
(nsamples(sem_fit) - 1) * sem_fit.minimum
2829

2930
# FIML
3031
function χ²(sem_fit::SemFit, observed::SemObservedMissing, imp, optimizer, loss_ml::SemFIML)
@@ -45,18 +46,18 @@ end
4546
function χ²(sem_fit::SemFit, model::SemEnsemble, lossfun::L) where {L <: SemWLS}
4647
check_ensemble_length(model)
4748
check_lossfun_types(model, L)
48-
return (sum(n_obs.(model.sems)) - 1) * sem_fit.minimum
49+
return (nsamples(model) - 1) * sem_fit.minimum
4950
end
5051

5152
function χ²(sem_fit::SemFit, model::SemEnsemble, lossfun::L) where {L <: SemML}
5253
check_ensemble_length(model)
5354
check_lossfun_types(model, L)
5455
F_G = sem_fit.minimum
5556
F_G -= sum([
56-
w * (logdet(m.observed.obs_cov) + m.observed.n_man) for
57+
w * (logdet(m.observed.obs_cov) + nobserved_vars(m.observed)) for
5758
(w, m) in zip(model.weights, model.sems)
5859
])
59-
return (sum(n_obs.(model.sems)) - 1) * F_G
60+
return (nsamples(model) - 1) * F_G
6061
end
6162

6263
function χ²(sem_fit::SemFit, model::SemEnsemble, lossfun::L) where {L <: SemFIML}

src/frontend/fit/fitmeasures/df.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ df(sem_fit::SemFit) = df(sem_fit.model)
1111
df(model::AbstractSem) = n_dp(model) - nparams(model)
1212

1313
function n_dp(model::AbstractSemSingle)
14-
nman = n_man(model)
15-
ndp = 0.5(nman^2 + nman)
14+
nvars = nobserved_vars(model)
15+
ndp = 0.5(nvars^2 + nvars)
1616
if !isnothing(model.imply.μ)
17-
ndp += n_man(model)
17+
ndp += nvars
1818
end
1919
return ndp
2020
end

src/frontend/fit/fitmeasures/minus2ll.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ minus2ll(sem_fit::SemFit, obs, imp, optimizer, args...) =
2525

2626
# SemML ------------------------------------------------------------------------------------
2727
minus2ll(minimum::Number, obs, imp::Union{RAM, RAMSymbolic}, optimizer, loss_ml::SemML) =
28-
n_obs(obs) * (minimum + log(2π) * n_man(obs))
28+
nsamples(obs) * (minimum + log(2π) * nobserved_vars(obs))
2929

3030
# WLS --------------------------------------------------------------------------------------
3131
minus2ll(minimum::Number, obs, imp::Union{RAM, RAMSymbolic}, optimizer, loss_ml::SemWLS) =
@@ -41,8 +41,8 @@ function minus2ll(
4141
loss_ml::SemFIML,
4242
)
4343
F = minimum
44-
F *= n_obs(observed)
45-
F += sum(log(2π) * observed.pattern_n_obs .* observed.pattern_nvar_obs)
44+
F *= nsamples(observed)
45+
F += sum(log(2π) * observed.pattern_nsamples .* observed.pattern_nobs_vars)
4646
return F
4747
end
4848

@@ -53,26 +53,26 @@ function minus2ll(observed::SemObservedMissing)
5353
minus2ll(
5454
observed.em_model.μ,
5555
observed.em_model.Σ,
56-
observed.n_obs,
57-
observed.rows,
56+
nsamples(observed),
57+
pattern_rows(observed),
5858
observed.patterns,
5959
observed.obs_mean,
6060
observed.obs_cov,
61-
observed.pattern_n_obs,
62-
observed.pattern_nvar_obs,
61+
observed.pattern_nsamples,
62+
observed.pattern_nobs_vars,
6363
)
6464
else
6565
em_mvn(observed)
6666
minus2ll(
6767
observed.em_model.μ,
6868
observed.em_model.Σ,
69-
observed.n_obs,
70-
observed.rows,
69+
nsamples(observed),
70+
pattern_rows(observed),
7171
observed.patterns,
7272
observed.obs_mean,
7373
observed.obs_cov,
74-
observed.pattern_n_obs,
75-
observed.pattern_nvar_obs,
74+
observed.pattern_nsamples,
75+
observed.pattern_nobs_vars,
7676
)
7777
end
7878
end
@@ -85,13 +85,13 @@ function minus2ll(
8585
patterns,
8686
obs_mean,
8787
obs_cov,
88-
pattern_n_obs,
89-
pattern_nvar_obs,
88+
pattern_nsamples,
89+
pattern_nobs_vars,
9090
)
9191
F = 0.0
9292

9393
for i in 1:length(rows)
94-
nᵢ = pattern_n_obs[i]
94+
nᵢ = pattern_nsamples[i]
9595
# missing pattern
9696
pattern = patterns[i]
9797
# observed data
@@ -106,7 +106,7 @@ function minus2ll(
106106
F += F_one_pattern(meandiffᵢ, Σᵢ⁻¹, Sᵢ, ld, nᵢ)
107107
end
108108

109-
F += sum(log(2π) * pattern_n_obs .* pattern_nvar_obs)
109+
F += sum(log(2π) * pattern_nsamples .* pattern_nobs_vars)
110110
#F *= N
111111

112112
return F

0 commit comments

Comments
 (0)