The Family Stress Model outlines a process in which economic stressors influence child and adolescent adjustment through various mediating pathways. This markdown examines how stress is conceptualized and measured across groups and time in various ways by using data from the Baby’s First Years study.
Baby’s First Years (BFY) data are freely available to the research community through the Interuniversity Consortium for Political and Social Research (ICPSR). Some fast facts about BFY:
Who: 1,000 mothers of infants with incomes below the federal poverty line
What: Mothers receive a monthly unconditional cash gift of either $333/month or $20/month for the first 52 months of their child’s life
Where: New York City, New Orleans, Twin Cities, Omaha metropolitan area
When: Recruitment of study participants began in May 2018 and ended in June 2019. Quantitative data is being collected just after birth and when the child reaches 12, 24, 36, and 48 months of age. This study uses data from waves 1 (child is 12 months) and 2 (child is 24 months).
Why: Examine the causal impact of monthly, unconditional cash gifts to low-income mothers on their child’s cognitive, socio-emotional, and brain development
The Baby’s First Years Study plans to analyze how “aspects of family life hypothesized to be affected by poverty, including parent stress” contribute to child development, using an longitudinal, experimental design.
Since stress is known to be a major contributor to childhood outcomes as depicted by the Family Stress Model, and stress will be used as a major focal predictor in future analyses using the Baby’s First Years data set (see Troller-Renfree, S. et al., 2022), it’s important to understand if (1) the latent factor “stress” is equivalent across experimental groups, and (2), the latent factor “stress” is equivalent across time.
To examine the stress latent factor, I will use four indicators of stress. These indicators were measured at Time 1, when children were 12 months old (2019-2020), and Time 2, when children were 24 months old (2020-2021).
Perceived Stress - Perceived maternal stress was assessed using the perceived stress scale (PSS; Cohen et al., 1994; Cohen & Williamson, 1988).
Parenting Stress - Parenting stress was assessed through a pre-registered seven-item index. Three items were used from the Aggravation in Parenting Scale (PSID-Child Development Supplement) and four were used from the Cleminshaw – Guidubaldi Parent Satisfaction Scale (Guidubaldi & Cleminshaw, 2010).
Economic Stress - Economic stress was assessed through mothers’ self-report on nine questions (Kling et al., 2007).
Household Chaos - Household stress was measured through the Confusion, Hubbub, and Order Scale (CHAOS; Matheny et al., 1995).
To examine the association between stress and child developmental outcomes at Time 2 (child is 24 months old), I will use the following outcome variable:
Is the latent construct “stress” equivalent across experimental groups?
#Loading data from Year 2 of the study:
load("/Users/allisonlanger/Desktop/SEM_final/37871-0003-Data.rda")
# loading indicator variables
Perceived<-scale(da37871.0003$MPERCEIVEDSTRESSA2) #self report perceived stress
Parenting<-scale(da37871.0003$MPARENTINGSTRESSA2) #self report parenting stress
Economic<-scale(as.numeric(da37871.0003$EXPENSEWORRYA2)) #self report economic stress/expense worry
Household_Chaos<-scale(da37871.0003$HHCHAOSA2) #household chaos scale
outcome<-scale(da37871.0003$CBITSEAPROBLEMA2) #BITSEA scores
#creating dataframe
df<-data.frame(Perceived, Parenting, Economic, Household_Chaos, outcome)
#getting experimental group info & creating separate groups
df$group<-NA
df$group<-as.numeric(da37871.0003$TREAT)
df$group[df$group=="1"]<-"0"
df$group[df$group=="2"]<-"1"
Correlation Table for Variables at Time 2:
apa.cor.table(
df,
table.number = 1,
show.conf.interval = TRUE,
show.sig.stars = TRUE,
landscape = TRUE)
##
##
## Table 1
##
## Means, standard deviations, and correlations with confidence intervals
##
##
## Variable M SD 1 2 3
## 1. Perceived -0.00 1.00
##
## 2. Parenting -0.00 1.00 .45**
## [.40, .50]
##
## 3. Economic -0.00 1.00 -.30** -.20**
## [-.36, -.24] [-.26, -.13]
##
## 4. Household_Chaos -0.00 1.00 .41** .34** -.16**
## [.36, .47] [.28, .39] [-.23, -.10]
##
## 5. outcome 0.00 1.00 .36** .30** -.18**
## [.30, .42] [.24, .36] [-.25, -.12]
##
## 4
##
##
##
##
##
##
##
##
##
##
##
## .33**
## [.27, .39]
##
##
## Note. M and SD are used to represent mean and standard deviation, respectively.
## Values in square brackets indicate the 95% confidence interval.
## The confidence interval is a plausible range of population correlations
## that could have caused the sample correlation (Cumming, 2014).
## * indicates p < .05. ** indicates p < .01.
##
Code for function to get commonly used fit indices:
trim_fit <- function(inp, outname) {
#Multiple steps to get smaller amount of information
fit_res <- data.frame(fitMeasures(inp))
fit_res <- round(fit_res, 3)
fit_res$index <- row.names(fit_res)
#rownames(fit_res) <- c()
# Retaining only the commonly used indices; the last '[c(2,1)]' just changes the
# order of the columns
out <- fit_res[fit_res$index %in% c("npar", "chisq", "df", "pvalue",
"rmsea", "rmsea.ci.lower", "rmsea.ci.upper",
"rmsea.pvalue", "cfi", "tli", "aic", "bic", "bic2"),][c(2,1)]
names(out) <- c("index", "value")
assign(deparse(substitute(outname)), out, envir=.GlobalEnv)
}
#Building the models
model <- 'Stress =~ NA*Perceived + Parenting + Economic + Household_Chaos
Stress ~~ 1*Stress'
#subset group into high cash (1) and low cash (0) groups
model_high_res <- sem(model, subset(df, group == 1))
model_low_res <- sem(model, subset(df, group == 0))
library(semPlot)
trim_fit(model_high_res, model_high_res_fit)
trim_fit(model_low_res, model_low_res_fit)
#Putting the high cash and low cash groups together
H_L_Fit <- cbind(model_high_res_fit, model_low_res_fit[2])
names(H_L_Fit) <- c("Index", "High Cash Model", "Low Cash Model")
#Configural invariance model - Are the same indicators reflective of stress across groups?
model_configural <- 'Stress =~ NA*Perceived + Parenting + Economic + Household_Chaos
Stress ~~ 1*Stress'
model_Configural_res <- sem(model_configural, df, group = "group")
#Model fit information
trim_fit(model_Configural_res, model_Configural_res_fit)
#View model fit information
model_Configural_res_fit
parameterestimates(model_Configural_res, ci = F, standardized = T)
#Metric invariance model - Are factor loadings to indicators equal across groups?
model_metric <- 'Stress =~ c(NA, NA)*Perceived + c(l1, l1)*Perceived + c(l2,l2)*Parenting + c(l3, l3)*Economic + c(l4,l4)*Household_Chaos
Stress ~~ c(1, NA)*Stress'
model_metric_res <- sem(model_metric, df, group = "group")
#Model fit information
trim_fit(model_metric_res, model_metric_res_fit)
#View model fit information
model_metric_res_fit
parameterestimates(model_metric_res, ci = F, standardized = T)
#Scalar invariance model - Are intercepts of indicator variables equal across groups?
model_scalar <- 'Stress =~ c(NA, NA)*Perceived + c(l1, l1)*Perceived + c(l2,l2)*Parenting + c(l3, l3)*Economic + c(l4,l4)*Household_Chaos
Stress ~~ c(1, NA)*Stress
Perceived ~ c(i1, i1)*1
Parenting ~ c(i2, i2)*1
Economic ~ c(i3, i3)*1
Household_Chaos ~ c(i4, i4)*1
Stress ~ c(1, NA)*1'
model_scalar_res <- sem(model_scalar, df, group = "group")
#Model fit information
trim_fit(model_scalar_res, model_scalar_res_fit)
#View model fit information
model_scalar_res_fit
parameterestimates(model_scalar_res, ci = F, standardized = T)
Fit Statistics for Configural, Metric, and Scalar Invariance for Stress across Experimental Groups
#Pulling all model fit together
all_fit <- cbind(model_Configural_res_fit, model_metric_res_fit[2], model_scalar_res_fit[2])
names(all_fit) <- c("Index", "Configural Model", "Metric Model", "Scalar Model")
print(all_fit)
## Index Configural Model Metric Model Scalar Model
## npar npar 24.000 21.000 18.000
## chisq chisq 6.309 8.801 11.110
## df df 4.000 7.000 10.000
## pvalue pvalue 0.177 0.267 0.349
## cfi cfi 0.995 0.996 0.998
## tli tli 0.986 0.994 0.997
## aic aic 9919.755 9916.247 9912.556
## bic bic 10035.409 10017.445 9999.296
## bic2 bic2 9959.188 9950.751 9942.130
## rmsea rmsea 0.036 0.024 0.016
## rmsea.ci.lower rmsea.ci.lower 0.000 0.000 0.000
## rmsea.ci.upper rmsea.ci.upper 0.085 0.065 0.054
## rmsea.pvalue rmsea.pvalue 0.615 0.819 0.919
The fit indices for the configural (RMSEA = 0.036; CFI = .995), metric (RMSEA = 0.24; CFI = 0.996) and scalar (RMSEA = 0.016; CFI= 0.998) models suggest the models support invariance in measuring the latent factor “stress” across experimental groups at Time 2 of the study. We can therefore make mean-level comparisons across the experimental groups with this data wave. Yay!
Is the association between stress and child outcomes different across experimental groups?
Null: Association between stress and child outcome (BITSEA) is equal across groups.
Alternative: Association between stress and child outcome (BITSEA) differs across groups.
null <- '
Stress =~ c(NA, NA)*Perceived + c(l1, l1)*Perceived + c(l2,l2)*Parenting + c(l3, l3)*Economic + c(l4,l4)*Household_Chaos
Stress ~~ c(1, NA)*Stress
Perceived ~ c(i1, i1)*1
Parenting ~ c(i2, i2)*1
Economic ~ c(i3, i3)*1
Household_Chaos ~ c(i4, i4)*1
Stress ~ c(1, NA)*1
#Structural Path
outcome ~ c(eq, eq)*Stress'
alt <- '
Stress =~ c(NA, NA)*Perceived + c(l1, l1)*Perceived + c(l2,l2)*Parenting + c(l3, l3)*Economic + c(l4,l4)*Household_Chaos
Stress ~~ c(1, NA)*Stress
Perceived ~ c(i1, i1)*1
Parenting ~ c(i2, i2)*1
Economic ~ c(i3, i3)*1
Household_Chaos ~ c(i4, i4)*1
Stress ~ c(1, NA)*1
#Structural Path
outcome ~ c(r, l)*Stress'
null_res <- sem(null, df, group = "group")
alt_res <- sem(alt, df, group = "group")
anova(null_res, alt_res)
We retain the null (chi-square=1.04, p>.05) that the association between stress and child outcomes, as measured by the BITSEA, is equal across experimental groups (high or low cash payments).
Is the latent construct “stress” equivalent across time?
#load T1 Data
load("/Users/allisonlanger/Desktop/SEM_final/37871-0002-Data.rda")
#load T2 Data
load("/Users/allisonlanger/Desktop/SEM_final/37871-0003-Data.rda")
Perceived1<-scale(da37871.0002$MPERCEIVEDSTRESSA1) #self report perceived stress
Perceived2<-scale(da37871.0003$MPERCEIVEDSTRESSA2)
Parenting1<-scale(da37871.0002$MPARENTINGSTRESSA1) #self report parenting stress
Parenting2<-scale(da37871.0003$MPARENTINGSTRESSA2)
Economic1<-scale(as.numeric(da37871.0002$EXPENSEWORRYA1)) #self report economic stress/expense worry
Economic2<-scale(as.numeric(da37871.0003$EXPENSEWORRYA2))
Household_Chaos1<-scale(da37871.0002$HHCHAOSA1) #household chaos scale
Household_Chaos2<-scale(da37871.0003$HHCHAOSA2)
df2<-data.frame(Perceived1, Parenting1, Economic1, Household_Chaos1, Perceived2, Parenting2, Economic2, Household_Chaos2)
Correlation Table for Variables at Time 1 & Time 2:
library(apaTables)
apa.cor.table(
df2,
filename = '/Users/allisonlanger/Desktop/assignment7tablefinal.doc',
table.number = 1,
show.conf.interval = TRUE,
show.sig.stars = TRUE,
landscape = TRUE
)
##
##
## Table 1
##
## Means, standard deviations, and correlations with confidence intervals
##
##
## Variable M SD 1 2 3
## 1. Perceived1 0.00 1.00
##
## 2. Parenting1 -0.00 1.00 .37**
## [.31, .42]
##
## 3. Economic1 -0.00 1.00 -.30** -.11**
## [-.35, -.24] [-.18, -.05]
##
## 4. Household_Chaos1 -0.00 1.00 .48** .33** -.13**
## [.42, .52] [.27, .38] [-.19, -.06]
##
## 5. Perceived2 -0.00 1.00 .54** .34** -.20**
## [.50, .59] [.28, .40] [-.26, -.13]
##
## 6. Parenting2 -0.00 1.00 .31** .58** -.11**
## [.25, .37] [.54, .63] [-.18, -.05]
##
## 7. Economic2 -0.00 1.00 -.31** -.15** .51**
## [-.37, -.25] [-.22, -.09] [.46, .55]
##
## 8. Household_Chaos2 -0.00 1.00 .36** .25** -.05
## [.30, .41] [.19, .31] [-.11, .02]
##
## 4 5 6 7
##
##
##
##
##
##
##
##
##
##
##
## .35**
## [.29, .41]
##
## .30** .45**
## [.23, .35] [.40, .50]
##
## -.20** -.30** -.20**
## [-.26, -.13] [-.36, -.24] [-.26, -.13]
##
## .63** .41** .34** -.16**
## [.59, .67] [.36, .47] [.28, .39] [-.23, -.10]
##
##
## Note. M and SD are used to represent mean and standard deviation, respectively.
## Values in square brackets indicate the 95% confidence interval.
## The confidence interval is a plausible range of population correlations
## that could have caused the sample correlation (Cumming, 2014).
## * indicates p < .05. ** indicates p < .01.
##
T1_m <- 'Stress_T1 =~ NA*Perceived1 + Parenting1 + Economic1 + Household_Chaos1
Stress_T1 ~~ 1*Stress_T1'
T2_m <- 'Stress_T2 =~ NA*Perceived2 + Parenting2 + Economic2 + Household_Chaos2
Stress_T2 ~~ 1*Stress_T2'
#latent factors covary
Config_m <- 'Stress_T1 =~ NA*Perceived1 + Parenting1 + Economic1 + Household_Chaos1
Stress_T2 =~ NA*Perceived2 + Parenting2 + Economic2 + Household_Chaos2
Stress_T1 ~~ 1*Stress_T1
Stress_T2 ~~ 1*Stress_T2'
#Metric model - fix loadings (no parameters labels across groups, just the one constraint)
Metric_m <- '
Stress_T1 =~ NA*Perceived1 + l1*Perceived1 + l2*Parenting1 + l3*Economic1 + l4*Household_Chaos1
Stress_T2 =~ NA*Perceived2 + l1*Perceived2 + l2*Parenting2 + l3*Economic2 + l4*Household_Chaos2
Stress_T1 ~~ 1*Stress_T1
Stress_T2 ~~ NA*Stress_T2'
#Scalar model - fix loadings and intercepts
Scalar_m <- '
Stress_T1 =~ NA*Perceived1 + l1*Perceived1 + l2*Parenting1 + l3*Economic1 + 43*Household_Chaos1
Stress_T2 =~ NA*Perceived2 + l1*Perceived2 + l2*Parenting2 + l3*Economic2 + l4*Household_Chaos2
Perceived1 ~ i1*1
Parenting1 ~ i2*1
Economic1 ~ i3*1
Household_Chaos1 ~ i4*1
Perceived2 ~ i1*1
Parenting2 ~ i2*1
Economic2 ~ i3*1
Household_Chaos2 ~ i4*1
Stress_T1 ~~ 1*Stress_T1
Stress_T2 ~~ NA*Stress_T2
Stress_T2 ~ NA*1'
#constraints on residual variances
Strict_m <-
'Stress_T1 =~ NA*Perceived1 + l1*Perceived1 + l2*Parenting1 + l3*Economic1 + l4*Household_Chaos1
Stress_T2 =~ NA*Perceived2 + l1*Perceived2+ l2*Parenting2 + l3*Economic2 + l4*Household_Chaos2
Perceived1 ~ i1*1
Parenting1 ~ i2*1
Economic1 ~ i3*1
Household_Chaos1 ~ i4*1
Perceived2 ~ i1*1
Parenting2 ~ i2*1
Economic2 ~ i3*1
Household_Chaos2 ~ i4*1
Stress_T1 ~~ 1*Stress_T1
Stress_T2 ~~ NA*Stress_T2
Stress_T2 ~ NA*1
Perceived1 ~ rv1*Perceived1
Parenting1 ~~ rv2*Parenting1
Economic1 ~~ rv3*Economic1
Household_Chaos1 ~~ rv4*Household_Chaos1
Perceived2 ~ rv1*Perceived2
Parenting2 ~~ rv2*Parenting2
Economic2 ~~ rv3*Economic2
Household_Chaos2 ~~ rv4*Household_Chaos2'
T1_m_res <- sem(T1_m, df2)
trim_fit(T1_m_res, T1_m_res_fit)
T1_m_res_fit
parameterestimates(T1_m_res, ci = F, standardized = T)
T2_m_res <- sem(T2_m, df2)
trim_fit(T2_m_res, T2_m_res_fit)
T2_m_res_fit
parameterestimates(T2_m_res, ci = F, standardized = T)
Config_m_res <- sem(Config_m, df2)
trim_fit(Config_m_res, Config_m_res_fit)
Config_m_res_fit
parameterestimates(Config_m_res, ci = F, standardized = T)
Metric_m_res <- sem(Metric_m, df2)
trim_fit(Metric_m_res, Metric_m_res_fit)
Metric_m_res_fit
parameterestimates(Metric_m_res, ci = F, standardized = T)
Scalar_m_res <- sem(Scalar_m, df2)
trim_fit(Scalar_m_res, Scalar_m_res_fit)
Scalar_m_res_fit
parameterestimates(Scalar_m_res, ci = F, standardized = T)
Strict_m_res <- sem(Strict_m, df2)
trim_fit(Strict_m_res, Strict_m_res_fit)
Strict_m_res_fit
parameterestimates(Strict_m_res, ci = F, standardized = T)
attachment_results <- data.frame(t(cbind(T1_m_res_fit,
T2_m_res_fit[2],
Config_m_res_fit[2],
Metric_m_res_fit[2],
Scalar_m_res_fit[2],
Strict_m_res_fit[2])))
attachment_results <- attachment_results[-1, ]
str(attachment_results)
attachment_results$Model <- c("T1", "T2", "Configural Model", "Metric Model", "Scalar Model", "Strict Model")
attachment_results <- attachment_results[c(14, 1:13)]
attachment_results$RMSEA_CI <- paste0(attachment_results$rmsea, " (",as.numeric(as.character(attachment_results$rmsea.ci.lower)),"-",attachment_results$rmsea.ci.upper,")")
sm_attachment_results <- attachment_results[c(1, 3, 4, 5, 6, 7, 15)]
Fit Statistics for Configural, Metric, Scalar, & Strict Invariance for Stress at Time 1 and Time 2
sm_attachment_results
Yikes! It appears there is a failure to support invariance, which suggests interpretations of mean-level differences across time may not be valid. This is indicated by the high jumps in chi-square values as the models become more restrictive, as well as the decrease in values for CFI and TLI. RMSEA values are only in an acceptable range for the basic first-order factor model with no restrictions.
So what’s happening? Beyond the influence of the latent factor, it’s possible the indicators could be associated for other reasons, which could be elucidated further by examining indicator residuals. It could also be possible that the time frame in which the items of each indicator variable were asked caused a difference in how they were interpreted. These two data waves were collected pre, post, and during covid lockdowns. It’s possible that certain measures (household chaos? economic pressures?) were conceptualized differently across these hugely different contexts.
Let’s return to our research questions now that we have answers:
| Research Questions | What did our models tell us? |
|---|---|
| Is the latent construct “stress” equivalent across experimental groups?(Model 3.1) | Yes! Well, yes for Time 2 data. We need to check other data waves too. |
| Is the association between stress and child developmental outcomes the same between the experimental groups? (Model 3.2) | Yes. We retained the null hypothesis. |
| Is the latent construct “stress” equivalent across experimental time? (Model 3.3) | No! We need to look into why… |
These analyses suggest that researchers who are using the Baby’s First Years data set to examine how stress may impact children’s development should…
Test for measurement invariance of latent constructs across the two experimental groups
Test for measurement invariance of latent constructs across the data collection waves
…before making cross-group or cross-time mean comparisons.
Thanks for scrolling!
if (FALSE) {
# upload a document
result <- rpubsUpload("Temple University SEM Spring 2024", "SEMFinalPresentationFinal.html")
if (!is.null(result$continueUrl))
browseURL(result$continueUrl)
else
stop(result$error)
# update the same document with a new title
updateResult <- rpubsUpload("Temple University SEM 2024", "SEMFinalPresentationFinal.html",
id = result$id)
}