Factor Analysis
Confirmatory Factor Analysis (CFA)
CFA tests whether items fit a factor structure you specified in advance.
Confirmatory factor analysis takes a set of items and a structure named in advance, which items reflect which underlying traits, and tests how well that structure reproduces how the items go together.
Does test anxiety split into worry and emotionality, as the scale's authors intended? Three hundred students rated eight test-anxiety statements from 1 to 5; four were written to capture worry and four the body's reactions (emotionality).
- Tests
- How well a pre-specified factor structure fits the items' covariances
- Observations
- Independent
- Parametric
- Yes
- Headline Statistic
- \text{RMSEA}
- Fit Index
- RMSEA
- Groups
- 1
The Basics
When to Use It
Use it when you have several measures of each trait (usually questionnaire items), and a structure fixed before you look at the data: how many factors there are, which items reflect which factor, and which factors may correlate. CFA tests whether that structure reproduces how the items go together, and estimates how strongly each item reflects its factor (Jöreskog, 1969). For example:
- Does test anxiety split into worry and emotionality, as the scale's authors intended?
- Do the nine items of a depression screener reflect one factor, or separate somatic and mood factors?
- Does a burnout measure keep the same three-factor structure in a new sample of nurses?
Vocabulary You Need
Definition Latent Variable
A variable that isn't measured directly, such as test anxiety, depression or working-memory capacity, but is inferred from several observed measures that it's thought to drive. In a factor model it's the factor: whatever the items share. It has no units of its own, so a model has to give it a scale, either by fixing its variance at 1 or by tying it to one item's units. Because it's estimated from what the items share, a latent variable is free of each item's own noise, which is why correlations between latent variables come out stronger than correlations between the observed scores. Glossary →
Definition Factor Loading (\lambda)
How strongly one observed variable, such as a questionnaire item, reflects a factor: an unobserved trait that a set of variables is assumed to share. A standardized loading puts everything in standard deviations; when each item reflects only one factor, it is the item's correlation with that factor, and its square is the share of the item's variance the factor accounts for. An unstandardized loading is in the item's own units: how much the item's score rises, on average, for each one-unit rise in the factor: one standard deviation when the factor's variance is fixed at 1, or one point of the marker item when that item's loading is fixed at 1. Loadings come from a factor analysis, so they depend on the model, the estimation method and which items are analyzed together. Glossary →
Definition Model Fit
How well a model reproduces the data it was fitted to. In factor analysis and structural equation modeling the data are the items' variances and covariances, and the model predicts a matrix of them from its loadings and factor correlations; fit is how close the two matrices are. It's judged with the χ² test of exact fit and with approximate fit indexes that describe the size of the misfit, such as RMSEA, CFI and SRMR, plus the individual residuals. Good fit shows the model is consistent with the data, not that it's the right model: other models, never tested, may fit as well. Glossary →
Definition Modification Index (\text{MI})
For each parameter a model fixes, usually at zero (a cross-loading, a residual correlation), an estimate of how much χ² would drop if that one parameter were freed, as a χ² with 1 degree of freedom. Programs list them from largest to smallest, along with the expected parameter change, how big the freed parameter would be. A large index points to where the model misses most. It's a clue, not a verdict: freeing parameters because the data suggested them capitalizes on chance, and changes made that way often don't hold up in new samples. Glossary →
Assumptions
1. Correctly Specified Model Your model includes the paths and correlations that really matter.
- What It Means
- The model's pattern is right in the ways that count: every path, loading or covariance that matters is included, and every one the model fixes at zero really is near zero. In a factor model, the items load on the factors you assigned them to and any residuals that share something (similar wording, a shared method) are allowed to correlate; in a path model, the arrows left out of the diagram really are absent. Every estimate, and every fit index, is conditional on that pattern. An omitted path, cross-loading or residual correlation doesn't just cost fit; the model makes up for it by bending the parameters it does have.
- Applies To
- Covariance structure
- How to Check
- Specify the model from theory (and, for a factor model, item content) before seeing the data. Then look beyond the overall fit: the residual correlations for pairs the model misses, the modification indices for where it misses most, and paths, loadings or factor correlations that look implausible. Compare the model with plausible alternatives specified in advance. Fit statistics can show a model is wrong; they can't show it's right.
- How Much It Matters
- A lot, for everything built on the model. A missing cross-loading (an item that also reflects a second factor) or residual correlation (two items sharing something the factors don't, such as similar wording) shows up as misfit, and it also bends the estimates: the leftover covariance gets pushed into other loadings and into the factor correlation. Small omissions, like one modest cross-loading, change the estimates a little; a wrong number of factors changes them a lot.
If It's Violated
- Alternative Test Exploratory Factor Analysis (EFA). If the structure is genuinely uncertain, explore it first, ideally in a separate sample, and confirm it in another.
- Design Change Specify competing structures before seeing the data (one factor, two factors, two factors plus a method factor) and compare them, rather than repairing one model.
- Proceed with Caveat If you free a parameter the data suggested, say so, report both models, and treat the change as a hypothesis for the next sample.
2. Multivariate Normality The variables are roughly bell-shaped together (within each group, when there are groups), or the sample is big enough that it matters less.
- What It Means
- The variables jointly follow a multivariate normal distribution: each is roughly normal, and every weighted combination of them is too, so the scatterplot of any two is an oval cloud. When the design has groups, as in MANOVA, it applies within each group; in a multivariate regression, to the outcomes' residuals; in factor analysis and SEM, it applies to the observed variables that maximum likelihood is fitted to (for outcomes, after allowing for any predictors the model takes as given). P-values and standard errors rely on it; estimates are usually less affected, and robust standard errors or a scaled test statistic can take its place.
- Applies To
- Joint distribution
- How to Check
- Look at a histogram of each variable (or each outcome's residuals, in a model with predictors) and a scatterplot of each pair, within each group if there are groups, for strong skew, curves or stray points. Multivariate outliers show up as large squared Mahalanobis distances from the means (each group's own, if there are groups). Don't rely on a normality test as a gatekeeper: with small samples it misses real problems, and with large ones it flags trivial ones.
- How Much It Matters
- Some, depending on the items. ML estimates stay roughly right, but skewed or heavy-tailed items make χ² too large and standard errors too small. Rating items with 5 or more roughly symmetric categories are usually fine to treat as continuous with robust ML (Rhemtulla et al., 2012); items with fewer categories, or piled up at one end, aren't. Our 5-point items are roughly symmetric, and refitting with robust ML or as ordered categories leads to the same conclusions.
If It's Violated
- Robust Estimation Use robust ML (MLR in lavaan and Mplus), which rescales χ² and the fit indexes and corrects the standard errors for non-normality.
- Alternative Test For items with fewer than 5 categories or lopsided answers, fit the model to polychoric correlations with a categorical least-squares estimator (WLSMV in lavaan and Mplus).
3. Independent Observations The people (or other units) in your study don't influence each other.
- What It Means
- Each person's data comes from a separate, unconnected source, so knowing one person's scores tells you nothing about anyone else's. In designs that measure people more than once, their own scores are meant to be related; it's different people who must be independent. Methods built for clustered data, such as multilevel models and GEE, also allow members of the same group to be related, as long as the analysis names the grouping; then it's the groups that must be independent.
- Applies To
- Observations
- How to Check
- By design, not by a test. Did each person contribute their own data, with no clusters such as roommates, couples, therapy groups or classrooms whose members influence each other? If there are clusters, does the analysis model every one of them?
- How Much It Matters
- Some. If the students had come from a handful of classrooms, they would share teachers and exams, so the model's standard errors are too small and χ² too large, and the factor structure can blend what happens within classrooms with differences between them.
If It's Violated
- Alternative Test Multilevel SEM. A multilevel factor model estimates the structure within and between clusters.
- Correction Keep the single-level model but correct the standard errors and χ² for clustering.
Limitations
This Is Not Your Test If…
- You don't yet know how many factors there are Exploratory Factor Analysis (EFA) →
- Your items measure one trait and you want its reliability McDonald's Omega →
- You're testing paths between measured variables, not factors Path Analysis →
- Your people are nested in groups, like classrooms Multilevel SEM →
- Your items are scored right or wrong and you want each item's properties Item Response Theory (IRT) →
What It Doesn't Tell You
- Whether the structure is the right one. Good fit says the two-factor model is consistent with the data, not that it's true. Models nobody tested, such as one with a general anxiety factor above worry and emotionality, might fit as well.
- Whether the factors are what their names say. The model says four items share something; calling it “worry” is an interpretation. Whether it predicts what worry should predict, and not what it shouldn't, is a question of validity.
- Whether the items reflect the trait. CFA assumes each item is an effect of its factor, so items on a factor are interchangeable and correlate only through it. If the items instead make up the construct, as income, education and occupation make up socioeconomic status, a factor model is the wrong tool.
- Why. Nothing here says what causes worry or emotionality, or how one affects the other. The factor correlation of .58 is a correlation.
- Who else. The structure holds for students like these 300. Whether it holds equally for other groups, such as men and women or students tested in another language, needs a test of measurement invariance.
- That a modified model would replicate. Freeing the panicky cross-loading improved fit in these data, which is exactly what data-driven changes do. Only a new sample can say whether it's real.
Common Mistakes
-
Don'tReject a model only because its χ² test is significant.
InsteadRead χ² with RMSEA, CFI, SRMR and the residuals. With a few hundred people, even trivial misfit is often significant.
-
Don'tTreat Hu and Bentler's cutoffs as pass marks.
InsteadReport the indexes with RMSEA's interval and judge the model's misfit, not whether it cleared .95 or .06.
-
Don'tReport fit without the loadings and factor correlations.
InsteadGive the full measurement model. A well-fitting model can still have weak loadings or factors too correlated to tell apart.
-
Don'tTake good fit as proof that the model is right.
InsteadSay the data are consistent with it, and name plausible alternative models you tested or couldn't rule out.
-
Don'tFree the parameter with the largest modification index because it improves fit.
InsteadFree it only if it makes sense for the items, say it was data-driven, and test it in a new sample.
-
Don'tReport RMSEA without its confidence interval.
InsteadGive the 90% interval too. With a few hundred people it's often wide enough to span “close” and “mediocre”.
-
Don'tRead a bigger χ² as a stronger effect.
InsteadReport an effect size suited to the design, such as Cramér's V, an odds ratio or a model's coefficients. χ² grows with sample size even when the effect doesn't.
-
Don'tRead a p-value as the chance that the null hypothesis is true.
InsteadRead it as: if the null were true, how often data at least this extreme would turn up.
Result Decoder
Significance and Fit Index Guidelines
Benchmarks Significance (p Value)
-
p < .001
Very strong evidence against the null hypothesis.
-
.001 ≤ p < .01
Strong evidence against the null hypothesis.
-
.01 ≤ p < .05
Statistically significant at α = .05. Moderate evidence against the null hypothesis.
-
.05 ≤ p < .10
Not significant. Weak evidence; sometimes called a “trend.” Treat it as inconclusive.
-
p ≥ .10
Not significant. Little evidence against the null, which is not the same as evidence of no effect.
These evidence labels are one common convention for describing p in graded language rather than as significant or not; the boundaries are rough guides, not rules, and other authors draw them differently. Muff et al. (2022) argue for this kind of language of evidence over a single cutoff. α is chosen before the analysis, and p never says how big or important an effect is.
Benchmarks Fit Index (RMSEA)
-
RMSEA ≤ .05
Close Fit
Little misfit per degree of freedom. Programs such as lavaan test whether the population value could be .05 or less (the test of close fit).
-
.05 < RMSEA ≤ .08
Reasonable
Some misfit, usually treated as acceptable. Hu and Bentler's (1999) cutoff of about .06 sits in this band.
-
.08 < RMSEA ≤ .10
Mediocre
Noticeable misfit. Look at the residuals and the modification indices for where the model goes wrong.
-
RMSEA > .10
Poor
The model misses an important part of how the measures go together.
RMSEA measures misfit, so smaller is better. The .05, .08 and .10 lines are long-standing conventions; Hu and Bentler (1999) suggested a cutoff close to .06, paired with SRMR. All of them are rules of thumb from particular simulations, not tests (Marsh et al., 2004). Read RMSEA with its 90% interval, alongside CFI, SRMR and the residuals.
How It's Reported
Equation Form
λ* = .59 to .75, φ = .58, 95% CI [.47, .69]; CFI = .975, RMSEA = .053, 90% CI [.024, .081], SRMR = .042; χ²(19) = 35.25, p = .013
Table 1. Fit of the One- and Two-Factor Models
| \chi^2 | df | p | CFI | RMSEA | RMSEA 90% CI | SRMR | |
|---|---|---|---|---|---|---|---|
| One factor (all eight items) | 154.48 | 20 | < .001 | .791 | .150 | [.128, .172] | .093 |
| Two factors: worry and emotionality | 35.25 | 19 | .013 | .975 | .053 | [.024, .081] | .042 |
| Two factors, plus panicky on worry (post hoc) | 23.48 | 18 | .173 | .991 | .032 | [.000, .064] | .029 |
Note. N = 300. Maximum likelihood estimation. The two-factor model is the one specified in advance; the last row frees the cross-loading the largest modification index pointed to, and is post hoc.
Table 2. Loadings of the Two-Factor Model
| \lambda | SE | \lambda^* | \theta | R^2 | |
|---|---|---|---|---|---|
| Worry | |||||
| 1. I think about failing while I'm taking a test | 0.88 | 0.07 | .73 | 0.69 | .53 |
| 2. I doubt I've prepared enough for exams | 0.79 | 0.06 | .71 | 0.63 | .50 |
| 3. I compare myself with how well others will do | 0.63 | 0.06 | .59 | 0.74 | .35 |
| 4. I worry about what a bad grade will mean | 0.77 | 0.07 | .66 | 0.76 | .44 |
| Emotionality | |||||
| 5. My heart races during exams | 0.89 | 0.07 | .75 | 0.62 | .56 |
| 6. My stomach gets upset before a test | 0.64 | 0.06 | .59 | 0.76 | .35 |
| 7. My hands shake during tests | 0.69 | 0.07 | .59 | 0.90 | .35 |
| 8. I feel panicky when a test begins | 0.83 | 0.06 | .72 | 0.62 | .52 |
Note. N = 300. λ = loading in item points (each factor's variance fixed at 1), with its standard error; λ* = standardized loading; θ = residual (unique) variance; R² = the share of the item's variance it shares with its factor (λ*²). Factor correlation φ = .58, 95% CI [.47, .69].
Full Text (APA Style)
A confirmatory factor analysis (maximum likelihood in lavaan 0.7-2, factor variances fixed at 1; no missing data) tested the planned two-factor structure of the eight test-anxiety items, with four worry items and four emotionality items and the two factors free to correlate. Every item loaded clearly on its own factor, with standardized loadings from .59 to .75 (Table 2), and worry and emotionality were related but distinct, φ = .58, 95% CI [.47, .69]. The model fit acceptably by conventional standards, CFI = .975, RMSEA = .053, 90% CI [.024, .081], SRMR = .042, although the test of exact fit was significant, χ²(19) = 35.25, p = .013. A one-factor model fit much worse (Table 1). The largest modification index (12.58) suggested that “I feel panicky when a test begins” also reflects worry; we report that model only as a post hoc exploration.
- Say how the model was specified before the data (which items on which factor, which factors correlate) and how it was identified.
- Name the estimator and software, and report χ² with its df and p, RMSEA with its 90% CI, CFI and SRMR.
- Give every standardized loading and the factor correlations, not just the fit.
- Report any change prompted by modification indices as post hoc, with both models' fit.
Reading Each Piece
The equation form above, piece by piece, in this order: how big, how certain, then how surprising.
| λ* = .59 to .75 | Every item reflects its own factor clearly: from .59 (“I compare myself with how well others will do”, “My hands shake”) to .75 (“My heart races”). Between 35% and 56% of each item's variance is shared with its factor; the rest is unique to the item. |
| φ = .58 | Students who worry more also tend to react more physically, but the two traits are far from identical: they share about a third of their variance (.58² = .34). |
| 95% CI [.47, .69] | The correlation between the traits in the population is plausibly anywhere from .47 to .69. Even the top of that range is well short of 1, and below the .85 threshold often used for correlations corrected for measurement error, so the two factors are distinct enough to score separately. |
| CFI = .975 | The model removes about 97% of the misfit of a model in which the items are unrelated. That clears Hu and Bentler's .95. |
| RMSEA = .053 | A small amount of misfit per degree of freedom, between the .05 “close fit” and .08 “reasonable fit” conventions and under Hu and Bentler's .06. Benchmarks ↑ |
| 90% CI [.024, .081] | The population RMSEA is plausibly anywhere from .024, very close fit, to .081, just past the “reasonable” line. With 300 students and 19 df, the data can't rule out either. |
| SRMR = .042 | The model's predicted correlations are typically off by about .04 to .05 (SRMR also averages in the 8 variances, which the model reproduces almost exactly), well within Hu and Bentler's .08. The biggest single misses are about .09: the model predicts too little overlap between the worry items and “I feel panicky”, and too much between them and “My hands shake”. |
| χ²(19) = 35.25 | If the model were exactly right, χ² would average about its 19 degrees of freedom. At 35.25 it's nearly twice that, so the model misses a little more than chance alone would explain. |
| p = .013 | If the two-factor model fit the population exactly, a gap at least this big would turn up about 1.3% of the time (1 in 77). So it isn't exactly right, which is true of almost every model. How far off it is is what RMSEA, CFI and SRMR describe. Benchmarks ↑ |
| MI = 12.58 | Feeling panicky may be partly a thought (“I'm going to fail”) as well as a bodily reaction, which would make sense. But the change was suggested by these data, so it belongs in the next study's pre-registered model, not quietly in this one. |
Calculations
The Running Example
The test-anxiety structure study: 300 students, eight items rated 1 to 5, and a two-factor model, worry and emotionality, specified before the data were collected.
- Null Hypothesis:
- \Sigma = \Sigma_{\text{model}}
- Alternative:
- \Sigma \neq \Sigma_{\text{model}}
\Sigma is the items' covariance matrix in the population of students like these: every item's variance and every pair's covariance. \Sigma_{\text{model}} is the matrix the two-factor model predicts from its 17 parameters (step 4 shows how). The null says the model reproduces the population covariances exactly; the fit indexes then ask how far from exact it is.
What Your Data Should Look Like
One row per person and one numeric column per item, with any reverse-worded items reverse-scored so that higher always means more of the trait. Rating items with five or more categories are usually treated as numbers. The model itself, which items go with which factor, lives in the analysis syntax, not in the data file. Aim for at least three items per factor and a few hundred people.
| student | think_failing | doubt_prepared | compare_others | worry_grades | heart_races | upset_stomach | hands_shake | panicky |
|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 1 | 3 | 3 | 4 | 3 | 3 |
| 2 | 1 | 4 | 3 | 3 | 1 | 2 | 1 | 2 |
| 3 | 2 | 3 | 2 | 3 | 1 | 4 | 2 | 2 |
| 4 | 4 | 5 | 4 | 4 | 4 | 5 | 4 | 3 |
| 5 | 3 | 3 | 2 | 2 | 2 | 4 | 2 | 2 |
| 6 | 3 | 3 | 3 | 2 | 2 | 2 | 2 | 3 |
| … 294 more rows | ||||||||
| Item | M | SD | Item–Total r |
|---|---|---|---|
| Worry | |||
| Think Failing | 3.02 | 1.21 | .61 |
| Doubt Prepared | 3.23 | 1.13 | .60 |
| Compare Others | 3.00 | 1.07 | .51 |
| Worry Grades | 2.92 | 1.16 | .55 |
| Total | 12.16 | 3.51 | |
| Emotionality | |||
| Heart Races | 2.73 | 1.19 | .62 |
| Upset Stomach | 2.81 | 1.08 | .52 |
| Hands Shake | 2.75 | 1.17 | .53 |
| Panicky | 2.92 | 1.14 | .56 |
| Total | 11.21 | 3.51 | |
N = 300. Each construct has its own total, adding its own items; each item's r is with the total of the other items in its construct (the corrected item–total correlation).
Simulated data.
Napkin Math
The whole test, one idea at a time. Under each heading, a line says why the step matters, and the green note reads the equation in plain English with the test-anxiety structure study's numbers. Collapse all to read just the headings: they tell the whole story on their own.
-
1. Count What the Model Has to Explain
The model says two items go together only because they reflect the same factor, or two correlated factors. From that it predicts every variance and covariance among the items, and it's judged on how closely those predictions match the observed ones. So first count them, then count the parameters the model uses. Each factor also needs a scale: we fix each factor's variance at 1, which lets all eight loadings be estimated. Fixing one loading per factor at 1 instead (a marker item) gives the same fit.
df = \dfrac{p(p + 1)}{2} - q =\dfrac{8 \times 9}{2} - 17 = 36 - 17= 19
p(p + 1)/2 = 36
q = 17
p is our 8 items (here p counts items; it isn't a p-value), which have 8 variances and 28 covariances, 36 in all. q is the model's 17 free parameters: 8 loadings, 8 residual variances and 1 factor correlation. 36 − 17 = 19 pieces of information the model could get wrong.
-
2. Estimate How Strongly Each Item Reflects Its Factor
Maximum likelihood finds the loadings, residual variances and factor correlation that bring the model's predicted covariances as close as possible to the observed ones. Standardizing a loading puts it on a correlation scale; Table 2 lists all eight.
\lambda^*_i = \dfrac{\lambda_i \sqrt{\phi_{ff}}}{\sqrt{\sigma_{ii}}} =\dfrac{0.891 \times 1}{\sqrt{1.419}} = \dfrac{0.891}{1.191}= .75
R^2_5 = .56
\lambda_5 is item 5's loading, 0.891 points on the 1-to-5 scale per standard deviation of emotionality, \phi_{ff} is the factor's variance, fixed at 1 (the φ with no subscript in step 3 is the correlation between the two factors), and \sigma_{55} is the item's variance, 1.419. 0.891 ÷ 1.191 = .75, so .75² = 56% of the variance in “My heart races” is shared with the emotionality factor.
-
3. Estimate How Closely Worry and Emotionality Are Related
The model lets the two factors correlate. Their correlation is estimated from what the items share, so each item's own noise is left out, and its standard error gives a range of plausible values.
\hat{\phi} \pm 1.96 \times SE_{\phi} =0.582 \pm 1.96 \times 0.0562= .58
95\%\ \text{CI} = [.47, .69]
\hat{\phi} is the estimated correlation between worry and emotionality, .58, and SE_{\phi} its standard error, .056. 1.96 × .056 = .11, so the range is .58 ± .11, from .47 to .69.
-
4. Turn the Estimates into Predicted Correlations
The model says two items correlate only through the factors behind them. Two items on the same factor should correlate by the product of their standardized loadings; two items on different factors, by that product times the factor correlation. Every prediction the model makes comes from this, and the gap between an observed and a predicted correlation is that pair's residual.
\hat{r}_{ij} = \lambda^*_i \lambda^*_j \ \text{(same factor)}, \qquad \hat{r}_{ij} = \lambda^*_i\, \phi\, \lambda^*_j \ \text{(different factors)} =\hat{r}_{18} = 0.730 \times 0.582 \times 0.724\hat{r}_{12} = .51
r_{12} = .48
\hat{r}_{18} = .31
r_{18} = .40
“I think about failing” (item 1, loading .730) and “I doubt I've prepared enough” (item 2, .705) are both on worry, so the model predicts they correlate .730 × .705 = .51; they actually correlate .48. “I feel panicky” (item 8, .724) is on emotionality, so for items 1 and 8 the model predicts .730 × .582 × .724 = .31. They actually correlate .40, a residual of +.09: one of the model's biggest misses, and the one the modification index picks up in step 10.
-
5. Measure How Far the Model's Predictions Miss
With the best estimates in hand, compare the covariance matrix the model predicts with the one observed. The maximum likelihood discrepancy measures the gap, and multiplying by the sample size turns it into a χ² statistic.
\chi^2 = N \times F_{\text{ML}} =300 \times 0.1175= 35.25
F_{\text{ML}} = 0.1175
F_{\text{ML}} is the leftover gap between the observed and predicted covariances after fitting, 0.1175, and N is our 300 students. 300 × 0.1175 = 35.25.
-
6. Ask How Surprising That Gap Would Be If the Model Were Exactly Right
The χ² test asks whether the model could be exactly right in the population. It's a test of perfect fit, and with a few hundred people even small, harmless misfit often makes it significant.
p = P(\chi^2_{df} \ge \chi^2) =P(\chi^2_{19} \ge 35.25)= .013
P(\chi^2_{19} \ge 35.25) is the chance that a χ² with 19 df lands at 35.25 or higher: .013, about 1 in 77.
-
7. Compare the Model with One That Explains Nothing
A baseline model says the items are unrelated. CFI asks what share of the baseline model's misfit beyond chance our model removes.
\text{CFI} = 1 - \dfrac{\max(\chi^2 - df,\, 0)}{\max(\chi^2_{b} - df_{b},\, \chi^2 - df,\, 0)} =1 - \dfrac{35.25 - 19}{670.72 - 28}= .975
\chi^2_b = 670.72
df_b = 28
\chi^2 - df is our model's misfit beyond chance, 35.25 − 19 = 16.25. The baseline model's is \chi^2_b - df_b = 670.72 − 28 = 642.72. 16.25 ÷ 642.72 = .025, and 1 − .025 = .975.
-
8. Measure the Misfit per Degree of Freedom, per Person
RMSEA takes the same misfit beyond chance, spreads it over the degrees of freedom and the sample size, and asks how much misfit there would be in the population. Its interval shows how precisely 300 students pin that down.
\text{RMSEA} = \sqrt{\dfrac{\max(\chi^2 - df,\, 0)}{df \times N}} =\sqrt{\dfrac{35.25 - 19}{19 \times 300}}= .053
90\%\ \text{CI} = [.024, .081]
\chi^2 - df is the 16.25 of misfit beyond chance, df our 19 and N our 300 students. 16.25 ÷ 5,700 = .00285, and √.00285 = .053.
-
9. Look at the Typical Leftover Correlation
SRMR skips the statistical machinery. It puts every gap between an observed and a predicted covariance on a correlation scale and takes their typical size.
\text{SRMR} = \sqrt{\dfrac{\sum_{i \le j} \left(\dfrac{s_{ij} - \hat{\sigma}_{ij}}{\sqrt{s_{ii}\, s_{jj}}}\right)^2}{p(p + 1)/2}} =\sqrt{\dfrac{0.0635}{36}}= .042
Each term is one of the 36 variances and covariances, observed minus predicted, divided by the two items' standard deviations. Their squares add to 0.0635; divided by 36 and square-rooted, that's .042.
-
10. Find Where the Model Misses Most
Overall fit says how much the model misses, not where. A modification index estimates how much χ² would drop if one parameter fixed at zero were freed. Here the candidates are cross-loadings (an item loading on the other factor as well as its own) and correlated residuals (two items sharing something the factors don't); this model fixes all of them at zero. It's a clue to check against the item's content, not an instruction.
\text{MI} \approx \chi^2_{\text{fixed}} - \chi^2_{\text{freed}}= 12.58
\Delta\chi^2 = 35.247 - 23.483 = 11.76
The largest \text{MI} is 12.58, for letting “I feel panicky when a test begins” load on worry as well as emotionality. It is estimated from the fitted model alone. Actually refitting with that loading freed drops \chi^2 from 35.247 to 23.483, by \Delta\chi^2 = 11.76, close to what the index predicted.
Run It in Software
The code below reads the running example's data from data.csv; save it in the same folder you run the code from.
SPSS Amos (version 30, as documented by IBM)
SPSS Statistics itself has no confirmatory factor analysis. IBM's add-on, SPSS Amos, fits it by drawing the model:
- Open the data file in Amos (File → Data Files).
- Draw two latent variables (ovals), worry and emotionality, each with four indicators (rectangles), and name the rectangles after the item columns. Amos adds an error term to each item.
- Draw a two-headed arrow between the two factors so they can correlate. Amos fixes one loading per factor to 1 (the marker item); for factor variances of 1 instead, set each factor's variance to 1 and free that loading.
- In Analysis Properties → Output, tick Standardized estimates and Modification indices, then Calculate Estimates.
Many SPSS users run the model in R (below) or in JASP or jamovi, whose factor-analysis menus run lavaan and give the same numbers.
Reading the Output
Read the standardized loadings and the factor correlation first, then RMSEA with its interval, CFI and SRMR, then χ² with its df and p. The estimates should match the R output below. Fit statistics can differ in the third decimal between programs that multiply F_ML by N (as lavaan does) and those that use N − 1. Amos reports SRMR only through its Standardized RMR plugin.
Output laid out from this page's computed results, not captured from a live run; labels and layout may differ slightly in your version.
R
library(lavaan)
anxiety <- read.csv("data.csv")
model <- '
worry =~ think_failing + doubt_prepared + compare_others + worry_grades
emotionality =~ heart_races + upset_stomach + hands_shake + panicky
'
# std.lv = TRUE fixes each factor's variance at 1 and frees every loading.
# Leave it out to use the first item of each factor as the marker (same fit).
fit <- cfa(model, data = anxiety, std.lv = TRUE)
summary(fit, fit.measures = TRUE, standardized = TRUE)
residuals(fit, type = "cor")$cov # observed minus predicted correlations, the gaps SRMR averages
head(modindices(fit, sort. = TRUE), 3)
# One factor, for comparison
fit1 <- cfa('anxiety =~ think_failing + doubt_prepared + compare_others + worry_grades +
heart_races + upset_stomach + hands_shake + panicky', data = anxiety, std.lv = TRUE)
anova(fit1, fit)
lavaan 0.7-2 ended normally after 13 iterations
Estimator ML
Number of model parameters 17
Number of observations 300
Model Test User Model:
Test statistic 35.247
Degrees of freedom 19
P-value (Chi-square) 0.013
Model Test Baseline Model:
Test statistic 670.721
Degrees of freedom 28
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.975
Tucker-Lewis Index (TLI) 0.963
Root Mean Square Error of Approximation:
RMSEA 0.053
90 Percent confidence interval - lower 0.024
90 Percent confidence interval - upper 0.081
P-value H_0: RMSEA <= 0.050 0.386
P-value H_0: RMSEA >= 0.080 0.054
Standardized Root Mean Square Residual:
SRMR 0.042
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
worry =~
think_failing 0.885 0.069 12.878 0.000 0.885 0.730
doubt_prepared 0.792 0.064 12.356 0.000 0.792 0.705
compare_others 0.629 0.063 9.970 0.000 0.629 0.590
worry_grades 0.770 0.067 11.469 0.000 0.770 0.663
emotionality =~
heart_races 0.891 0.067 13.227 0.000 0.891 0.748
upset_stomach 0.639 0.064 10.006 0.000 0.639 0.592
hands_shake 0.691 0.069 9.968 0.000 0.691 0.590
panicky 0.826 0.065 12.715 0.000 0.826 0.724
Covariances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
worry ~~
emotionality 0.582 0.056 10.351 0.000 0.582 0.582
thnk_f dbt_pr cmpr_t wrry_g hrt_rc upst_s hnds_s pancky
think_failing 0.000
doubt_prepared -0.033 0.000
compare_others 0.016 0.039 0.000
worry_grades 0.021 0.008 -0.049 0.000
heart_races -0.007 0.004 -0.042 -0.051 0.000
upset_stomach -0.030 -0.029 -0.067 0.050 0.007 0.000
hands_shake -0.067 -0.093 -0.067 -0.001 0.017 0.070 0.000
panicky 0.089 0.092 0.050 0.028 -0.002 -0.038 -0.026 0.000
lhs op rhs mi epc sepc.lv sepc.all sepc.nox
23 worry =~ panicky 12.585 0.326 0.326 0.286 0.286
53 upset_stomach ~~ hands_shake 5.239 0.135 0.135 0.163 0.163
22 worry =~ hands_shake 4.931 -0.210 -0.210 -0.179 -0.179
Chi-Squared Difference Test
Df AIC BIC Chisq Chisq diff RMSEA Df diff Pr(>Chisq)
fit 19 6849.1 6912.0 35.247
fit1 20 6966.3 7025.6 154.480 119.23 0.62778 1 < 2.2e-16 ***
Output trimmed, not edited: the variances section, the information criteria and GFI are left out. lavaan is described in Rosseel (2012). Std.all is the standardized loading λ* (Table 2), and with factor variances fixed at 1 the covariance between worry and emotionality is their correlation, .582. The P-value H_0: RMSEA <= 0.050 line is Browne and Cudeck's (1992) test of close fit: .386, so close fit can't be ruled out. CFI is Bentler's (1990) index. TLI (.963) also compares the model with the unrelated-items baseline, but per degree of freedom, so it penalizes extra parameters, and it can go above 1; Hu and Bentler's .95 applies to it too. If you ask for fitMeasures(fit, "fmin"), lavaan gives 0.0587 (printed as 0.059), half the 0.1175 in Napkin Math step 5, because it scales the discrepancy by ½ (χ² = 2N × fmin). The residual matrix is where the step 9 reading comes from: +.089 and +.092 for “I feel panicky” with the first two worry items. In the modification indices, epc is the expected size of the freed loading. For robust ML, add estimator = "MLR"; for items with fewer than five categories, ordered = TRUE. If some answers are missing, add missing = "fiml" to use every student's available answers rather than dropping anyone. If lavaan warns of a negative variance or a standardized loading above 1, that's usually a sign the model is wrong or the sample too small for it; sort that out before reporting the estimates.
Output from a real run: R 4.6.1 (lavaan 0.7-2), run 2026-10-10.
Python
import pandas as pd
import semopy
anxiety = pd.read_csv("data.csv")
desc = """
worry =~ think_failing + doubt_prepared + compare_others + worry_grades
emotionality =~ heart_races + upset_stomach + hands_shake + panicky
"""
model = semopy.Model(desc)
model.fit(anxiety.drop(columns="student"))
print(semopy.calc_stats(model).T.loc[["DoF", "chi2", "chi2 p-value", "CFI", "RMSEA"]])
est = model.inspect(std_est=True) # loadings (marker scaling) and standardized loadings
print(est[["lval", "op", "rval", "Estimate", "Est. Std"]])
Value
DoF 19.000000
chi2 35.246863
chi2 p-value 0.013041
CFI 0.974722
RMSEA 0.053478
lval op rval Estimate Est. Std
0 think_failing ~ worry 1.000000 0.729428
1 doubt_prepared ~ worry 0.895926 0.705144
...
4 heart_races ~ emotionality 1.000000 0.748454
...
9 emotionality ~~ worry 0.458379 0.581536
Output trimmed: the other loadings, the factor variances and the residual variances are left out. semopy 2.3.11 scales each factor by its first item (the marker), so its unstandardized loadings differ from Table 2 while the standardized ones and the fit match. It writes each loading the other way round (item ~ factor). Its RMSEA divides by N − 1 rather than N, giving .0535 instead of .0534, and its optimizer stops a little earlier than lavaan's, so the fourth decimal of some standardized loadings differs. It doesn't report SRMR or modification indices.
Output from a real run: Python 3.9.6 (semopy 2.3.11, pandas 2.3.3), run 2026-10-03.
Impact
Effect Size
In CFA the “size” question has two parts: how big the model's misfit is, and how strong its parameters are. RMSEA, CFI and SRMR describe how big the misfit is rather than how surprising, so unlike χ² they don't grow just because the sample does. They still aren't on one scale across studies: the same misspecification gives a worse RMSEA when the loadings are high and a better one when they're low (McNeish et al., 2018), and the values also depend on the model's size and degrees of freedom.
After allowing for chance, the two-factor model leaves a small amount of misfit per degree of freedom, .053, with a 90% interval from .024 to .081. CFI (.975) and SRMR (.042) tell the same story: the model reproduces how the eight items go together closely, though not perfectly.
The parameters carry the substance. Standardized loadings from .59 to .75 mean each item shares a third to a half of its variance with its factor, and a factor correlation of .58 means worry and emotionality share about a third of their variance: related, but clearly two things, much as Liebert and Morris (1967) described when they first distinguished them. In practical terms, a student's worry score and emotionality score say different things, so reporting them separately is worth it. Fit benchmarks are conventions from simulations of particular models (Hu & Bentler, 1999), and they don't transfer neatly to models of other sizes (Marsh et al., 2004), and RMSEA in particular too often flags a correct model when df and N are small (Kenny et al., 2015). A small misfit that's substantively meaningful, like the panicky item, matters more than which side of .06 RMSEA falls.
Benchmarks RMSEA at a Glance
Statistical Significance
p here answers an unusual question: if the model reproduced the population's covariances exactly, how often would a sample miss them by at least this much? The null hypothesis is the model, so a significant result counts against it, and a non-significant one only says the test couldn't find the misfit.
Our χ²(19) = 35.25 gives p = .013, so the two-factor model isn't exactly right. That's expected: no model of eight questionnaire items is exactly right. If the misfit in the population were the same, 100 students would give a χ² of about 24 on average (p ≈ .18) and 600 students about 51 (p < .001), which is why the decision rests on how big the misfit is, not on p.
χ² is still worth reporting, and it earns its keep when comparing nested models, where one model is the other with some parameters fixed. The one-factor model is the two-factor model with the factor correlation fixed at 1. Its χ²(20) = 154.48 is far worse than the two-factor model's, a gap of 119.23 for that one degree of freedom, which is strong evidence that worry and emotionality aren't a single factor. p is not the probability that the model is right, and a large p in a small sample doesn't show that a model fits well.
Benchmarks p at a Glance
Strength of evidence against the null hypothesis.
Power and Sample Size
Power is the chance that a study detects an effect that's really there. In CFA the usual planning target is the fit itself: enough people that RMSEA's interval is narrow enough to tell a close-fitting model from a poor one (MacCallum et al., 1996). With few items per factor or weak loadings, you also need people for the estimates to be stable at all. To plan for precise loadings or a precise factor correlation, simulate data from the model you expect and count how often the estimates land close enough (Muthén & Muthén, 2002); lavaan's simulateData() generates the data.
Suppose we wanted to be able to rule out poor fit: MacCallum et al.'s test of not-close fit, whose null is that RMSEA is .08 or more, with power to reject it if the population RMSEA is really .05. With our 19 degrees of freedom, 300 students give that test about 60% power; about 460 students would give 80%. Our interval, from .024 to .081, shows the same thing: 300 students couldn't quite rule out RMSEA above .08.
Plan the sample for the model's degrees of freedom and the smallest misfit you'd care about, not by a people-per-item rule. More degrees of freedom need fewer people; small models with few df need many. There's no calculator for this test yet; semTools' findRMSEAsamplesize(rmsea0 = .08, rmseaA = .05, df = 19, power = .80) in R does the calculation.
Other Info
Follow-Up and Post Hoc Tests
None needed. This test answers a single question, so there are no planned contrasts or post hoc tests to run.
References
-
Jöreskog, K. G. (1969). A general approach to confirmatory maximum likelihood factor analysis. Psychometrika, 34(2), 183–202.
https://doi.org/10.1007/BF02289343
“By choosing the fixed parameters appropriately, factors can be defined to have desired properties and make subsequent rotation unnecessary.” (Abstract)
-
Hu, L., & Bentler, P. M. (1999). Cutoff criteria for fit indexes in covariance structure analysis: Conventional criteria versus new alternatives. Structural Equation Modeling, 6(1), 1–55.
https://doi.org/10.1080/10705519909540118
“a cutoff value close to .95 for TLI, BL89, CFI, RNI, and Gamma Hat; a cutoff value close to .90 for Mc; a cutoff value close to .08 for SRMR; and a cutoff value close to .06 for RMSEA are needed before we can conclude that there is a relatively good fit between the hypothesized model and the observed data” (Abstract)
“the ML‐based TLI, Mc, and RMSEA tend to overreject true‐population models at small sample size and thus are less preferable when sample size is small” (Abstract)
-
Marsh, H. W., Hau, K.-T., & Wen, Z. (2004). In search of golden rules: Comment on hypothesis-testing approaches to setting cutoff values for fit indexes and dangers in overgeneralizing Hu and Bentler's (1999) findings. Structural Equation Modeling, 11(3), 320–341.
https://doi.org/10.1207/s15328007sem1103_2
“current practice seems to have incorporated their new guidelines without sufficient attention to the limitations noted by Hu and Bentler (1999)” (Abstract)
-
MacCallum, R. C., Roznowski, M., & Necowitz, L. B. (1992). Model modifications in covariance structure analysis: The problem of capitalization on chance. Psychological Bulletin, 111(3), 490–504.
https://doi.org/10.1037/0033-2909.111.3.490
“These findings lead to skepticism about generalizability of models resulting from data-driven modifications of an initial model.” (Abstract)
“Because this process is data driven, it is inherently susceptible to capitalization on chance characteristics of the data” (Abstract)
“The use of alternative a priori models is recommended as a preferred strategy.” (Abstract)
-
Rhemtulla, M., Brosseau-Liard, P. É., & Savalei, V. (2012). When can categorical variables be treated as continuous? A comparison of robust continuous and categorical SEM estimation methods under suboptimal conditions. Psychological Methods, 17(3), 354–373.
https://doi.org/10.1037/a0029315
“we recommend cat-LS for data sets containing variables with fewer than 5 categories and ML when there are 5 or more categories, sample size is small, and category thresholds are approximately symmetric” (Abstract)
-
Rosseel, Y. (2012). lavaan: An R package for structural equation modeling. Journal of Statistical Software, 48(2), 1–36.
https://doi.org/10.18637/jss.v048.i02
“The model syntax above overrides the default behavior of fixing the first factor loading and estimating the factor variances.” (Section 4.1, p. 11)
- McNeish, D., An, J., & Hancock, G. R. (2018). The thorny relation between measurement quality and fit index cutoffs in latent variable models. Journal of Personality Assessment, 100(1), 43–52. https://doi.org/10.1080/00223891.2017.1281286
- Muthén, L. K., & Muthén, B. O. (2002). How to use a Monte Carlo study to decide on sample size and determine power. Structural Equation Modeling, 9(4), 599–620. https://doi.org/10.1207/S15328007SEM0904_8
-
Liebert, R. M., & Morris, L. W. (1967). Cognitive and emotional components of test anxiety: A distinction and some initial data. Psychological Reports, 20(3), 975–978.
https://doi.org/10.2466/pr0.1967.20.3.975
“This study examined the relationship between two aspects of the TAQ, identified as “worry” and “emotionality,”” (Abstract)
-
Browne, M. W., & Cudeck, R. (1992). Alternative ways of assessing model fit. Sociological Methods & Research, 21(2), 230–258.
https://doi.org/10.1177/0049124192021002005
“Practical difficulties associated with the usual tests of exact fit or [sic] a model are discussed and a test of “close fit” of a model is suggested.” (Abstract)
-
MacCallum, R. C., Browne, M. W., & Sugawara, H. M. (1996). Power analysis and determination of sample size for covariance structure modeling. Psychological Methods, 1(2), 130–149.
https://doi.org/10.1037/1082-989X.1.2.130
“We emphasize the value of confidence intervals for fit indices, and we stress the relationship of confidence intervals to a framework for hypothesis testing.” (Abstract)
-
Kenny, D. A., Kaniskan, B., & McCoach, D. B. (2015). The performance of RMSEA in models with small degrees of freedom. Sociological Methods & Research, 44(3), 486–507.
https://doi.org/10.1177/0049124114543236
“when the cutoff values are used to assess the fit of the properly specified models with small df and small sample size, the RMSEA too often falsely indicates a poor fitting model” (Abstract)
-
Bentler, P. M. (1990). Comparative fit indexes in structural models. Psychological Bulletin, 107(2), 238–246.
https://doi.org/10.1037/0033-2909.107.2.238
“A new coefficient is proposed to summarize the relative reduction in the noncentrality parameters of two nested models.” (Abstract)
-
Muff, S., Nilsen, E. B., O'Hara, R. B., & Nater, C. R. (2022). Rewriting results sections in the language of evidence. Trends in Ecology & Evolution, 37(3), 203–210.
https://doi.org/10.1016/j.tree.2021.10.009
“we suggest language of evidence that allows for a more nuanced approach to communicate scientific findings as a simple and intuitive alternative to statistical significance testing” (Abstract)
Spot an error? Something still confusing? Tell us at support@statsforpsychology.com.