StructuralEquationModels.jl icon indicating copy to clipboard operation
StructuralEquationModels.jl copied to clipboard

Fixed **and** Labeled loadings don't show up in the degrees of freedom.

Open nickhaf opened this issue 8 months ago • 4 comments

I have a question regarding the degrees of freedom for fixed and labeled loadings. If a loading is labeled and fixed, the degrees of freedom are not updated compared to the fit with the loading unfixed. If the fixed loading is not labeled, there is no problem:

Just fixed, not labeled

using StructuralEquationModels, StenoGraphs
data = example_data("political_democracy")

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

graph_2 = @StenoGraph begin

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

    # latent regressions
    ind60 → dem60
    dem60 → dem65
    ind60 → dem65

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

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

end

partable_2 = ParameterTable(
    latent_vars = latent_vars,
    observed_vars = observed_vars,
    graph = graph_2)

model_2 = Sem(
    specification = partable_2,
    data = data
)

df(model_2)

Fixed and labeled

graph_3 = @StenoGraph begin

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

    # latent regressions
    ind60 → dem60
    dem60 → dem65
    ind60 → dem65

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

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

end

partable_3 = ParameterTable(
    latent_vars = latent_vars,
    observed_vars = observed_vars,
    graph = graph_3)

model_3 = Sem(
    specification = partable_3,
    data = data
)

df(model_3)

When fitting this model with sem_fit(model_2) I get a warning that I'm using a labeled constant. However, I can also extract the df before fitting the model, in which case I don't get the warning. In either case, I found this a bit counterintuitive, in lavaan both is possible:

Lavaan

library(lavaan)

## Not fixed: 
model <- '
  # measurement model
    ind60 =~ x1 + x2 + x3
    dem60 =~ y1 + y2 + y3 + y4
    dem65 =~ y5 + y6 + y7 + y8
  # regressions
    dem60 ~ ind60
    dem65 ~ ind60 + dem60
  # residual correlations
    y1 ~~ y5
    y2 ~~ y4 + y6
    y3 ~~ y7
    y4 ~~ y8
    y6 ~~ y8
'

fit_1 <- sem(model, data = PoliticalDemocracy)
fitMeasures(fit_1, "df")


## Fixed and labeled:
model <- '
  # measurement model
    ind60 =~ x1 + 0*testlabel*x2 + x3
    dem60 =~ y1 + y2 + y3 + y4
    dem65 =~ y5 + y6 + y7 + y8
  # regressions
    dem60 ~ ind60
    dem65 ~ ind60 + dem60
  # residual correlations
    y1 ~~ y5
    y2 ~~ y4 + y6
    y3 ~~ y7
    y4 ~~ y8
    y6 ~~ y8
'

fit_2 <- sem(model, data = PoliticalDemocracy)
fitMeasures(fit_2, "df")

nickhaf avatar Jul 01 '24 09:07 nickhaf