#::::::::::::::::: PSY9140 SEM course Day 3 :::::::::::::::::::::::::::::###

################### (0) Preparations #######################################
# installing packages; has just to be done once.
# install.packages("lavaan")
# install.packages("psych")
# install.packages("lavaanPlot")
# install.packages("semPlot")
# install.packages("semTable")
# install.packages("ggcorrplot")

# load packages. Has to be done each time R is started.
library(lavaan)
library(psych)
library(lavaanPlot)
library(semPlot)
library(semTable)
library(ggcorrplot)


################### (1) Exercise 1 #######################################

# load the datafile "data1.csv" as a dataframe with the name "df1" by using the "read.csv" command
df1 <- read.csv("https://www.sv.uio.no/psi/personer/vit/tilmanns/psy9140/data1.csv")

# descriptive statistics for all variables in "df1" by using "describe" from the "psych" package
describe(df1)

# (1) Specify a multiple regression model with body image (body ), depressive symptoms (dep) and 
# body mass index (bmi) as independent variable and dieting (diet) as dependent variable

# Specify the model
model <- "diet ~ body + dep + bmi
          body ~~ dep + bmi
          dep ~~ bmi"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df1)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE)

# (2) Which independent variable is the strongest predictor of dieting?
# Answer: The standardized regression coefficients show that body image is the strongest predictor (beta = -.315)

# (3) How much variance is explained by all three independent variables together?
# Answer: The standardized residual variance for diet provides information about R2. R2 = 1 - .778 = .222. 
# It is also possible to obtain information about R2 directly from lavaan by using the rsquare command:

summary(fit,  fit.measures = TRUE, standardized=TRUE, rsquare = TRUE)

# (4) Estimate the model with and without correlations among the independent variables. 
# In which way do the results differ?

# We can estimate the model without correlations among independent variables:
model <- "diet ~ body + dep + bmi"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df1)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE)

# Results show that variances of independent variables and covariances among independent variables are not estimated.
# Moreover, only 2268 cases are used in the analyses because of missing data for some cases on the independent variable.
# Missing data also lead to somewhat different estimates of the regression coefficients.


################### (2) Exercise 2 #######################################

# (1) Specify the multiple mediation model as depicted on the previous slide.
# Specify the model in the next line
model <- "diet ~ body + dep + girl
          dep ~ body + girl
          body ~ girl"
          
# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df1)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE)

# (2) Point out the different mediational processes that can be estimated in the model.
# Answer: Four mediational processes / indirect paths can be estimated: 
# girl-->body-->dep
# girl-->dep-->diet
# body-->dep-->diet
# girl-->body-->dep-->diet

# (3) Test one (or several) of them by using the := operator command.
# The solution provided here tests all mediational paths simultaneously:

model <- "diet ~ body_diet*body + dep_diet*dep + girl_diet*girl
          dep ~ body_dep*body + girl_dep*girl
          body ~ girl_body*girl
          # Indirect effects
          ind_body_dep_diet := body_dep*dep_diet
          ind_girl_body_dep := girl_body*body_dep
          ind_girl_body_diet := girl_body*body_diet
          ind_girl_dep_diet := girl_dep*dep_diet
          ind_girl_body_dep_diet := girl_body*body_dep*dep_diet
          # Total effects
          tot_body_diet := body_dep*dep_diet + body_diet
          tot_girl_dep := girl_body*body_dep + girl_dep
          tot_girl_diet := girl_dep*dep_diet +  girl_body*body_diet + girl_body*body_dep*dep_diet + girl_diet"

# (4) Use Sobel's test and/or bootstrapping (with 100 draws) to test for mediation. 

# The model is first run by using the Sobel's test (default)
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df1)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE)
# Note that the Defined parameter names are shortened by lavaan

# The model is now run by using bootstrapping (100 draws - you should have more draws, e.g., 5,000 but that takes time)
fit <-sem(model, missing = "ML", meanstructure = TRUE, se = "bootstrap", bootstrap = 100, data = df1)

# Providing output for the model 
summary(fit, fit.measures = TRUE, standardized =TRUE, ci = TRUE)

# Providing bias corrected confidence intervals from the bootstrap
parameterestimates(fit, boot.ci.type = "bca.simple")


################### (3) Exercise 3 #######################################

# load the datafile "data2.csv" as a dataframe with the name "df2" by using the "read.csv" command
df2 <- read.csv("https://www.sv.uio.no/psi/personer/vit/tilmanns/psy9140/data2.csv")

# descriptive statistics for all variables in "df2"
describe(df2)

# (1) Test the measure of Body Image by conducting a CFA

# Specify the model in the next line
model <- "body =~ body1 + body2 + body3 + body4 + body5 + body6 + body7"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df2)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE, modindices = TRUE)

# (2) Modify the measurement model, if necessary. Use both results from the analyses (for instance 
# modification indices and factor loadings) and substantive considerations when modifying the model.

# One possible solution is to remove body5 and body7 from the CFA to improve model fit.

model <- "body =~ body1 + body2 + body3 + body4 + body6"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df2)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE, modindices = TRUE)

# (3) Combine all factors in one model with correlations among latent factors

model <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Covariances among latent factors
            diet ~~ dep
            diet ~~ body
            dep ~~ body"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df2)

# Providing output for the model
summary(fit, fit.measures = TRUE, standardized=TRUE)

# (4) Examine model fit. Does the model fit the data well?
# Answer: The RMSEA, CFI, and TLI indicate that model fit is rather good.


################### (4) Exercise 4 #####################################

# (1) Specify and estimate the combined measurement and structural model depicted below.

model <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural part of the model
            diet ~ dep
            diet ~ body
            dep ~ body"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df2)

# Providing output for the model
summary(fit, fit.measures = TRUE, standardized=TRUE)

# (2) Assess mediation by using the ":=" operator. Do the results differ from the mediation model based on mean scores?

model <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural part of the model
            diet ~ b*dep
            diet ~ c*body
            dep ~ a*body
          # Mediation
            ab := a*b
            total := a*b + c"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df2)

# Providing output for the model
summary(fit, fit.measures = TRUE, standardized=TRUE)


################### (5) Exercise 5 #######################################

# (1) Use parameter constraints in to examine whether the relationships between the variables are moderated by gender.
# (2) Which paths show significant gender differences?

# Model without constraints
unconstraint_model <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural models
            diet ~ dep
            diet ~ body
            dep ~ body"

# Model where regression coefficient for body-->dep is forced to be equal across gender
constraint_model_a <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural models
            diet ~ dep
            diet ~ body
            dep ~ c(a,a)*body"

# Model where regression coefficient for dep-->diet is forced to be equal across gender
constraint_model_b <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural models
            diet ~ c(b,b)*dep
            diet ~ body
            dep ~ body"

# Model where regression coefficient for body-->diet is forced to be equal across gender
constraint_model_c <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural models
            diet ~ dep
            diet ~ c(c,c)*body
            dep ~ body"

# Running the models
fit_unconstraint <- sem(unconstraint_model, missing = "ML", meanstructure = TRUE, data = df2, group = "girl")
fit_constraint_a <- sem(constraint_model_a, missing = "ML", meanstructure = TRUE, data = df2, group = "girl")
fit_constraint_b <- sem(constraint_model_b, missing = "ML", meanstructure = TRUE, data = df2, group = "girl")
fit_constraint_c <- sem(constraint_model_c, missing = "ML", meanstructure = TRUE, data = df2, group = "girl")

# Providing output for the models
summary(fit_unconstraint, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint_a, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint_b, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint_c, fit.measures = TRUE, standardized=TRUE)

# Compare model fit for unconstraint and constraint models
anova(fit_unconstraint, fit_constraint_a)
anova(fit_unconstraint, fit_constraint_b)
anova(fit_unconstraint, fit_constraint_c)

# Only the regression coefficient for dep-->diet is significantly different for boys and girls, 
# as shown by the significant chi-square difference test.


################### (6) Exercise 6 #######################################

# (1) Conduct a multi group analysis for a CFA for depressive symptoms. 
# Do you find measurement invariance (equal factor loadings) across gender?

model <- "dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6"

# Running the model
fit_unconstraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                      group = "girl")

fit_constraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                  group = "girl", group.equal = c("loadings"))

# Providing output for the model
summary(fit_unconstraint, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint, fit.measures = TRUE, standardized=TRUE)

anova(fit_unconstraint, fit_constraint)

# The chi-square difference test is not significant and indicates as such measurement invariance across gender.

# (2) Examine measurement invariance across gender for dieting. 
# Which factor loadings have to be freed to improve model fit?

model <- "diet =~ diet1 + diet2 + diet3 + diet4"

# Running the models
fit_unconstraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                        group = "girl")

fit_constraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                      group = "girl", group.equal = c("loadings"))

# Providing output for the model
summary(fit_unconstraint, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint, fit.measures = TRUE, standardized=TRUE)

anova(fit_unconstraint, fit_constraint)
# Chi-square difference tests show a clear difference in model fit between the two models, 
# thereby indicating that measurement invariance cannot be assumed.

# We can use the lavTestScore() function to examine model fit when specific factor loadings are freed across groups
lavTestScore(fit_constraint)
# The results show that all three factor loadings may be freed to improve model fit

# (3) Estimate the following structural model with depressive symptoms as predictor and dieting as 
# outcome variable in a two group analysis with measurement invariance. Is the relationship between
# depressive symptoms and dieting moderated by gender?

model <- "dep =~ dep1 + dep2 + dep3 + dep4 + dep5 + dep6
          diet =~ diet1 + diet2 + diet3 + diet4
          diet ~ dep"

# Running the models
fit_unconstraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                        group = "girl", group.equal = c("loadings"))

fit_constraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                      group = "girl", group.equal = c("loadings", "regressions"))

# Providing output for the model
summary(fit_unconstraint, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint, fit.measures = TRUE, standardized=TRUE)

anova(fit_unconstraint, fit_constraint)
# Chi-square difference test show a p-value of .055. As a result, there is no moderation by gender, 
# but the p-value is close to being significant.

################### (7) Exercise 7 #######################################

# (1) Use the anova or lavTestLRT function to compare the estimated model with a model without measurement invariance.

model <- "# CFA
            body =~ body1 + body2 + body3 + body4 + body6
            diet =~ diet1 + diet2 + diet3 + diet4
          # Structural models
            diet ~ body"

# Running the model without constraints
fit_unconstraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                        group = "girl")

# Running the model with constraints
fit_constraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                        group = "girl", group.equal = c("loadings"))

# Providing output for the model
summary(fit_unconstraint, fit.measures = TRUE, standardized=TRUE)
summary(fit_constraint, fit.measures = TRUE, standardized=TRUE)

anova(fit_unconstraint, fit_constraint)
# The chi-square difference tests indicate that the model with measurement invariance
# has worse model fit than the model without measurement invariance

# (2) Use the lavTestScore () function to obtain information about constraint parameters that may be freed

lavTestScore(fit_constraint)
# Results show that all factor loadings can be freed to obtain better model fit, but the factor loading of 
# the body4 indicator (.p4.) and diet2 (.p7.) provide the largest decrease in chi-square when freed. 

# (3) Use the group.partial = function to free one or several parameters (e.g., factor loadings)

# We will here free .p4. and .p7.
fit_partly_constraint <- sem(model, missing = "ML", meanstructure = TRUE, data = df2, 
                         group = "girl", group.equal = c("loadings"), 
                         group.partial = c("body =~ body4", "diet =~ diet2"))

summary(fit_partly_constraint, fit.measures = TRUE, standardized=TRUE)
# Even after freeing the two factor loadings, model fit is still not adequate.


################### (8) Exercise 8 #######################################

# (1) Transfer data from SPSS to R

# installing package "foreign (has just to be done once.
# install.packages("foreign")

# Load package "foreign"
library(foreign)

# Load SPSS file as data frame
df_cosmetics <- read.spss("https://www.sv.uio.no/psi/personer/vit/tilmanns/psy9140/cosmetics.sav",
                          use.value.labels=FALSE, to.data.frame=TRUE)

# descriptive statistics for all variables in "df_cosmetics"
describe(df_cosmetics)

# (2) Specify and estimate the following model

# Specify the model
model <- "wish ~ invest + satisfac
          invest ~~ satisfac"

# Running the model
fit <- sem(model, missing = "ML", meanstructure = TRUE, data = df_cosmetics)

# Providing output for the model
summary(fit,  fit.measures = TRUE, standardized=TRUE)

