Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup Variables API #207

Open
wants to merge 26 commits into
base: devel
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions src/StructuralEquationModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ include("additional_functions/commutation_matrix.jl")
# fitted objects
include("frontend/fit/SemFit.jl")
# specification of models
include("frontend/common.jl")
include("frontend/specification/checks.jl")
include("frontend/specification/ParameterTable.jl")
include("frontend/specification/RAMMatrices.jl")
Expand All @@ -39,7 +40,7 @@ include("frontend/fit/summary.jl")
# pretty printing
include("frontend/pretty_printing.jl")
# observed
include("observed/get_colnames.jl")
include("observed/abstract.jl")
include("observed/covariance.jl")
include("observed/data.jl")
include("observed/missing.jl")
Expand All @@ -48,6 +49,7 @@ include("observed/EM.jl")
include("frontend/specification/Sem.jl")
include("frontend/specification/documentation.jl")
# imply
include("imply/abstract.jl")
include("imply/RAM/symbolic.jl")
include("imply/RAM/generic.jl")
include("imply/empty.jl")
Expand Down Expand Up @@ -80,10 +82,8 @@ include("frontend/fit/fitmeasures/BIC.jl")
include("frontend/fit/fitmeasures/chi2.jl")
include("frontend/fit/fitmeasures/df.jl")
include("frontend/fit/fitmeasures/minus2ll.jl")
include("frontend/fit/fitmeasures/n_obs.jl")
include("frontend/fit/fitmeasures/p.jl")
include("frontend/fit/fitmeasures/RMSEA.jl")
include("frontend/fit/fitmeasures/n_man.jl")
include("frontend/fit/fitmeasures/fit_measures.jl")
# standard errors
include("frontend/fit/standard_errors/hessian.jl")
Expand Down Expand Up @@ -163,10 +163,9 @@ export AbstractSem,
df,
fit_measures,
minus2ll,
n_obs,
nsamples,
p_value,
RMSEA,
n_man,
EmMVNModel,
se_hessian,
se_bootstrap,
Expand Down
18 changes: 9 additions & 9 deletions src/additional_functions/start_val/start_fabin3.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,26 @@ function start_fabin3(observed::SemObservedMissing, imply, optimizer, args...; k
end

function start_fabin3(ram_matrices::RAMMatrices, Σ, μ)
A_ind, S_ind, F_ind, M_ind, params = ram_matrices.A_ind,
A_ind, S_ind, F_ind, M_ind, n_par = ram_matrices.A_ind,
ram_matrices.S_ind,
ram_matrices.F_ind,
ram_matrices.M_ind,
ram_matrices.params
nparams(ram_matrices)

n_par = length(params)
start_val = zeros(n_par)
n_var, n_nod = ram_matrices.size_F
n_latent = n_nod - n_var
n_obs = nobserved_vars(ram_matrices)
n_var = nvars(ram_matrices)
n_latent = nlatent_vars(ram_matrices)

C_indices = CartesianIndices((n_nod, n_nod))
C_indices = CartesianIndices((n_var, n_var))

# check in which matrix each parameter appears

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

#= in_S = length.(S_ind) .!= 0
in_A = length.(A_ind) .!= 0
A_ind_c = [linear2cartesian(ind, (n_nod, n_nod)) for ind in A_ind]
A_ind_c = [linear2cartesian(ind, (n_var, n_var)) for ind in A_ind]
in_Λ = [any(ind[2] .∈ F_ind) for ind in A_ind_c]

if !isnothing(M)
Expand All @@ -77,7 +77,7 @@ function start_fabin3(ram_matrices::RAMMatrices, Σ, μ)

# set loadings
constants = ram_matrices.constants
A_ind_c = [linear2cartesian(ind, (n_nod, n_nod)) for ind in A_ind]
A_ind_c = [linear2cartesian(ind, (n_var, n_var)) for ind in A_ind]
# ind_Λ = findall([is_in_Λ(ind_vec, F_ind) for ind_vec in A_ind_c])

function calculate_lambda(
Expand All @@ -99,7 +99,7 @@ function start_fabin3(ram_matrices::RAMMatrices, Σ, μ)
end
end

for i in setdiff(1:n_nod, F_ind)
for i in setdiff(1:n_var, F_ind)
reference = Int64[]
indicators = Int64[]
indicator2parampos = Dict{Int, Int}()
Expand Down
10 changes: 5 additions & 5 deletions src/additional_functions/start_val/start_simple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,17 @@ function start_simple(
start_means = 0.0,
kwargs...,
)
A_ind, S_ind, F_ind, M_ind, params = ram_matrices.A_ind,
A_ind, S_ind, F_ind, M_ind, n_par = ram_matrices.A_ind,
ram_matrices.S_ind,
ram_matrices.F_ind,
ram_matrices.M_ind,
ram_matrices.params
nparams(ram_matrices)

n_par = length(params)
start_val = zeros(n_par)
n_var, n_nod = ram_matrices.size_F
n_obs = nobserved_vars(ram_matrices)
n_var = nvars(ram_matrices)

C_indices = CartesianIndices((n_nod, n_nod))
C_indices = CartesianIndices((n_var, n_var))

for i in 1:n_par
if length(S_ind[i]) != 0
Expand Down
57 changes: 57 additions & 0 deletions src/frontend/common.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# API methods supported by multiple SEM.jl types

"""
nparams(semobj)

Return the number of parameters in a SEM model associated with `semboj`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Return the number of parameters in a SEM model associated with `semboj`.
Return the number of parameters in a SEM model associated with `semobj`.


See also [`params`](@ref).
"""
nparams(semobj) = length(params(semobj))

"""
nvars(semobj)

Return the number of variables in a SEM model associated with `semobj`.

See also [`vars`](@ref).
"""
nvars(semobj) = length(vars(semobj))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function (and the next ones) do not seem to be exported - I think we should do that.


"""
nobserved_vars(semobj)

Return the number of observed variables in a SEM model associated with `semobj`.
"""
nobserved_vars(semobj) = length(observed_vars(semobj))

"""
nlatent_vars(semobj)

Return the number of latent variables in a SEM model associated with `semobj`.
"""
nlatent_vars(semobj) = length(latent_vars(semobj))

"""
param_indices(semobj)

Returns a dict of parameter names and their indices in `semobj`.

# Examples
```julia
parind = param_indices(my_fitted_sem)
parind[:param_name]
```

See also [`params`](@ref).
"""
param_indices(semobj) = Dict(par => i for (i, par) in enumerate(params(semobj)))

Check warning on line 48 in src/frontend/common.jl

View check run for this annotation

Codecov / codecov/patch

src/frontend/common.jl#L48

Added line #L48 was not covered by tests

"""
nsamples(semobj)

Return the number of samples (observed data points).

For ensemble models, return the sum over all submodels.
"""
function nsamples end
1 change: 1 addition & 0 deletions src/frontend/fit/SemFit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ end

params(fit::SemFit) = params(fit.model)
nparams(fit::SemFit) = nparams(fit.model)
nsamples(fit::SemFit) = nsamples(fit.model)

# access fields
minimum(sem_fit::SemFit) = sem_fit.minimum
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/fit/fitmeasures/BIC.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

Return the bayesian information criterion.
"""
BIC(sem_fit::SemFit) = minus2ll(sem_fit) + log(n_obs(sem_fit)) * nparams(sem_fit)
BIC(sem_fit::SemFit) = minus2ll(sem_fit) + log(nsamples(sem_fit)) * nparams(sem_fit)
8 changes: 4 additions & 4 deletions src/frontend/fit/fitmeasures/RMSEA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ Return the RMSEA.
function RMSEA end

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

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

function RMSEA(df, chi2, n_obs)
rmsea = (chi2 - df) / (n_obs * df)
function RMSEA(df, chi2, nsamples)
rmsea = (chi2 - df) / (nsamples * df)
rmsea > 0 ? nothing : rmsea = 0
return sqrt(rmsea)
end
11 changes: 6 additions & 5 deletions src/frontend/fit/fitmeasures/chi2.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ function χ² end

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

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

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

function χ²(sem_fit::SemFit, model::SemEnsemble, lossfun::L) where {L <: SemML}
check_ensemble_length(model)
check_lossfun_types(model, L)
F_G = sem_fit.minimum
F_G -= sum([
w * (logdet(m.observed.obs_cov) + m.observed.n_man) for
w * (logdet(m.observed.obs_cov) + nobserved_vars(m.observed)) for
(w, m) in zip(model.weights, model.sems)
])
return (sum(n_obs.(model.sems)) - 1) * F_G
return (nsamples(model) - 1) * F_G
end

function χ²(sem_fit::SemFit, model::SemEnsemble, lossfun::L) where {L <: SemFIML}
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/fit/fitmeasures/df.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ df(sem_fit::SemFit) = df(sem_fit.model)
df(model::AbstractSem) = n_dp(model) - nparams(model)

function n_dp(model::AbstractSemSingle)
nman = n_man(model)
ndp = 0.5(nman^2 + nman)
nvars = nobserved_vars(model)
ndp = 0.5(nvars^2 + nvars)
if !isnothing(model.imply.μ)
ndp += n_man(model)
ndp += nvars
end
return ndp
end
Expand Down
30 changes: 15 additions & 15 deletions src/frontend/fit/fitmeasures/minus2ll.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ minus2ll(sem_fit::SemFit, obs, imp, optimizer, args...) =

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

# WLS --------------------------------------------------------------------------------------
minus2ll(minimum::Number, obs, imp::Union{RAM, RAMSymbolic}, optimizer, loss_ml::SemWLS) =
Expand All @@ -41,8 +41,8 @@ function minus2ll(
loss_ml::SemFIML,
)
F = minimum
F *= n_obs(observed)
F += sum(log(2π) * observed.pattern_n_obs .* observed.pattern_nvar_obs)
F *= nsamples(observed)
F += sum(log(2π) * observed.pattern_nsamples .* observed.pattern_nobs_vars)
return F
end

Expand All @@ -53,26 +53,26 @@ function minus2ll(observed::SemObservedMissing)
minus2ll(
observed.em_model.μ,
observed.em_model.Σ,
observed.n_obs,
observed.rows,
nsamples(observed),
pattern_rows(observed),
observed.patterns,
observed.obs_mean,
observed.obs_cov,
observed.pattern_n_obs,
observed.pattern_nvar_obs,
observed.pattern_nsamples,
observed.pattern_nobs_vars,
)
else
em_mvn(observed)
minus2ll(
observed.em_model.μ,
observed.em_model.Σ,
observed.n_obs,
observed.rows,
nsamples(observed),
pattern_rows(observed),
observed.patterns,
observed.obs_mean,
observed.obs_cov,
observed.pattern_n_obs,
observed.pattern_nvar_obs,
observed.pattern_nsamples,
observed.pattern_nobs_vars,
)
end
end
Expand All @@ -85,13 +85,13 @@ function minus2ll(
patterns,
obs_mean,
obs_cov,
pattern_n_obs,
pattern_nvar_obs,
pattern_nsamples,
pattern_nobs_vars,
)
F = 0.0

for i in 1:length(rows)
nᵢ = pattern_n_obs[i]
nᵢ = pattern_nsamples[i]
# missing pattern
pattern = patterns[i]
# observed data
Expand All @@ -106,7 +106,7 @@ function minus2ll(
F += F_one_pattern(meandiffᵢ, Σᵢ⁻¹, Sᵢ, ld, nᵢ)
end

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

return F
Expand Down
11 changes: 0 additions & 11 deletions src/frontend/fit/fitmeasures/n_man.jl

This file was deleted.

16 changes: 0 additions & 16 deletions src/frontend/fit/fitmeasures/n_obs.jl

This file was deleted.

2 changes: 1 addition & 1 deletion src/frontend/fit/standard_errors/bootstrap.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
end

if isnothing(data)
data = get_data(observed(model(semfit)))
data = samples(observed(model(semfit)))

Check warning on line 28 in src/frontend/fit/standard_errors/bootstrap.jl

View check run for this annotation

Codecov / codecov/patch

src/frontend/fit/standard_errors/bootstrap.jl#L28

Added line #L28 was not covered by tests
end

data = prepare_data_bootstrap(data)
Expand Down
Loading
Loading