Models with mean structures

To make use of mean structures in your model, you have to

  1. Specify your model with a mean structure. The sections Graph interface and RAMMatrices interface both explain how this works.
  2. Build your model with a meanstructure. We explain how that works in the following.

Lets say you specified A first model as a graph with a meanstructure:

using StructuralEquationModels

observed_vars = [:x1, :x2, :x3, :y1, :y2, :y3, :y4, :y5, :y6, :y7, :y8]
latent_vars = [:ind60, :dem60, :dem65]

graph = @StenoGraph begin

    # loadings
    ind60 → fixed(1)*x1 + x2 + x3
    dem60 → fixed(1)*y1 + y2 + y3 + y4
    dem65 → fixed(1)*y5 + y6 + y7 + y8

    # latent regressions
    dem60 ← ind60
    dem65 ← dem60
    dem65 ← ind60

    # variances
    _(observed_vars) ↔ _(observed_vars)
    _(latent_vars) ↔ _(latent_vars)

    # covariances
    y1 ↔ y5
    y2 ↔ y4 + y6
    y3 ↔ y7
    y8 ↔ y4 + y6

    # means
    Symbol("1") → _(observed_vars)
end

partable = ParameterTable(
    latent_vars = latent_vars, 
    observed_vars = observed_vars, 
    graph = graph)

that is, all observed variable means are estimated freely.

To build the model with a meanstructure, we proceed as usual, but pass the argument meanstructure = true. For our example,

data = example_data("political_democracy")

model = Sem(
    specification = partable,
    data = data,
    meanstructure = true
)

sem_fit(model)
Fitted Structural Equation Model 
=============================================== 
--------------------- Model ------------------- 

Structural Equation Model 
- Loss Functions 
   SemML
- Fields 
   observed:    SemObservedData 
   imply:       RAM 
   optimizer:   SemOptimizerOptim 

------------- Optimization result ------------- 

 * Status: success

 * Candidate solution
    Final objective value:     2.120543e+01

 * Found with
    Algorithm:     L-BFGS

 * Convergence measures
    |x - x'|               = 4.36e-05 ≰ 1.5e-08
    |x - x'|/|x'|          = 5.84e-06 ≰ 0.0e+00
    |f(x) - f(x')|         = 1.86e-09 ≰ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 8.77e-11 ≤ 1.0e-10
    |g(x)|                 = 9.70e-05 ≰ 1.0e-08

 * Work counters
    Seconds run:   0  (vs limit Inf)
    Iterations:    187
    f(x) calls:    558
    ∇f(x) calls:   558

If we build the model by parts, we have to pass the meanstructure = true argument to every part that requires it (when in doubt, simply comsult the documentation for the respective part).

For our example,

observed = SemObservedData(specification = partable, data = data, meanstructure = true)

imply_ram = RAM(specification = partable, meanstructure = true)

ml = SemML(observed = observed, meanstructure = true)

model = Sem(observed, imply_ram, SemLoss(ml), SemOptimizerOptim())

sem_fit(model)
Fitted Structural Equation Model 
=============================================== 
--------------------- Model ------------------- 

Structural Equation Model 
- Loss Functions 
   SemML
- Fields 
   observed:    SemObservedData 
   imply:       RAM 
   optimizer:   SemOptimizerOptim 

------------- Optimization result ------------- 

 * Status: success

 * Candidate solution
    Final objective value:     2.120543e+01

 * Found with
    Algorithm:     L-BFGS

 * Convergence measures
    |x - x'|               = 4.36e-05 ≰ 1.5e-08
    |x - x'|/|x'|          = 5.84e-06 ≰ 0.0e+00
    |f(x) - f(x')|         = 1.86e-09 ≰ 0.0e+00
    |f(x) - f(x')|/|f(x')| = 8.77e-11 ≤ 1.0e-10
    |g(x)|                 = 9.70e-05 ≰ 1.0e-08

 * Work counters
    Seconds run:   0  (vs limit Inf)
    Iterations:    187
    f(x) calls:    558
    ∇f(x) calls:   558