Fixed **and** Labeled loadings don't show up in the degrees of freedom.
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")
Thanks a lot for raising this issue, I will have a look!
I'm not sure how to proceed - internally, we label all fixed parameters as :const and reserve that label for fixed parameters. So we could either allow for labeled fixed parameters, or throw an error on model construction. @nickhaf is there actually a use case for labeled & fixed parameters?
I played around with a "relaxed lasso" implementation when I noticed this issue. For this very niche application it might be helpful to be able to label and fix parameters, but even here it is definitely not necessary. So in my opinion throwing a clear error message would be enough for now.
This should be fixed in the next release. Thanks again for reporting.