I want your feedback to make the book better for you and other readers. If you find typos, errors, or places where the text may be improved, please let me know. The best ways to provide feedback are by GitHub or hypothes.is annotations.
You can leave a comment at the bottom of the page/chapter, or open an issue or submit a pull request on GitHub: https://github.com/isaactpetersen/Fantasy-Football-Analytics-Textbook
Alternatively, you can leave an annotation using hypothes.is.
To add an annotation, select some text and then click the
symbol on the pop-up menu.
To see the annotations of others, click the
symbol in the upper right-hand corner of the page.
22 Factor Analysis
“All models are wrong, but some are useful.”
— George Box (1979, p. 202)
This chapter provides an overview of factor analysis, which encompasses a range of latent variable modeling approaches, including exploratory and confirmatory factor analysis.
22.1 Getting Started
22.1.1 Load Packages
22.1.2 Load Data
We created the player_stats_weekly.RData and player_stats_seasonal.RData objects in Section 4.4.3.
22.1.3 Prepare Data
22.1.3.1 Season-Averages
Code
player_stats_seasonal_avgPerGame <- player_stats_seasonal |>
mutate(
completionsPerGame = completions / games,
attemptsPerGame = attempts / games,
passing_yardsPerGame = passing_yards / games,
passing_tdsPerGame = passing_tds / games,
passing_interceptionsPerGame = passing_interceptions / games,
sacks_sufferedPerGame = sacks_suffered / games,
sack_yards_lostPerGame = sack_yards_lost / games,
sack_fumblesPerGame = sack_fumbles / games,
sack_fumbles_lostPerGame = sack_fumbles_lost / games,
passing_air_yardsPerGame = passing_air_yards / games,
passing_yards_after_catchPerGame = passing_yards_after_catch / games,
passing_first_downsPerGame = passing_first_downs / games,
#passing_epaPerGame = passing_epa / games,
#passing_cpoePerGame = passing_cpoe / games,
passing_2pt_conversionsPerGame = passing_2pt_conversions / games,
#pacrPerGame = pacr / games
pass_40_ydsPerGame = pass_40_yds / games,
pass_incPerGame = pass_inc / games,
#pass_comp_pctPerGame = pass_comp_pct / games,
fumblesPerGame = fumbles / games
)
nfl_nextGenStats_seasonal <- nfl_nextGenStats_weekly |>
filter(season_type == "REG") |>
select(-any_of(c("week","season_type","player_display_name","player_position","team_abbr","player_first_name","player_last_name","player_jersey_number","player_short_name"))) |>
group_by(player_gsis_id, season) |>
summarise(
across(everything(),
~ mean(.x, na.rm = TRUE)),
.groups = "drop")22.1.3.2 Merge Data
22.1.3.3 Specify Variables
Code
faVars <- c(
"completionsPerGame","attemptsPerGame","passing_yardsPerGame","passing_tdsPerGame",
"passing_air_yardsPerGame","passing_yards_after_catchPerGame","passing_first_downsPerGame",
"avg_completed_air_yards","avg_intended_air_yards","aggressiveness","max_completed_air_distance",
"avg_air_distance","max_air_distance","avg_air_yards_to_sticks","passing_cpoe","pass_comp_pct",
"passer_rating","completion_percentage_above_expectation"
)22.1.3.4 Subset Data
22.1.3.5 Standardize Variables
Standardizing variables is not strictly necessary in factor analysis; however, standardization can be helpful to prevent some variables from having considerably larger variances than others.
22.2 Overview of Factor Analysis
Factor analysis involves the estimation of latent variables. Latent variables are ways of studying and operationalizing theoretical constructs that cannot be directly observed or quantified. Factor analysis is a class of latent variable models that is designated to identify the structure of a measure or set of measures, and ideally, a construct or set of constructs. It aims to identify the optimal latent structure for a group of variables. The goal of factor analysis is to identify simple, parsimonious factors that underlie the “junk” (i.e., scores filled with measurement error) that we observe. That is, similar to principal component analysis, factor analysis can help us perform data reduction—i.e., it can help us reduce down a larger set of variables into a smaller set of factors.
Factor analysis encompasses two general types: confirmatory factor analysis and exploratory factor analysis. Exploratory factor analysis (EFA) is a latent variable modeling approach that is used when the researcher has no a priori hypotheses about how a set of variables is structured. EFA seeks to identify the empirically optimal-fitting model in ways that balance accuracy (i.e., variance accounted for) and parsimony (i.e., simplicity). Confirmatory factor analysis (CFA) is a latent variable modeling approach that is used when a researcher wants to evaluate how well a hypothesized model fits, and the model can be examined in comparison to alternative models. Using a CFA approach, the researcher can pit models representing two theoretical frameworks against each other to see which better accounts for the observed data.
Factor analysis involves observed (manifest) variables and unobserved (latent) factors. Factor analysis assumes that the latent factor influences the manifest variables, and the latent factor therefore reflects the common variance among the variables. A factor model potentially includes factor loadings, residuals (errors or disturbances), intercepts/means, covariances, and regression paths. When depicting a factor analysis model, rectangles represent variables we observe (i.e., manifest variables), and circles represent latent (i.e., unobserved) variables. A regression path indicates a hypothesis that one variable (or factor) influences another, and it is depicted using a single-headed arrow. The standardized regression coefficient (i.e., beta or \(\beta\)) represents the strength of association between the variables or factors. A factor loading is a regression path from a latent factor to an observed (manifest) variable. The standardized factor loading represents the strength of association between the variable and the latent factor, where conceptually, it is intended to reflect the magnitude that the latent factor influences the observed variable. A residual is variance in a variable (or factor) that is unexplained by other variables or factors. A variable’s intercept is the expected value of the variable when the factor(s) (onto which it loads) is equal to zero. A covariance is the unstandardized index of the strength of association between two variables (or factors), and it is depicted with a double-headed arrow.
Because a covariance is unstandardized, its scale depends on the scale of the variables. The covariance between two variables is the average product of their deviations from their respective means, as in Equation 10.1. The covariance of a variable with itself is equivalent to its variance, as in Equation 10.2. By contrast, a correlation is a standardized index of the strength of association between two variables. Because a correlation is standardized (fixed between [−1,1]), its scale does not depend on the scales of the variables. Because a covariance is unstandardized, its scale depends on the scale of the variables. A covariance path between two variables represents omitted shared cause(s) of the variables. For instance, if you depict a covariance path between two variables, it means that there is a shared cause of the two variables that is omitted from the model (for instance, if the common cause is not known or was not assessed).
In factor analysis, the relation between an indicator (\(\text{X}\)) and its underlying latent factor(s) (\(\text{F}\)) can be represented with a regression formula as in Equation 22.1:
\[ X = \lambda \cdot \text{F} + \text{Item Intercept} + \text{Error Term} \tag{22.1}\]
where:
- \(\text{X}\) is the observed value of the indicator
- \(\lambda\) is the factor loading, indicating the strength of the association between the indicator and the latent factor(s)
- \(\text{F}\) is the person’s value on the latent factor(s)
- \(\text{Item Intercept}\) represents the constant term that accounts for the expected value of the indicator when the latent factor(s) are zero
- \(\text{Error Term}\) is the residual, indicating the extent of variance in the indicator that is not explained by the latent factor(s)
When the latent factors are uncorrelated, the (standardized) error term for an indicator is calculated as 1 minus the sum of squared standardized factor loadings for a given item (including cross-loadings). A cross-loading is when a variable loads onto more than one latent factor.
Factor analysis is a powerful technique to help identify the factor structure that underlies a measure or construct. However, given the extensive method variance that influences scores on measure, factor analysis (and principal component analysis) tends to extract method factors. Method factors are factors that are related to the methods being assessed rather than the construct of interest. To better estimate construct factors, it is sometimes necessary to estimate both construct and method factors.
Before pursuing factor analysis, it is helpful to evaluate the extent to which the variables are intercorrelated. If the variables are not correlated (or are correlated only weakly), there is no reason to believe that a common factor influences them, and thus factor analysis would not be useful. So, before conducting a factor analysis, it is important to examine the correlation matrix of the variables to determine whether the variables intercorrelate strongly enough to justify a factor analysis.
22.3 Factor Analysis and Structural Equation Modeling
Factor analysis forms the measurement model component of a structural equation model (SEM). The measurement model is what we settle on as the estimation of each construct before we add the structural component to estimate the relations among latent variables. Basically, in a structural equation model, we add the structural component onto the measurement model. For instance, our measurement model (i.e., based on factor analyis) might be to estimate, from a set of items, three latent factors: usage, aggressiveness, and performance. Our structural model then may examine what processes (e.g., sport drink consumption or sleep) influence these latent factors, how the latent factors influence each other, and what the latent factors influence (e.g., fantasy points). SEM is confirmatory factor analysis with regression paths that specify hypothesized causal relations between the latent variables (the structural component of the model). Exploratory structural equation modeling (ESEM) is a form of SEM that allows for a combination of exploratory factor analysis and confirmatory factor analysis to estimate latent variables and the relations between them.
22.4 Path Diagrams
A key tool when designing a factor analysis or structural equation model is a conceptual depiction of the hypothesized causal processes. A path diagram depicts the hypothesized causal processes that link two or more variables. Path diagrams are an example of a causal diagram and are similar to directed acyclic graphs discussed in Section 13.6. Karch (2025a) provides a tool to create lavaan R syntax from a path analytic diagram: https://lavaangui.org.
In a path analysis diagram, rectangles represent variables we observe, and circles represent latent (i.e., unobserved) variables. Single-headed arrows indicate regression paths, where conceptually, one variable is thought to influence another variable. Double-headed arrows indicate covariance paths, where conceptually, two variables are associated for some unknown reason (i.e., an omitted shared cause).
22.5 Decisions in Factor Analysis
There are five primary decisions to make in factor analysis:
- what variables to include in the model and how to scale them
- method of factor extraction: whether to use exploratory or confirmatory factor analysis
- if using exploratory factor analysis, whether and how to rotate factors
- how many factors to retain (and what variables load onto which factors)
- how to interpret and use the factors
The answer you get can differ highly depending on the decisions you make. Below, we provide guidance for each of these decisions.
22.5.1 1. Variables to Include and their Scaling
The first decision when conducting a factor analysis is which variables to include and the scaling of those variables. What factors you extract can differ widely depending on what variables you include in the analysis. For example, if you include many variables from the same source (e.g., self-report), it is possible that you will extract a factor that represents the common variance among the variables from that source (i.e., the self-reported variables). This would be considered a method factor, which works against the goal of estimating latent factors that represent the constructs of interest (as opposed to the measurement methods used to estimate those constructs).
An additional consideration is the scaling of the variables: whether to use raw variables, standardized variables, or dividing some variables by a constant to make the variables’ variances more similar. Before performing a principal component analysis (PCA), it is generally important to ensure that the variables included in the PCA are on the same scale. PCA seeks to identify components that explain variance in the data, so if the variables are not on the same scale, some variables may contribute considerably more variance than others. A common way of ensuring that variables are on the same scale is to standardize them using, for example, z scores that have a mean of zero and standard deviation of one. By contrast, factor analysis can better accommodate variables that are on different scales.
22.5.2 2. Method of Factor Extraction
22.5.2.1 Exploratory Factor Analysis
Exploratory factor analysis (EFA) is used if you have no a priori hypotheses about the factor structure of the model, but you would like to understand the latent variables represented by your items.
EFA is partly induced from the data. You feed in the data and let the program build the factor model. You can set some parameters going in, including how to extract or rotate the factors. The factors are extracted from the data without specifying the number and pattern of loadings between the items and the latent factors (Bollen, 2002). All cross-loadings are freely estimated.
22.5.2.2 Confirmatory Factor Analysis
Confirmatory factor analysis (CFA) is used to (dis)confirm a priori hypotheses about the factor structure of the model. CFA is a test of the hypothesis. In CFA, you specify the model and ask how well this model represents the data. The researcher specifies the number, meaning, associations, and pattern of free parameters in the factor loading matrix (Bollen, 2002). A key advantage of CFA is the ability to directly compare alternative models (i.e., factor structures), which is valuable for theory testing (Strauss & Smith, 2009). For instance, you could use CFA to test whether the variance in several measures’ scores is best explained with one factor or two factors. In CFA, cross-loadings are not estimated unless the researcher specifies them.
22.5.3 3. Factor Rotation
When using EFA or principal component analysis, an important step is, possibly, to rotate the factors to make them more interpretable and simple, which is the whole goal. To interpret the results of a factor analysis, we examine the factor matrix. The columns refer to the different factors; the rows refer to the different observed variables. The cells in the table are the factor loadings—they are basically the correlation between the variable and the factor. Our goal is to achieve a model with simple structure because it is easily interpretable. Simple structure means that every variable loads perfectly on one and only one factor, as operationalized by a matrix of factor loadings with values of one and zero and nothing else. An example of a factor matrix that follows simple structure is depicted in Figure 22.1.
An example of a factor analysis model that follows simple structure is depicted in Figure 22.2. Each variable loads onto one and only one factor, which makes it easy to interpret the meaning of each factor, because a given factor represents the common variance among the items that load onto it.
However, pure simple structure only occurs in simulations, not in real-life data. In reality, our unrotated factor analysis model might look like the model in Figure 22.3. In this example, the factor analysis model does not show simple structure because the items have cross-loadings—that is, the items load onto more than one factor. The cross-loadings make it difficult to interpret the factors, because all of the items load onto all of the factors, so the factors are not very distinct from each other, which makes it difficult to interpret what the factors mean.
As a result of the challenges of interpretability caused by cross-loadings, factor rotations are often performed. An example of an unrotated factor matrix is in Figure 22.4.
In the example factor matrix in Figure 22.5, the factor analysis is not very helpful—it tells us very little because it did not distinguish between the two factors. The variables have similar loadings on Factor 1 and Factor 2. An example of a unrotated factor solution is in Figure 22.5. In the figure, all of the variables are in the midst of the quadrants—they are not on the factors’ axes. Thus, the factors are not very informative.
As a result, to improve the interpretability of the factor analysis, we can do what is called rotation. Rotation leverages the idea that there are infinite solutions to the factor analysis model that fit equally well. Rotation involves changing the orientation of the factors by changing the axes so that variables end up with very high (close to one or negative one) or very low (close to zero) loadings, so that it is clear which factors include which variables (and which factors each variable most strongly loads onto). That is, rotation rescales the factors and tries to identify the ideal solution (factor) for each variable. Rotation occurs by changing the variables’ loadings while keeping the structure of correlations among the variables intact (Field et al., 2012). Moreover, rotation does not change the variance explained by factors. Rotation searches for simple structure and keeps searching until it finds a minimum (i.e., the closest as possible to simple structure). After rotation, if the rotation was successful for imposing simple structure, each factor will have loadings close to one (or negative one) for some variables and close to zero for other variables. The goal of factor rotation is to achieve simple structure, to help make it easier to interpret the meaning of the factors.
To perform factor rotation, orthogonal rotations are often used. Orthogonal rotations make the rotated factors uncorrelated. An example of a commonly used orthogonal rotation is varimax rotation. Varimax rotation maximizes the sum of the variance of the squared loadings (i.e., so that items have either a very high or very low loading on a factor) and yields axes with a 90-degree angle.
An example of a factor matrix following an orthogonal rotation is depicted in Figure 22.6. An example of a factor solution following an orthogonal rotation is depicted in Figure 22.7.
An example of a factor matrix from SPSS following an orthogonal rotation is depicted in Figure 22.8.
An example of a factor structure from an orthogonal rotation is in Figure 22.9.
Sometimes, however, the two factors and their constituent variables may be correlated. Examples of two correlated factors may be depression and anxiety. When the two factors are correlated in reality, if we make them uncorrelated, this would result in an inaccurate model. Oblique rotation allows for factors to be correlated and yields axes with an angle of less than 90 degrees. However, if the factors have low correlation (e.g., .2 or less), you can likely continue with orthogonal rotation. Nevertheless, just because an oblique rotation allows for correlated factors does not mean that the factors will be correlated, so oblique rotation provides greater flexibility than orthogonal rotation. An example of a factor structure from an oblique rotation is in Figure 22.10. Results from an oblique rotation are more complicated than orthogonal rotation—they provide lots of output and are more complicated to interpret. In addition, oblique rotation might not yield a smooth answer if you have a relatively small sample size.
As an example of rotation based on interpretability, consider the Five-Factor Model of Personality (the Big Five), which goes by the acronym, OCEAN: Openness, Conscientiousness, Extraversion, Agreeableness, and Neuroticism. Although the five factors of personality are somewhat correlated, we can use rotation to ensure they are maximally independent. Upon rotation, extraversion and neuroticism are essentially uncorrelated, as depicted in Figure 22.11. The other pole of extraversion is intraversion and the other pole of neuroticism might be emotional stability or calmness.
Simple structure is achieved when each variable loads highly onto as few factors as possible (i.e., each item has only one significant or primary loading). Oftentimes this is not the case, so we choose our rotation method in order to decide if the factors can be correlated (an oblique rotation) or if the factors will be uncorrelated (an orthogonal rotation). If the factors are not correlated with each other, use an orthogonal rotation. The correlation between an item and a factor is a factor loading, which is simply a way to ask how much a variable is correlated with the underlying factor. However, its interpretation is more complicated if there are correlated factors!
An orthogonal rotation (e.g., varimax) can help with simplicity of interpretation because it seeks to yield simple structure without cross-loadings. Cross-loadings are instances where a variable loads onto multiple factors. My recommendation would always be to use an orthogonal rotation if you have reason to believe that finding simple structure in your data is possible; otherwise, the factors are extremely difficult to interpret—what exactly does a cross-loading even mean? However, you should always try an oblique rotation, too, to see how strongly the factors are correlated. Examples of oblique rotations include oblimin and promax.
22.5.4 4. Determining the Number of Factors to Retain
A goal of factor analysis and principal component analysis is simplification or parsimony, while still explaining as much variance as possible. The hope is that you can have fewer factors that explain the associations between the variables than the number of observed variables. It does not make sense to replace 18 variables with 18 latent factors because that would not result in any simplification. The fewer the number of factors retained, the greater the parsimony, but the greater the amount of information that is discarded (i.e., the less the variance accounted for in the variables). The more factors we retain, the less the amount of information that is discarded (i.e., more variance is accounted for in the variables), but the less the parsimony. But how do you decide on the number of factors?
There are a number of criteria that one can use to help determine how many factors/components to keep:
- Kaiser-Guttman criterion (Kaiser, 1960): factors with eigenvalues greater than zero
- or, for principal component analysis, components with eigenvalues greater than 1
- Cattell’s scree test (Cattell, 1966): the “elbow” (inflection point) in a scree plot minus one; sometimes operationalized with optimal coordinates (OC) or the acceleration factor (AF)
- Parallel analysis: factors that explain more variance than randomly simulated data
- Very simple structure (VSS) criterion: larger is better
- Velicer’s minimum average partial (MAP) test: smaller is better
- Akaike information criterion (AIC): smaller is better
- Bayesian information criterion (BIC): smaller is better
- Sample size-adjusted BIC (SABIC): smaller is better
- Root mean square error of approximation (RMSEA): smaller is better
- Chi-square difference test: smaller is better; a significant test indicates that the more complex model is significantly better fitting than the less complex model
- Standardized root mean square residual (SRMR): smaller is better
- Comparative Fit Index (CFI): larger is better
- Tucker Lewis Index (TLI): larger is better
There is not necessarily a “correct” criterion to use in determining how many factors to keep, so it is generally recommended that researchers use multiple criteria in combination with theory and interpretability.
A scree plot provides lots of information. A scree plot has the factor number on the x-axis and the eigenvalue on the y-axis. The eigenvalue is the variance accounted for by a factor; when using a varimax (orthogonal) rotation, an eigenvalue (or factor variance) is calculated as the sum of squared standardized factor (or component) loadings on that factor. An example of a scree plot is in Figure 22.12.
The total variance is equal to the number of variables you have, so one eigenvalue is approximately one variable’s worth of variance. The first factor accounts for the most variance, the second factor accounts for the second-most variance, and so on. The more factors you add, the less variance is explained by the additional factor.
One criterion for how many factors to keep is the Kaiser-Guttman criterion. According to the Kaiser-Guttman criterion (Kaiser, 1960), you should keep any principal components (from PCA) whose eigenvalue is greater than 1 and factors (from factor analysis) whose eigenvalue is greater than 1. That is, for the sake of simplicity, parsimony, and data reduction, you should take any factors that explain more than a single variable would explain. According to the Kaiser-Guttman criterion, we would keep three components from Figure 22.12 that have eigenvalues greater than 1. The default in SPSS is to retain factors with eigenvalues greater than 1. However, keeping factors whose eigenvalue is greater than 1 is not the most correct rule. If you let SPSS do this, you may get many factors with eigenvalues around 1 (e.g., factors with an eigenvalue ~ 1.0001) that are not adding so much that it is worth the added complexity. The Kaiser-Guttman criterion usually results in keeping too many factors. Factors with small eigenvalues around 1 could reflect error shared across variables. For instance, factors with small eigenvalues could reflect method variance (i.e., method factor), such as a self-report factor that turns up as a factor in factor analysis, but that may be useless to you as a conceptual factor of a construct of interest.
Another criterion is Cattell’s scree test (Cattell, 1966), which involves selecting the number of factors from looking at the scree plot. “Scree” refers to the rubble of stones at the bottom of a mountain. According to Cattell’s scree test, you should keep the factors before the last steep drop in eigenvalues—i.e., the factors before the rubble, where the slope approaches zero. The beginning of the scree (or rubble), where the slope approaches zero, is called the “elbow” of a scree plot. Using Cattell’s scree test, you retain the number of factors that explain the most variance prior to the explained variance drop-off, because, ultimately, you want to include only as many factors in which you gain substantially more by the inclusion of these factors. That is, you would keep the number of factors at the elbow of the scree plot minus one. If the last steep drop occurs from Factor 4 to Factor 5 and the elbow is at Factor 5, we would keep four factors. In Figure 22.12, the last steep drop in eigenvalues occurs from Factor 3 to Factor 4; the elbow of the scree plot occurs at Factor 4. We would keep the number of factors at the elbow minus one. Thus, using Cattell’s scree test, we would keep three factors based on Figure 22.12.
There are more sophisticated ways of using a scree plot, but they usually end up at a similar decision. Examples of more sophisticated tests include parallel analysis and very simple structure (VSS) plots. In a parallel analysis, you examine where the eigenvalues from observed data and random data converge, so you do not retain a factor that explains less variance than would be expected by random chance. Using the VSS criterion, the optimal number of factors to retain is the number of factors that maximizes the VSS criterion (Revelle & Rocklin, 1979). The VSS criterion is evaluated with models in which factor loadings for a given item that are less than the maximum factor loading for that item are suppressed to zero, thus forcing simple structure (i.e., no cross-loadings). The goal is finding a factor structure with interpretability so that factors are clearly distinguishable. Thus, we want to identify the number of factors with the highest VSS criterion.
In general, my recommendation is to use Cattell’s scree test, and then test the factor solutions with plus or minus one factor, in addition to examining model fit. You should never accept factors with eigenvalues less than zero (or components from PCA with eigenvalues less than one), because they are likely to be largely composed of error. If you are using maximum likelihood factor analysis, you can compare the fit of various models with model fit criteria to see which model fits best for its parsimony. A model will always fit better when you add additional parameters or factors, so you examine if there is significant improvement in model fit when adding the additional factor—that is, we keep adding complexity until additional complexity does not buy us much. Always try a factor solution that is one less and one more than suggested by Cattell’s scree test to buffer your final solution because the purpose of factor analysis is to explain things and to have interpretability. Even if all rules or indicators suggest to keep X number of factors, maybe \(\pm\) one factor helps clarify things. Even though factor analysis is empirical, theory and interpretatability should also inform decisions.
22.5.4.1 Model Fit Indices
In factor analysis, we fit a model to observed data, or to the variance-covariance matrix, and we evaluate the degree of model misfit. That is, fit indices evaluate how likely it is that a given causal model gave rise to the observed data. Various model fit indices can be used for evaluating how well a model fits the data and for comparing the fit of two competing models. Fit indices known as absolute fit indices compare whether the model fits better than the best-possible fitting model (i.e., a saturated model). Examples of absolute fit indices include the chi-square test, root mean square error of approximation (RMSEA), and the standardized root mean square residual (SRMR).
The chi-square test evaluates whether the model has a significant degree of misfit relative to the best-possible fitting model (a saturated model that fits as many parameters as possible; i.e., as many parameters as there are degrees of freedom); the null hypothesis of a chi-square test is that there is no difference between the predicted data (i.e., the data that would be observed if the model were true) and the observed data. Thus, a non-significant chi-square test indicates good model fit. However, because the null hypothesis of the chi-square test is that the model-implied covariance matrix is exactly equal to the observed covariance matrix (i.e., a model of perfect fit), this may be an unrealistic comparison. Models are simplifications of reality, and our models are virtually never expected to be a perfect description of reality. Thus, we would say a model is “useful” and partially validated if “it helps us to understand the relation between variables and does a ‘reasonable’ job of matching the data…A perfect fit may be an inappropriate standard, and a high chi-square estimate may indicate what we already know—that the hypothesized model holds approximately, not perfectly.” (Bollen, 1989, p. 268). The power of the chi-square test depends on sample size, and a large sample will likely detect small differences as significantly worse than the best-possible fitting model (Bollen, 1989).
RMSEA is an index of absolute fit. Lower values indicate better fit.
SRMR is an index of absolute fit with no penalty for model complexity. Lower values indicate better fit.
There are also various fit indices known as incremental, comparative, or relative fit indices that compare whether the model fits better than the worst-possible fitting model (i.e., a “baseline” or “null” model). Incremental fit indices include a chi-square difference test, the comparative fit index (CFI), and the Tucker-Lewis index (TLI). Unlike the chi-square test comparing the model to the best-possible fitting model, a significant chi-square test of the relative fit index indicates better fit—i.e., that the model fits better than the worst-possible fitting model.
CFI is another relative fit index that compares the model to the worst-possible fitting model. Higher values indicate better fit.
TLI is another relative fit index. Higher values indicate better fit.
Parsimony fit include fit indices that use information criteria fit indices, including the Akaike Information Criterion (AIC) and the Bayesian Information Criterion (BIC). BIC penalizes model complexity more so than AIC. Lower AIC and BIC values indicate better fit.
Chi-square difference tests and CFI can be used to compare two nested models. AIC and BIC can be used to compare two non-nested models.
Criteria for acceptable fit and good fit of SEM models are in Table 22.1. In addition, dynamic fit indexes have been proposed based on simulation to identify fit index cutoffs that are tailored to the characteristics of the specific model and data (McNeish & Wolf, 2023); dynamic fit indexes are available via the dynamic package (Wolf & McNeish, 2022) or with a webapp.
| SEM Fit Index | Acceptable Fit | Good Fit |
|---|---|---|
| RMSEA | \(\leq\) .08 | \(\leq\) .05 |
| CFI | \(\geq\) .90 | \(\geq\) .95 |
| TLI | \(\geq\) .90 | \(\geq\) .95 |
| SRMR | \(\leq\) .10 | \(\leq\) .08 |
However, good model fit does not necessarily indicate a true model.
In addition to global fit indices, it can also be helpful to examine evidence of local fit, such as the residual covariance matrix. The residual covariance matrix represents the difference between the observed covariance matrix and the model-implied covariance matrix (the observed covariance matrix minus the model-implied covariance matrix). These difference values are called covariance residuals. Standardizing the covariance matrix by converting each to a correlation matrix can be helpful for interpreting the magnitude of any local misfit. This is known as a residual correlation matrix, which is composed of correlation residuals. Correlation residuals greater than |.10| are possible evidence for poor local fit (Kline, 2023). If a correlation residual is positive, it suggests that the model underpredicts the observed association between the two variables (i.e., the observed covariance is greater than the model-implied covariance). If a correlation residual is negative, it suggests that the model overpredicts their observed association between the two variables (i.e., the observed covariance is smaller than the model-implied covariance). If the two variables are connected by only indirect pathways, it may be helpful to respecify the model with direct pathways between the two variables, such as a direct effect (i.e., regression path) or a covariance path. For guidance on evaluating local fit, see Kline (2024).
22.5.5 5. Interpreting and Using Latent Factors
The next step is interpreting the model and latent factors. One data matrix can lead to many different (correct) models—you must choose one based on the factor structure and theory. Use theory to interpret the model and label the factors. In latent variable models, factors have meaning. You can use them as predictors, mediators, moderators, or outcomes. When possible, it is preferable to use the factors by examining their associations with other variables in the same model. You can extract factor scores, if necessary, for use in other analyses; however, it is preferable to examine the associations between factors and other variables in the same model if possible. And, using latent factors helps disattenuate associations for measurement error, to identify what the association is between variables when removing random measurement error.
22.6 Example of Exploratory Factor Analysis
We generated the scree plot in Figure 22.13 using the psych::fa.parallel() function of the psych package (Revelle, 2025). The optimal coordinates and the acceleration factor attempt to operationalize the Cattell scree test: i.e., the “elbow” of the scree plot (Ruscio & Roche, 2012). The optimal coordinators factor is quantified using a series of linear equations to determine whether observed eigenvalues exceed the predicted values. The acceleration factor is quantified using the acceleration of the curve, that is, the second derivative. The Kaiser-Guttman rule states to keep principal components whose eigenvalues are greater than 1. However, for exploratory factor analysis (as opposed to PCA), the criterion is to keep the factors whose eigenvalues are greater than zero (i.e., not the factors whose eigenvalues are greater than 1) (Dinno, 2014).
The number of factors to keep would depend on which criteria one uses. Based on the rule to keep factors whose eigenvalues are greater than zero and based on the parallel test, we would keep five factors. However, based on the Cattell scree test (the “elbow” of the screen plot minus one), we would keep three factors. If using the optimal coordinates, we would keep eight factors; if using the acceleration factor, we would keep one factor. Therefore, interpretability of the factors would be important for deciding how many factors to keep.
Code
Parallel analysis suggests that the number of factors = 5 and the number of components = NA
We generated the scree plot in Figure 22.14 using the nFactors::nScree() and nFactors::plotnScree() functions of the nFactors package (Raiche & Magis, 2025).
Code
#screeDataEFA <- nFactors::nScree(
# x = cor( # throws error with correlation matrix, so use covariance instead (below)
# dataForFA[faVars],
# use = "pairwise.complete.obs"),
# model = "factors")
screeDataEFA <- nFactors::nScree(
x = cov(
dataForFA[faVars],
use = "pairwise.complete.obs"),
cor = FALSE,
model = "factors")
nFactors::plotnScree(screeDataEFA)We generated the very simple structure (VSS) plots in Figures 22.15 and 22.16 using the psych::vss() and psych::nfactors() functions of the psych package (Revelle, 2025). In addition to VSS plots, the output also provides additional criteria by which to determine the optimal number of factors, each for which lower values are better, including the Velicer minimum average partial (MAP) test, the Bayesian information criterion (BIC), the sample size-adjusted BIC (SABIC), and the root mean square error of approximation (RMSEA). Depending on the criterion, the optimal number of factors extracted varies between 2 and 8 factors.
Very Simple Structure
Call: psych::vss(x = dataForFA[faVars], rotate = "oblimin", fm = "minres")
VSS complexity 1 achieves a maximimum of 0.78 with 2 factors
VSS complexity 2 achieves a maximimum of 0.87 with 3 factors
The Velicer MAP achieves a minimum of 0.13 with 3 factors
BIC achieves a minimum of 134929.4 with 4 factors
Sample Size adjusted BIC achieves a minimum of 135205.8 with 4 factors
Statistics by number of factors
vss1 vss2 map dof chisq prob sqresid fit RMSEA BIC SABIC complex
1 0.68 0.00 0.19 135 157657 0 25.9 0.68 0.75 156626 157055 1.0
2 0.78 0.86 0.16 118 143293 0 11.1 0.86 0.76 142391 142766 1.2
3 0.77 0.87 0.13 102 NaN NaN 8.9 0.89 NA NA NA 1.2
4 0.77 0.86 0.15 87 135594 0 8.1 0.90 0.87 134929 135206 1.3
5 0.72 0.81 0.31 73 151375 0 10.5 0.87 1.00 150818 151050 1.4
6 0.63 0.78 0.43 60 NaN NaN 12.8 0.84 NA NA NA 1.4
7 0.67 0.73 NaN 48 NaN NaN 12.9 0.84 NA NA NA 1.6
8 0.53 0.69 NaN 37 153142 0 18.0 0.78 1.41 152859 152977 1.5
eChisq SRMR eCRMS eBIC
1 18353 0.1699 0.181 17322
2 5637 0.0942 0.107 4736
3 814 0.0358 0.044 35
4 471 0.0272 0.036 -194
5 240 0.0194 0.028 -318
6 122 0.0139 0.022 -336
7 66 0.0102 0.018 -301
8 21 0.0057 0.012 -262
Code
Number of factors
Call: vss(x = x, n = n, rotate = rotate, diagonal = diagonal, fm = fm,
n.obs = n.obs, plot = FALSE, title = title, use = use, cor = cor)
VSS complexity 1 achieves a maximimum of 0.78 with 2 factors
VSS complexity 2 achieves a maximimum of 0.87 with 3 factors
The Velicer MAP achieves a minimum of 0.13 with 3 factors
Empirical BIC achieves a minimum of -336.01 with 6 factors
Sample Size adjusted BIC achieves a minimum of 111139.7 with 9 factors
Statistics by number of factors
vss1 vss2 map dof chisq prob sqresid fit RMSEA BIC SABIC complex
1 0.68 0.00 0.19 135 157657 0 25.9 0.68 0.75 156626 157055 1.0
2 0.78 0.86 0.16 118 143293 0 11.1 0.86 0.76 142391 142766 1.2
3 0.77 0.87 0.13 102 NaN NaN 8.9 0.89 NA NA NA 1.2
4 0.77 0.86 0.15 87 135594 0 8.1 0.90 0.87 134929 135206 1.3
5 0.72 0.81 0.31 73 151375 0 10.5 0.87 1.00 150818 151050 1.4
6 0.63 0.78 0.43 60 NaN NaN 12.8 0.84 NA NA NA 1.4
7 0.67 0.73 NaN 48 NaN NaN 12.9 0.84 NA NA NA 1.6
8 0.53 0.69 NaN 37 153142 0 18.0 0.78 1.41 152859 152977 1.5
9 0.53 0.66 NaN 27 111260 0 19.1 0.76 1.41 111054 111140 1.6
10 0.33 0.48 NaN 18 150938 0 28.5 0.65 2.01 150801 150858 1.8
11 0.32 0.44 NaN 10 NaN NaN 31.8 0.61 NA NA NA 1.8
12 0.28 0.41 NaN 3 NaN NaN 35.8 0.56 NA NA NA 1.7
13 0.24 0.35 NaN -3 0 NA 39.7 0.51 NA NA NA 2.1
14 0.21 0.28 NaN -8 0 NA 42.5 0.47 NA NA NA 2.2
15 0.19 0.27 NaN -12 NaN NA 45.5 0.44 NA NA NA 2.4
16 0.40 0.43 NaN -15 NaN NA 33.2 0.59 NA NA NA 2.0
17 0.43 0.49 NaN -17 NaN NA 31.9 0.60 NA NA NA 2.1
18 0.39 0.44 NA -18 NaN NA 37.3 0.54 NA NA NA 1.7
eChisq SRMR eCRMS eBIC
1 18353.5 0.1699 0.181 17322
2 5637.4 0.0942 0.107 4736
3 813.9 0.0358 0.044 35
4 471.0 0.0272 0.036 -194
5 239.8 0.0194 0.028 -318
6 122.3 0.0139 0.022 -336
7 65.9 0.0102 0.018 -301
8 20.8 0.0057 0.012 -262
9 12.6 0.0045 0.011 -194
10 9.3 0.0038 0.011 -128
11 7.5 0.0034 0.013 -69
12 6.5 0.0032 0.023 -16
13 5.3 0.0029 NA NA
14 5.0 0.0028 NA NA
15 4.9 0.0028 NA NA
16 4.9 0.0028 NA NA
17 4.9 0.0028 NA NA
18 4.9 0.0028 NA NA
We fit EFA models using the lavaan::efa() function of the lavaan package (Rosseel, 2012; Rosseel et al., 2024).
The model fits well according to CFI with 5 or more factors; the model fits well according to RMSEA with 6 or more factors. However, 5+ factors does not represent much of a simplification (relative to the 18 variables included). Moreover, in the model with four factors, only one variable had a significant loading on Factor 1. Thus, even with four factors, one of the factors does not seem to represent the aggregation of multiple variables. For these reasons, and because the “elbow test” for the scree plot suggested three factors, we decided to retain three factors and to see if we could achieve better fit by making additional model modifications (e.g., correlated residuals). Correlated residuals may be necessary, for example, when variables are correlated for reasons other than the latent factors.
This is lavaan 0.7-2 -- running exploratory factor analysis
Estimator ML
Rotation method GEOMIN OBLIQUE
Geomin epsilon 0.001
Rotation algorithm (rstarts) GPA (30)
Standardized metric TRUE
Row weights None
Number of observations 2078
Number of missing patterns 4
Overview models:
aic bic sabic chisq df pvalue cfi rmsea
nfactors = 1 145638.2 145942.7 145771.1 18910.759 135 0 0.054 0.618
nfactors = 2 124186.0 124586.4 124360.8 5524.117 118 0 0.668 0.395
nfactors = 3 119810.0 120300.6 120024.2 3309.908 102 0 0.733 0.390
nfactors = 4 116556.0 117131.2 116807.2 1505.471 87 0 0.893 0.285
nfactors = 5 115456.3 116110.5 115741.9 780.657 73 0 0.904 0.330
nfactors = 6 115193.3 115920.8 115511.0 NA 60 NA 0.938 0.398
nfactors = 7 114804.7 115599.8 115151.9 301.407 48 0 1.000 0.708
Eigenvalues correlation matrix:
ev1 ev2 ev3 ev4 ev5 ev6 ev7 ev8
6.81349 6.42319 2.91430 0.52122 0.38429 0.32468 0.20607 0.11241
ev9 ev10 ev11 ev12 ev13 ev14 ev15 ev16
0.08575 0.05955 0.04077 0.03402 0.02483 0.02160 0.01615 0.00971
ev17 ev18
0.00606 0.00193
Number of factors: 1
Standardized loadings: (* = significant at 1% level)
f1 unique.var communalities
completionsPerGame 0.887* 0.213 0.787
attemptsPerGame 0.887* 0.213 0.787
passing_yardsPerGame 0.876* 0.232 0.768
passing_tdsPerGame 0.741* 0.452 0.548
passing_air_yardsPerGame 0.586* 0.657 0.343
passing_yards_after_catchPerGame 0.724* 0.475 0.525
passing_first_downsPerGame 0.871* 0.242 0.758
avg_completed_air_yards -0.986* 0.028 0.972
avg_intended_air_yards -0.999* 0.002 0.998
aggressiveness -0.884* 0.219 0.781
max_completed_air_distance -0.930* 0.134 0.866
avg_air_distance -0.993* 0.013 0.987
max_air_distance -0.937* 0.123 0.877
avg_air_yards_to_sticks -0.997* 0.007 0.993
passing_cpoe .* 0.921 0.079
pass_comp_pct .* 0.916 0.084
passer_rating -0.505 0.745 0.255
completion_percentage_above_expectation -0.542* 0.707 0.293
f1
Sum of squared loadings 11.701
Proportion of total 1.000
Proportion var 0.650
Cumulative var 0.650
Number of factors: 2
Standardized loadings: (* = significant at 1% level)
f1 f2 unique.var
completionsPerGame 0.985* 0.030
attemptsPerGame 0.974* * 0.051
passing_yardsPerGame 0.996* 0.008
passing_tdsPerGame 0.865* 0.251
passing_air_yardsPerGame 0.721* 0.688* 0.034
passing_yards_after_catchPerGame 0.758* -0.609* 0.029
passing_first_downsPerGame 0.990* 0.019
avg_completed_air_yards 0.967* 0.065
avg_intended_air_yards * 0.998* 0.002
aggressiveness . 0.774* 0.385
max_completed_air_distance .* 0.817* 0.270
avg_air_distance * 0.979* 0.032
max_air_distance .* 0.855* 0.248
avg_air_yards_to_sticks 0.992* 0.016
passing_cpoe 0.322* -0.310* 0.795
pass_comp_pct .* 0.918
passer_rating 0.599* . 0.619
completion_percentage_above_expectation 0.459* . 0.734
communalities
completionsPerGame 0.970
attemptsPerGame 0.949
passing_yardsPerGame 0.992
passing_tdsPerGame 0.749
passing_air_yardsPerGame 0.966
passing_yards_after_catchPerGame 0.971
passing_first_downsPerGame 0.981
avg_completed_air_yards 0.935
avg_intended_air_yards 0.998
aggressiveness 0.615
max_completed_air_distance 0.730
avg_air_distance 0.968
max_air_distance 0.752
avg_air_yards_to_sticks 0.984
passing_cpoe 0.205
pass_comp_pct 0.082
passer_rating 0.381
completion_percentage_above_expectation 0.266
f2 f1 total
Sum of sq (obliq) loadings 6.891 6.602 13.494
Proportion of total 0.511 0.489 1.000
Proportion var 0.383 0.367 0.750
Cumulative var 0.383 0.750 0.750
Factor correlations: (* = significant at 1% level)
f1 f2
f1 1.000
f2 -0.027 1.000
Number of factors: 3
Standardized loadings: (* = significant at 1% level)
f1 f2 f3 unique.var
completionsPerGame 0.978* * 0.026
attemptsPerGame 0.993* * * 0.039
passing_yardsPerGame 0.987* * 0.010
passing_tdsPerGame 0.835* * .* 0.253
passing_air_yardsPerGame 0.786* 0.723* 0.023
passing_yards_after_catchPerGame 0.709* -0.597* 0.031
passing_first_downsPerGame 0.979* * 0.020
avg_completed_air_yards 0.777* 0.344* 0.039
avg_intended_air_yards 0.884* .* 0.002
aggressiveness 0.793* 0.370
max_completed_air_distance .* 0.672* 0.360* 0.178
avg_air_distance * 0.877* .* 0.025
max_air_distance .* 0.819* . 0.212
avg_air_yards_to_sticks 0.861* .* 0.012
passing_cpoe 1.001* 0.021
pass_comp_pct -0.477* 1.059* 0.105
passer_rating * 0.925* 0.092
completion_percentage_above_expectation 0.965* 0.046
communalities
completionsPerGame 0.974
attemptsPerGame 0.961
passing_yardsPerGame 0.990
passing_tdsPerGame 0.747
passing_air_yardsPerGame 0.977
passing_yards_after_catchPerGame 0.969
passing_first_downsPerGame 0.980
avg_completed_air_yards 0.961
avg_intended_air_yards 0.998
aggressiveness 0.630
max_completed_air_distance 0.822
avg_air_distance 0.975
max_air_distance 0.788
avg_air_yards_to_sticks 0.988
passing_cpoe 0.979
pass_comp_pct 0.895
passer_rating 0.908
completion_percentage_above_expectation 0.954
f2 f1 f3 total
Sum of sq (obliq) loadings 6.022 5.777 4.697 16.496
Proportion of total 0.365 0.350 0.285 1.000
Proportion var 0.335 0.321 0.261 0.916
Cumulative var 0.335 0.656 0.916 0.916
Factor correlations: (* = significant at 1% level)
f1 f2 f3
f1 1.000
f2 -0.131* 1.000
f3 0.162* 0.450* 1.000
Number of factors: 4
Standardized loadings: (* = significant at 1% level)
f1 f2 f3 f4
completionsPerGame 0.970* *
attemptsPerGame * 1.025*
passing_yardsPerGame .* 0.896*
passing_tdsPerGame 0.384* 0.663*
passing_air_yardsPerGame 0.817* 0.725*
passing_yards_after_catchPerGame .* 0.605* -0.603*
passing_first_downsPerGame .* 0.899* *
avg_completed_air_yards .* 0.954*
avg_intended_air_yards 1.022*
aggressiveness 0.812*
max_completed_air_distance .* .* 0.782*
avg_air_distance 0.998*
max_air_distance .* 0.880*
avg_air_yards_to_sticks 1.003*
passing_cpoe . 0.915*
pass_comp_pct -0.445* 1.068*
passer_rating .* 0.798*
completion_percentage_above_expectation . 0.891*
unique.var communalities
completionsPerGame 0.015 0.985
attemptsPerGame 0.001 0.999
passing_yardsPerGame 0.001 0.999
passing_tdsPerGame 0.204 0.796
passing_air_yardsPerGame 0.016 0.984
passing_yards_after_catchPerGame 0.024 0.976
passing_first_downsPerGame 0.023 0.977
avg_completed_air_yards 0.063 0.937
avg_intended_air_yards 0.003 0.997
aggressiveness 0.341 0.659
max_completed_air_distance 0.281 0.719
avg_air_distance 0.035 0.965
max_air_distance 0.228 0.772
avg_air_yards_to_sticks 0.018 0.982
passing_cpoe 0.039 0.961
pass_comp_pct 0.071 0.929
passer_rating 0.148 0.852
completion_percentage_above_expectation 0.029 0.971
f3 f2 f4 f1 total
Sum of sq (obliq) loadings 6.941 5.380 3.400 0.740 16.460
Proportion of total 0.422 0.327 0.207 0.045 1.000
Proportion var 0.386 0.299 0.189 0.041 0.914
Cumulative var 0.386 0.684 0.873 0.914 0.914
Factor correlations: (* = significant at 1% level)
f1 f2 f3 f4
f1 1.000
f2 0.400* 1.000
f3 0.011 -0.159* 1.000
f4 0.305* 0.131* 0.435* 1.000
Number of factors: 5
Standardized loadings: (* = significant at 1% level)
f1 f2 f3 f4 f5
completionsPerGame 0.950* * .*
attemptsPerGame 0.977* *
passing_yardsPerGame 0.857* .*
passing_tdsPerGame 0.641* 0.438*
passing_air_yardsPerGame 0.798* 0.748* *
passing_yards_after_catchPerGame 0.527* 0.304* -0.602* .* *
passing_first_downsPerGame 0.871* .* *
avg_completed_air_yards 0.991* .*
avg_intended_air_yards 1.002*
aggressiveness . 0.785* .
max_completed_air_distance .* . 0.727*
avg_air_distance 0.951* .*
max_air_distance .* 0.683* .* .
avg_air_yards_to_sticks 0.976*
passing_cpoe . 0.898*
pass_comp_pct .* 1.035*
passer_rating 0.319* 0.756*
completion_percentage_above_expectation .* 0.851*
unique.var communalities
completionsPerGame 0.008 0.992
attemptsPerGame 0.005 0.995
passing_yardsPerGame 0.003 0.997
passing_tdsPerGame 0.187 0.813
passing_air_yardsPerGame 0.016 0.984
passing_yards_after_catchPerGame 0.000 1.000
passing_first_downsPerGame 0.018 0.982
avg_completed_air_yards 0.039 0.961
avg_intended_air_yards 0.002 0.998
aggressiveness 0.447 0.553
max_completed_air_distance 0.334 0.666
avg_air_distance 0.051 0.949
max_air_distance 0.292 0.708
avg_air_yards_to_sticks 0.027 0.973
passing_cpoe 0.019 0.981
pass_comp_pct 0.097 0.903
passer_rating 0.141 0.859
completion_percentage_above_expectation 0.040 0.960
f3 f1 f5 f2 f4 total
Sum of sq (obliq) loadings 6.481 5.155 3.350 1.031 0.257 16.275
Proportion of total 0.398 0.317 0.206 0.063 0.016 1.000
Proportion var 0.360 0.286 0.186 0.057 0.014 0.904
Cumulative var 0.360 0.646 0.833 0.890 0.904 0.904
Factor correlations: (* = significant at 1% level)
f1 f2 f3 f4 f5
f1 1.000
f2 0.398* 1.000
f3 -0.105* 0.083 1.000
f4 0.203 0.122 0.006 1.000
f5 0.133* 0.384* 0.437* 0.257 1.000
Number of factors: 6
Standardized loadings: (* = significant at 1% level)
f1 f2 f3 f4 f5
completionsPerGame 0.970*
attemptsPerGame 0.998
passing_yardsPerGame . 0.885
passing_tdsPerGame 0.390 0.680
passing_air_yardsPerGame 0.817* 0.763*
passing_yards_after_catchPerGame . 0.553 -0.590* .
passing_first_downsPerGame . 0.899*
avg_completed_air_yards 0.865 0.314 .
avg_intended_air_yards 0.995*
aggressiveness . 0.735*
max_completed_air_distance . 0.534 0.300
avg_air_distance 0.949* .
max_air_distance . 0.721* .
avg_air_yards_to_sticks 0.949
passing_cpoe .
pass_comp_pct .
passer_rating .
completion_percentage_above_expectation .*
f6 unique.var communalities
completionsPerGame 0.009 0.991
attemptsPerGame 0.000 1.000
passing_yardsPerGame 0.002 0.998
passing_tdsPerGame 0.198 0.802
passing_air_yardsPerGame . 0.012 0.988
passing_yards_after_catchPerGame 0.000 1.000
passing_first_downsPerGame 0.020 0.980
avg_completed_air_yards 0.000 1.000
avg_intended_air_yards 0.002 0.998
aggressiveness 0.458 0.542
max_completed_air_distance . 0.326 0.674
avg_air_distance 0.051 0.949
max_air_distance . 0.283 0.717
avg_air_yards_to_sticks 0.027 0.973
passing_cpoe 0.893* 0.019 0.981
pass_comp_pct 1.024 0.095 0.905
passer_rating 0.765* 0.147 0.853
completion_percentage_above_expectation 0.867* 0.040 0.960
f3 f2 f6 f1 f4 f5 total
Sum of sq (obliq) loadings 6.131 5.375 3.452 0.689 0.390 0.277 16.313
Proportion of total 0.376 0.329 0.212 0.042 0.024 0.017 1.000
Proportion var 0.341 0.299 0.192 0.038 0.022 0.015 0.906
Cumulative var 0.341 0.639 0.831 0.869 0.891 0.906 0.906
Factor correlations: (* = significant at 1% level)
f1 f2 f3 f4 f5 f6
f1 1.000
f2 0.382 1.000
f3 0.085 -0.110 1.000
f4 0.465 0.226 0.242 1.000
f5 0.146 0.250 -0.036 0.277 1.000
f6 0.408 0.179 0.406 0.225 0.338 1.000
Number of factors: 7
Standardized loadings: (* = significant at 1% level)
f1 f2 f3 f4 f5
completionsPerGame 0.961*
attemptsPerGame 0.978*
passing_yardsPerGame 0.865* . .
passing_tdsPerGame 0.673 0.424 .
passing_air_yardsPerGame 0.776* 0.478 0.314
passing_yards_after_catchPerGame 0.549 0.365 -0.465
passing_first_downsPerGame 0.905* .
avg_completed_air_yards 0.424 0.549
avg_intended_air_yards 0.461 0.562
aggressiveness . 0.692
max_completed_air_distance 0.457
avg_air_distance 0.485 0.513
max_air_distance 0.606
avg_air_yards_to_sticks 0.370 0.680
passing_cpoe
pass_comp_pct . -0.541
passer_rating .
completion_percentage_above_expectation
f6 f7 unique.var
completionsPerGame . 0.010
attemptsPerGame 0.003
passing_yardsPerGame 0.003
passing_tdsPerGame 0.190
passing_air_yardsPerGame 0.006
passing_yards_after_catchPerGame . 0.000
passing_first_downsPerGame 0.016
avg_completed_air_yards 0.029
avg_intended_air_yards 0.001
aggressiveness 0.450
max_completed_air_distance 0.905* 0.000
avg_air_distance 0.050
max_air_distance 0.387* 0.175
avg_air_yards_to_sticks 0.027
passing_cpoe 0.966* 0.019
pass_comp_pct 0.993* 0.041
passer_rating 0.754* 0.146
completion_percentage_above_expectation 0.977* 0.047
communalities
completionsPerGame 0.990
attemptsPerGame 0.997
passing_yardsPerGame 0.997
passing_tdsPerGame 0.810
passing_air_yardsPerGame 0.994
passing_yards_after_catchPerGame 1.000
passing_first_downsPerGame 0.984
avg_completed_air_yards 0.971
avg_intended_air_yards 0.999
aggressiveness 0.550
max_completed_air_distance 1.000
avg_air_distance 0.950
max_air_distance 0.825
avg_air_yards_to_sticks 0.973
passing_cpoe 0.981
pass_comp_pct 0.959
passer_rating 0.854
completion_percentage_above_expectation 0.953
f1 f7 f5 f3 f6 f4 f2 total
Sum of sq (obliq) loadings 5.172 3.529 2.783 2.058 1.172 1.089 0.984 16.787
Proportion of total 0.308 0.210 0.166 0.123 0.070 0.065 0.059 1.000
Proportion var 0.287 0.196 0.155 0.114 0.065 0.061 0.055 0.933
Cumulative var 0.287 0.483 0.638 0.752 0.817 0.878 0.933 0.933
Factor correlations: (* = significant at 1% level)
f1 f2 f3 f4 f5 f6 f7
f1 1.000
f2 0.386* 1.000
f3 -0.018 -0.288 1.000
f4 -0.194 -0.250 0.631 1.000
f5 -0.149 -0.245 0.754 0.839* 1.000
f6 0.387 0.251 0.404 -0.027 0.247 1.000
f7 0.115 0.307 0.564 0.161 0.255 0.484 1.000
A path diagram of the three-factor EFA model in Figure 22.17 was created using the lavaanPlot::lavaanPlot() function of the lavaanPlot package (Lishinski, 2024).
To make the plot interactive for editing, you can use the lavaangui::plot_lavaan() function of the lavaangui package (Karch, 2025b; Karch, 2025a):
Here is the syntax for estimating a three-factor EFA using exploratory structural equation modeling (ESEM). Estimating the model in a ESEM framework allows us to make modifications to the model, such as adding correlated residuals, and adding predictors or outcomes of the latent factors. The syntax below represents the same model (with the same fit indices) as the three-factor EFA model above.
Code
efa3factor_syntax <- '
# EFA Factor Loadings
efa("efa1")*f1 +
efa("efa1")*f2 +
efa("efa1")*f3 =~ completionsPerGame + attemptsPerGame + passing_yardsPerGame + passing_tdsPerGame +
passing_air_yardsPerGame + passing_yards_after_catchPerGame + passing_first_downsPerGame +
avg_completed_air_yards + avg_intended_air_yards + aggressiveness + max_completed_air_distance +
avg_air_distance + max_air_distance + avg_air_yards_to_sticks + passing_cpoe + pass_comp_pct +
passer_rating + completion_percentage_above_expectation
'To fit the ESEM model, we use the lavaan::sem() function of the lavaan package (Rosseel, 2012; Rosseel et al., 2024).
The fit indices suggests that the model does not fit well to the data and that additional model modifications are necessary. The fit indices are below.
lavaan 0.7-2 ended normally after 233 iterations
Estimator ML
Optimization method NLMINB
Number of model parameters 93
Row rank of the constraints matrix 24
Rotation method GEOMIN OBLIQUE
Geomin epsilon 0.001
Rotation algorithm (rstarts) GPA (30)
Standardized metric TRUE
Row weights None
Number of observations 2078
Number of missing patterns 4
Model Test User Model:
Standard Scaled
Test Statistic 5779.260 3309.908
Degrees of freedom 102 102
P-value (Chi-square) 0.000 0.000
Scaling correction factor 1.746
Yuan-Bentler correction (Mplus variant)
Model Test Baseline Model:
Test statistic 45294.523 24411.364
Degrees of freedom 153 153
P-value 0.000 0.000
Scaling correction factor 1.855
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.874 0.868
Tucker-Lewis Index (TLI) 0.811 0.802
Robust Comparative Fit Index (CFI) 0.733
Robust Tucker-Lewis Index (TLI) 0.600
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -59818.005 -59818.005
Scaling correction factor 1.790
for the MLR correction
Loglikelihood unrestricted model (H1) -56928.375 -56928.375
Scaling correction factor 1.766
for the MLR correction
Akaike (AIC) 119810.009 119810.009
Bayesian (BIC) 120300.616 120300.616
Sample-size adjusted Bayesian (SABIC) 120024.209 120024.209
Root Mean Square Error of Approximation:
RMSEA 0.164 0.123
90 Percent confidence interval - lower 0.160 0.120
90 Percent confidence interval - upper 0.167 0.126
P-value H_0: RMSEA <= 0.050 0.000 0.000
P-value H_0: RMSEA >= 0.080 1.000 1.000
Robust RMSEA 0.390
90 Percent confidence interval - lower 0.369
90 Percent confidence interval - upper 0.412
P-value H_0: Robust RMSEA <= 0.050 0.000
P-value H_0: Robust RMSEA >= 0.080 1.000
Standardized Root Mean Square Residual:
SRMR 0.483 0.483
Goodness of Fit Index:
Goodness of Fit Index (GFI) 0.767
90 Percent confidence interval - lower 0.759
90 Percent confidence interval - upper 0.775
Robust GFI 0.381
90 Percent confidence interval - lower 0.356
90 Percent confidence interval - upper 0.408
Parameter Estimates:
Standard errors Sandwich
Information bread Observed
Observed information based on Hessian
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
f1 =~ efa1
completinsPrGm 7.829 0.084 93.715 0.000 7.829 0.978
attemptsPerGam 12.434 0.128 97.501 0.000 12.434 0.993
pssng_yrdsPrGm 91.431 0.934 97.933 0.000 91.431 0.987
passng_tdsPrGm 0.576 0.010 57.896 0.000 0.576 0.835
pssng_r_yrdsPG 94.422 2.154 43.842 0.000 94.422 0.786
pssng_yrds__PG 46.017 1.010 45.574 0.000 46.017 0.709
pssng_frst_dPG 4.435 0.047 94.412 0.000 4.435 0.979
avg_cmpltd_r_y 0.019 0.045 0.427 0.669 0.019 0.005
avg_ntndd_r_yr -0.047 0.039 -1.209 0.227 -0.047 -0.010
aggressiveness -0.273 0.365 -0.749 0.454 -0.273 -0.036
mx_cmpltd_r_ds 1.906 0.368 5.175 0.000 1.906 0.181
avg_air_distnc -0.203 0.069 -2.932 0.003 -0.203 -0.046
max_air_distnc 1.778 0.394 4.516 0.000 1.778 0.194
avg_r_yrds_t_s 0.013 0.040 0.323 0.747 0.013 0.003
passing_cpoe -0.489 0.215 -2.272 0.023 -0.489 -0.036
pass_comp_pct 0.000 0.000 0.697 0.486 0.000 0.003
passer_rating 3.177 0.896 3.546 0.000 3.177 0.098
cmpltn_prcnt__ -0.353 0.275 -1.282 0.200 -0.353 -0.030
f2 =~ efa1
completinsPrGm 0.022 0.031 0.723 0.470 0.022 0.003
attemptsPerGam 0.441 0.083 5.313 0.000 0.441 0.035
pssng_yrdsPrGm -0.290 0.288 -1.007 0.314 -0.290 -0.003
passng_tdsPrGm -0.032 0.009 -3.479 0.001 -0.032 -0.046
pssng_r_yrdsPG 86.842 1.839 47.225 0.000 86.842 0.723
pssng_yrds__PG -38.767 0.943 -41.095 0.000 -38.767 -0.597
pssng_frst_dPG -0.030 0.014 -2.080 0.037 -0.030 -0.007
avg_cmpltd_r_y 3.342 0.189 17.712 0.000 3.342 0.777
avg_ntndd_r_yr 4.143 0.192 21.601 0.000 4.143 0.884
aggressiveness 6.001 0.835 7.186 0.000 6.001 0.793
mx_cmpltd_r_ds 7.081 0.730 9.703 0.000 7.081 0.672
avg_air_distnc 3.835 0.207 18.548 0.000 3.835 0.877
max_air_distnc 7.522 0.688 10.940 0.000 7.522 0.819
avg_r_yrds_t_s 4.069 0.221 18.422 0.000 4.069 0.861
passing_cpoe -0.192 0.413 -0.465 0.642 -0.192 -0.014
pass_comp_pct -0.062 0.006 -10.876 0.000 -0.062 -0.477
passer_rating 0.525 0.849 0.619 0.536 0.525 0.016
cmpltn_prcnt__ 0.414 0.477 0.867 0.386 0.414 0.035
f3 =~ efa1
completinsPrGm 0.407 0.048 8.404 0.000 0.407 0.051
attemptsPerGam -0.691 0.074 -9.350 0.000 -0.691 -0.055
pssng_yrdsPrGm 4.036 0.454 8.886 0.000 4.036 0.044
passng_tdsPrGm 0.076 0.009 8.676 0.000 0.076 0.111
pssng_r_yrdsPG -2.055 1.495 -1.375 0.169 -2.055 -0.017
pssng_yrds__PG 0.210 0.426 0.493 0.622 0.210 0.003
pssng_frst_dPG 0.255 0.026 9.688 0.000 0.255 0.056
avg_cmpltd_r_y 1.479 0.236 6.252 0.000 1.479 0.344
avg_ntndd_r_yr 1.003 0.197 5.084 0.000 1.003 0.214
aggressiveness -0.087 0.563 -0.154 0.878 -0.087 -0.011
mx_cmpltd_r_ds 3.793 0.861 4.407 0.000 3.793 0.360
avg_air_distnc 0.870 0.229 3.805 0.000 0.870 0.199
max_air_distnc 1.194 0.762 1.567 0.117 1.194 0.130
avg_r_yrds_t_s 1.151 0.205 5.615 0.000 1.151 0.243
passing_cpoe 13.719 0.577 23.791 0.000 13.719 1.001
pass_comp_pct 0.138 0.005 27.298 0.000 0.138 1.059
passer_rating 29.956 2.128 14.080 0.000 29.956 0.925
cmpltn_prcnt__ 11.435 0.682 16.760 0.000 11.435 0.965
Covariances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
f1 ~~
f2 -0.131 0.027 -4.816 0.000 -0.131 -0.131
f3 0.162 0.035 4.626 0.000 0.162 0.162
f2 ~~
f3 0.450 0.037 12.221 0.000 0.450 0.450
Intercepts:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 13.241 0.176 75.384 0.000 13.241 1.654
.attemptsPerGam 21.833 0.275 79.459 0.000 21.833 1.743
.pssng_yrdsPrGm 147.988 2.033 72.795 0.000 147.988 1.597
.passng_tdsPrGm 0.849 0.015 56.124 0.000 0.849 1.231
.pssng_r_yrdsPG 134.335 2.634 50.999 0.000 134.335 1.119
.pssng_yrds__PG 86.971 1.424 61.068 0.000 86.971 1.340
.pssng_frst_dPG 7.143 0.099 71.831 0.000 7.143 1.576
.avg_cmpltd_r_y 3.563 0.168 21.255 0.000 3.563 0.828
.avg_ntndd_r_yr 5.700 0.157 36.321 0.000 5.700 1.216
.aggressiveness 13.852 0.508 27.252 0.000 13.852 1.831
.mx_cmpltd_r_ds 33.435 0.561 59.581 0.000 33.435 3.175
.avg_air_distnc 19.158 0.162 117.917 0.000 19.158 4.380
.max_air_distnc 42.755 0.591 72.373 0.000 42.755 4.655
.avg_r_yrds_t_s -3.309 0.180 -18.349 0.000 -3.309 -0.700
.passing_cpoe -6.225 0.430 -14.484 0.000 -6.225 -0.454
.pass_comp_pct 0.592 0.003 183.536 0.000 0.592 4.555
.passer_rating 71.808 1.630 44.045 0.000 71.808 2.217
.cmpltn_prcnt__ -5.565 0.487 -11.426 0.000 -5.565 -0.470
Variances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.cmpltPG 1.660 0.123 13.455 0.000 1.660 0.026
.attmpPG 6.100 0.403 15.131 0.000 6.100 0.039
.pssn_PG 86.973 8.272 10.514 0.000 86.973 0.010
.pssn_PG 0.120 0.006 20.920 0.000 0.120 0.253
.pss__PG 328.780 29.227 11.249 0.000 328.780 0.023
.ps___PG 130.879 8.591 15.234 0.000 130.879 0.031
.pss__PG 0.414 0.026 16.033 0.000 0.414 0.020
.avg_c__ 0.726 0.071 10.261 0.000 0.726 0.039
.avg_n__ 0.035 0.013 2.753 0.006 0.035 0.002
.aggrssv 21.192 1.988 10.663 0.000 21.192 0.370
.mx_cm__ 19.775 1.764 11.208 0.000 19.775 0.178
.avg_r_d 0.478 0.032 14.831 0.000 0.478 0.025
.mx_r_ds 17.921 1.734 10.337 0.000 17.921 0.212
.avg____ 0.264 0.031 8.621 0.000 0.264 0.012
.pssng_c 3.989 0.988 4.038 0.000 3.989 0.021
.pss_cm_ 0.002 0.000 6.664 0.000 0.002 0.105
.pssr_rt 96.496 14.441 6.682 0.000 96.496 0.092
.cmpl___ 6.406 0.835 7.672 0.000 6.406 0.046
f1 (lb+b) 1.000 1.000 1.000
f2 (lb+b) 1.000 1.000 1.000
f3 (lb+b) 1.000 1.000 1.000
R-Square:
Estimate
completinsPrGm 0.974
attemptsPerGam 0.961
pssng_yrdsPrGm 0.990
passng_tdsPrGm 0.747
pssng_r_yrdsPG 0.977
pssng_yrds__PG 0.969
pssng_frst_dPG 0.980
avg_cmpltd_r_y 0.961
avg_ntndd_r_yr 0.998
aggressiveness 0.630
mx_cmpltd_r_ds 0.822
avg_air_distnc 0.975
max_air_distnc 0.788
avg_r_yrds_t_s 0.988
passing_cpoe 0.979
pass_comp_pct 0.895
passer_rating 0.908
cmpltn_prcnt__ 0.954
Code
chisq df pvalue
5779.260 102.000 0.000
chisq.scaled df.scaled pvalue.scaled
3309.908 102.000 0.000
chisq.scaling.factor baseline.chisq baseline.df
1.746 45294.523 153.000
baseline.pvalue rmsea cfi
0.000 0.164 0.874
tli srmr rmsea.robust
0.811 0.483 0.390
cfi.robust tli.robust
0.733 0.600
$type
[1] "cor.bollen"
$cov
cmplPG attmPG pssng_yPG pssng_tPG
completionsPerGame 0.000
attemptsPerGame 0.021 0.000
passing_yardsPerGame -0.004 -0.007 0.000
passing_tdsPerGame -0.022 -0.043 0.014 0.000
passing_air_yardsPerGame 0.003 0.008 0.000 -0.011
passing_yards_after_catchPerGame -0.006 0.001 0.006 0.000
passing_first_downsPerGame 0.000 -0.006 0.003 0.019
avg_completed_air_yards -0.077 -0.060 -0.029 0.003
avg_intended_air_yards -0.078 -0.068 -0.046 -0.018
aggressiveness -0.052 -0.025 -0.040 -0.029
max_completed_air_distance 0.099 0.121 0.143 0.163
avg_air_distance -0.075 -0.063 -0.044 -0.019
max_air_distance 0.033 0.037 0.048 0.061
avg_air_yards_to_sticks -0.076 -0.065 -0.042 -0.005
passing_cpoe 0.025 0.025 0.030 0.026
pass_comp_pct 0.013 0.010 0.000 -0.015
passer_rating 0.068 0.065 0.102 0.179
completion_percentage_above_expectation -0.003 -0.007 -0.002 -0.004
pssng_r_PG p___PG pssng_f_PG avg_c__
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame 0.000
passing_yards_after_catchPerGame -0.008 0.000
passing_first_downsPerGame -0.003 -0.002 0.000
avg_completed_air_yards -0.050 -0.025 -0.039 0.000
avg_intended_air_yards -0.056 -0.009 -0.052 -0.027
aggressiveness -0.158 0.090 -0.029 -0.136
max_completed_air_distance -0.053 0.233 0.109 -0.168
avg_air_distance -0.073 0.013 -0.054 -0.047
max_air_distance -0.019 0.105 0.031 -0.114
avg_air_yards_to_sticks -0.066 0.005 -0.043 -0.033
passing_cpoe -0.029 0.068 0.028 -0.312
pass_comp_pct 0.004 0.000 0.001 -0.318
passer_rating -0.189 0.293 0.097 -0.526
completion_percentage_above_expectation 0.003 -0.009 -0.001 -0.218
avg_n__ aggrss mx_c__ avg_r_ mx_r_d
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards 0.000
aggressiveness -0.123 0.000
max_completed_air_distance -0.232 -0.293 0.000
avg_air_distance -0.013 -0.139 -0.207 0.000
max_air_distance -0.075 -0.211 -0.013 -0.065 0.000
avg_air_yards_to_sticks -0.008 -0.115 -0.223 -0.020 -0.086
passing_cpoe -0.301 -0.420 -0.268 -0.298 -0.181
pass_comp_pct -0.304 -0.354 -0.207 -0.303 -0.142
passer_rating -0.561 -0.596 -0.354 -0.555 -0.373
completion_percentage_above_expectation -0.208 -0.275 -0.226 -0.208 -0.118
av____ pssng_ pss_c_ pssr_r cmp___
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards
aggressiveness
max_completed_air_distance
avg_air_distance
max_air_distance
avg_air_yards_to_sticks 0.000
passing_cpoe -0.325 0.000
pass_comp_pct -0.338 0.030 0.000
passer_rating -0.575 -0.088 0.081 0.000
completion_percentage_above_expectation -0.234 -0.007 -0.018 -0.140 0.000
$mean
completionsPerGame attemptsPerGame
0.000 0.000
passing_yardsPerGame passing_tdsPerGame
0.000 0.000
passing_air_yardsPerGame passing_yards_after_catchPerGame
0.000 0.000
passing_first_downsPerGame avg_completed_air_yards
0.000 0.462
avg_intended_air_yards aggressiveness
0.308 0.247
max_completed_air_distance avg_air_distance
0.415 0.319
max_air_distance avg_air_yards_to_sticks
0.147 0.372
passing_cpoe pass_comp_pct
0.043 -0.002
passer_rating completion_percentage_above_expectation
0.263 0.022
We can examine the model modification indices to identify parameters that, if estimated, would substantially improve model fit. For instance, the modification indices below indicate additional correlated residuals that could substantially improve model fit. However, it is generally not recommended to blindly estimate additional parameters solely based on modification indices, which can lead to data dredging and overfitting. Rather, it is generally advised to consider modification indices in light of theory. Based on the modification indices, we will add several correlated residuals to the model, to help account for why variables are associated with each other for reasons other than their underlying latent factors.
Below are factor scores from the model for the first six players:
f1 f2 f3
[1,] -0.08970449 -1.31994346 0.3139289
[2,] 0.22693533 -1.70337546 -1.0138644
[3,] 0.44257427 -1.87236094 -1.2401232
[4,] 0.07382945 0.07540888 0.7574559
[5,] 0.90362767 1.17489045 0.4571868
[6,] -0.02368323 0.12074646 -0.2221859
A path diagram of the three-factor ESEM model is in Figure 22.18.
To make the plot interactive for editing, you can use the lavaangui::plot_lavaan() function of the lavaangui package (Karch, 2025b; Karch, 2025a):
Below is a modification of the three-factor model with correlated residuals. For instance, it makes sense that passing completions and attempts are related to each other (even after accounting for their latent factor).
Code
efa3factorModified_syntax <- '
# EFA Factor Loadings
efa("efa1")*F1 +
efa("efa1")*F2 +
efa("efa1")*F3 =~ completionsPerGame + attemptsPerGame + passing_yardsPerGame + passing_tdsPerGame +
passing_air_yardsPerGame + passing_yards_after_catchPerGame + passing_first_downsPerGame +
avg_completed_air_yards + avg_intended_air_yards + aggressiveness + max_completed_air_distance +
avg_air_distance + max_air_distance + avg_air_yards_to_sticks + passing_cpoe + pass_comp_pct +
passer_rating + completion_percentage_above_expectation
# Correlated Residuals
completionsPerGame ~~ attemptsPerGame
passing_yardsPerGame ~~ passing_yards_after_catchPerGame
attemptsPerGame ~~ passing_yardsPerGame
attemptsPerGame ~~ passing_air_yardsPerGame
passing_air_yardsPerGame ~~ passing_yards_after_catchPerGame
passing_yards_after_catchPerGame ~~ avg_completed_air_yards
passing_tdsPerGame ~~ passer_rating
completionsPerGame ~~ passing_air_yardsPerGame
max_completed_air_distance ~~ max_air_distance
aggressiveness ~~ completion_percentage_above_expectation
'lavaan 0.7-2 ended normally after 385 iterations
Estimator ML
Optimization method NLMINB
Number of model parameters 103
Row rank of the constraints matrix 34
Rotation method GEOMIN OBLIQUE
Geomin epsilon 0.001
Rotation algorithm (rstarts) GPA (30)
Standardized metric TRUE
Row weights None
Number of observations 2078
Number of missing patterns 4
Model Test User Model:
Standard Scaled
Test Statistic 1439.758 840.288
Degrees of freedom 92 92
P-value (Chi-square) 0.000 0.000
Scaling correction factor 1.713
Yuan-Bentler correction (Mplus variant)
Model Test Baseline Model:
Test statistic 45294.523 24411.364
Degrees of freedom 153 153
P-value 0.000 0.000
Scaling correction factor 1.855
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.970 0.969
Tucker-Lewis Index (TLI) 0.950 0.949
Robust Comparative Fit Index (CFI) 0.937
Robust Tucker-Lewis Index (TLI) 0.895
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -57648.254 -57648.254
Scaling correction factor 1.816
for the MLR correction
Loglikelihood unrestricted model (H1) -56928.375 -56928.375
Scaling correction factor 1.766
for the MLR correction
Akaike (AIC) 115490.508 115490.508
Bayesian (BIC) 116037.506 116037.506
Sample-size adjusted Bayesian (SABIC) 115729.329 115729.329
Root Mean Square Error of Approximation:
RMSEA 0.084 0.063
90 Percent confidence interval - lower 0.080 0.060
90 Percent confidence interval - upper 0.088 0.066
P-value H_0: RMSEA <= 0.050 0.000 0.000
P-value H_0: RMSEA >= 0.080 0.957 0.000
Robust RMSEA 0.203
90 Percent confidence interval - lower 0.178
90 Percent confidence interval - upper 0.228
P-value H_0: Robust RMSEA <= 0.050 0.000
P-value H_0: Robust RMSEA >= 0.080 1.000
Standardized Root Mean Square Residual:
SRMR 0.150 0.150
Goodness of Fit Index:
Goodness of Fit Index (GFI) 0.933
90 Percent confidence interval - lower 0.927
90 Percent confidence interval - upper 0.938
Robust GFI 0.717
90 Percent confidence interval - lower 0.667
90 Percent confidence interval - upper 0.767
Parameter Estimates:
Standard errors Sandwich
Information bread Observed
Observed information based on Hessian
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 =~ efa1
completinsPrGm 7.775 0.086 90.750 0.000 7.775 0.971
attemptsPerGam 12.253 0.131 93.674 0.000 12.253 0.979
pssng_yrdsPrGm 91.692 0.950 96.535 0.000 91.692 0.990
passng_tdsPrGm 0.590 0.010 60.427 0.000 0.590 0.856
pssng_r_yrdsPG 89.142 2.869 31.072 0.000 89.142 0.743
pssng_yrds__PG 47.912 1.389 34.488 0.000 47.912 0.737
pssng_frst_dPG 4.455 0.048 93.301 0.000 4.455 0.983
avg_cmpltd_r_y 0.011 0.093 0.119 0.905 0.011 0.003
avg_ntndd_r_yr -0.070 0.098 -0.715 0.475 -0.070 -0.020
aggressiveness 0.001 0.240 0.004 0.997 0.001 0.000
mx_cmpltd_r_ds 1.884 0.426 4.418 0.000 1.884 0.226
avg_air_distnc -0.236 0.116 -2.036 0.042 -0.236 -0.072
max_air_distnc 1.551 0.423 3.667 0.000 1.551 0.209
avg_r_yrds_t_s 0.005 0.088 0.055 0.956 0.005 0.001
passing_cpoe -0.864 0.405 -2.135 0.033 -0.864 -0.059
pass_comp_pct 0.001 0.002 0.324 0.746 0.001 0.006
passer_rating 3.107 0.912 3.405 0.001 3.107 0.114
cmpltn_prcnt__ -0.632 0.420 -1.503 0.133 -0.632 -0.052
F2 =~ efa1
completinsPrGm -0.108 0.042 -2.581 0.010 -0.108 -0.013
attemptsPerGam 0.095 0.135 0.706 0.480 0.095 0.008
pssng_yrdsPrGm 0.908 0.337 2.693 0.007 0.908 0.010
passng_tdsPrGm -0.002 0.008 -0.189 0.850 -0.002 -0.002
pssng_r_yrdsPG 82.647 2.537 32.577 0.000 82.647 0.689
pssng_yrds__PG -36.247 1.110 -32.648 0.000 -36.247 -0.558
pssng_frst_dPG -0.000 0.016 -0.022 0.983 -0.000 -0.000
avg_cmpltd_r_y 3.027 0.144 21.048 0.000 3.027 0.939
avg_ntndd_r_yr 3.506 0.141 24.874 0.000 3.506 1.005
aggressiveness 4.940 0.687 7.193 0.000 4.940 0.786
mx_cmpltd_r_ds 5.898 0.561 10.514 0.000 5.898 0.707
avg_air_distnc 3.207 0.148 21.626 0.000 3.207 0.977
max_air_distnc 6.299 0.561 11.221 0.000 6.299 0.850
avg_r_yrds_t_s 3.416 0.154 22.150 0.000 3.416 0.980
passing_cpoe 0.698 0.983 0.711 0.477 0.698 0.048
pass_comp_pct -0.071 0.011 -6.578 0.000 -0.071 -0.551
passer_rating -0.423 0.428 -0.988 0.323 -0.423 -0.015
cmpltn_prcnt__ 0.763 0.800 0.954 0.340 0.763 0.063
F3 =~ efa1
completinsPrGm 0.466 0.105 4.436 0.000 0.466 0.058
attemptsPerGam -0.603 0.138 -4.379 0.000 -0.603 -0.048
pssng_yrdsPrGm 3.185 1.216 2.620 0.009 3.185 0.034
passng_tdsPrGm 0.057 0.013 4.371 0.000 0.057 0.082
pssng_r_yrdsPG 0.468 1.400 0.335 0.738 0.468 0.004
pssng_yrds__PG -1.431 1.652 -0.867 0.386 -1.431 -0.022
pssng_frst_dPG 0.219 0.065 3.387 0.001 0.219 0.048
avg_cmpltd_r_y 0.170 0.165 1.035 0.300 0.170 0.053
avg_ntndd_r_yr -0.056 0.143 -0.388 0.698 -0.056 -0.016
aggressiveness -2.492 0.672 -3.710 0.000 -2.492 -0.397
mx_cmpltd_r_ds 1.640 0.774 2.119 0.034 1.640 0.196
avg_air_distnc -0.067 0.167 -0.401 0.689 -0.067 -0.020
max_air_distnc -0.599 0.651 -0.920 0.357 -0.599 -0.081
avg_r_yrds_t_s 0.061 0.157 0.391 0.696 0.061 0.018
passing_cpoe 14.197 0.823 17.244 0.000 14.197 0.971
pass_comp_pct 0.143 0.007 20.333 0.000 0.143 1.103
passer_rating 24.861 2.262 10.992 0.000 24.861 0.910
cmpltn_prcnt__ 11.649 0.836 13.927 0.000 11.649 0.956
Covariances:
Estimate Std.Err z-value P(>|z|)
.completionsPerGame ~~
.attemptsPerGam 3.404 0.184 18.525 0.000
.passing_yardsPerGame ~~
.pssng_yrds__PG 90.386 7.556 11.962 0.000
.attemptsPerGame ~~
.pssng_yrdsPrGm 3.312 0.620 5.340 0.000
.pssng_r_yrdsPG 53.792 2.881 18.669 0.000
.passing_air_yardsPerGame ~~
.pssng_yrds__PG -228.362 40.089 -5.696 0.000
.passing_yards_after_catchPerGame ~~
.avg_cmpltd_r_y -6.080 0.408 -14.910 0.000
.passing_tdsPerGame ~~
.passer_rating 1.724 0.149 11.551 0.000
.completionsPerGame ~~
.pssng_r_yrdsPG 13.989 1.240 11.284 0.000
.max_completed_air_distance ~~
.max_air_distnc 8.589 1.247 6.890 0.000
.aggressiveness ~~
.cmpltn_prcnt__ 5.883 0.803 7.327 0.000
F1 ~~
F2 -0.088 0.037 -2.419 0.016
F3 0.171 0.037 4.571 0.000
F2 ~~
F3 0.484 0.067 7.183 0.000
Std.lv Std.all
3.404 0.784
90.386 0.605
3.312 0.123
53.792 0.608
-228.362 -0.464
-6.080 -0.435
1.724 0.509
13.989 0.327
8.589 0.447
5.883 0.550
-0.088 -0.088
0.171 0.171
0.484 0.484
Intercepts:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 13.241 0.176 75.384 0.000 13.241 1.654
.attemptsPerGam 21.833 0.275 79.459 0.000 21.833 1.744
.pssng_yrdsPrGm 147.988 2.033 72.795 0.000 147.988 1.597
.passng_tdsPrGm 0.849 0.015 56.124 0.000 0.849 1.231
.pssng_r_yrdsPG 134.335 2.634 50.999 0.000 134.335 1.120
.pssng_yrds__PG 86.971 1.424 61.068 0.000 86.971 1.338
.pssng_frst_dPG 7.143 0.099 71.831 0.000 7.143 1.576
.avg_cmpltd_r_y 4.238 0.139 30.591 0.000 4.238 1.315
.avg_ntndd_r_yr 6.440 0.140 46.091 0.000 6.440 1.847
.aggressiveness 15.297 0.427 35.831 0.000 15.297 2.433
.mx_cmpltd_r_ds 34.806 0.455 76.440 0.000 34.806 4.169
.avg_air_distnc 19.849 0.138 143.347 0.000 19.849 6.050
.max_air_distnc 44.182 0.488 90.472 0.000 44.182 5.959
.avg_r_yrds_t_s -2.561 0.147 -17.395 0.000 -2.561 -0.735
.passing_cpoe -7.082 0.437 -16.200 0.000 -7.082 -0.484
.pass_comp_pct 0.592 0.003 184.733 0.000 0.592 4.559
.passer_rating 73.912 1.428 51.743 0.000 73.912 2.706
.cmpltn_prcnt__ -6.143 0.438 -14.033 0.000 -6.143 -0.504
Variances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 2.096 0.114 18.434 0.000 2.096 0.033
.attemptsPerGam 8.995 0.375 23.995 0.000 8.995 0.057
.pssng_yrdsPrGm 80.199 8.317 9.643 0.000 80.199 0.009
.passng_tdsPrGm 0.113 0.006 20.161 0.000 0.113 0.237
.pssng_r_yrdsPG 871.275 94.242 9.245 0.000 871.275 0.061
.pssng_yrds__PG 278.050 19.737 14.088 0.000 278.050 0.066
.pssng_frst_dPG 0.321 0.026 12.200 0.000 0.321 0.016
.avg_cmpltd_r_y 0.703 0.062 11.336 0.000 0.703 0.068
.avg_ntndd_r_yr 0.007 0.013 0.514 0.607 0.007 0.001
.aggressiveness 20.825 1.938 10.748 0.000 20.825 0.527
.mx_cmpltd_r_ds 20.201 1.789 11.292 0.000 20.201 0.290
.avg_air_distnc 0.488 0.033 14.904 0.000 0.488 0.045
.max_air_distnc 18.236 1.735 10.513 0.000 18.236 0.332
.avg_r_yrds_t_s 0.279 0.032 8.788 0.000 0.279 0.023
.passing_cpoe 5.685 2.506 2.268 0.023 5.685 0.027
.pass_comp_pct 0.001 0.000 2.870 0.004 0.001 0.065
.passer_rating 101.872 13.100 7.777 0.000 101.872 0.137
.cmpltn_prcnt__ 5.495 0.905 6.070 0.000 5.495 0.037
F1 1.000 1.000 1.000
F2 1.000 1.000 1.000
F3 1.000 1.000 1.000
R-Square:
Estimate
completinsPrGm 0.967
attemptsPerGam 0.943
pssng_yrdsPrGm 0.991
passng_tdsPrGm 0.763
pssng_r_yrdsPG 0.939
pssng_yrds__PG 0.934
pssng_frst_dPG 0.984
avg_cmpltd_r_y 0.932
avg_ntndd_r_yr 0.999
aggressiveness 0.473
mx_cmpltd_r_ds 0.710
avg_air_distnc 0.955
max_air_distnc 0.668
avg_r_yrds_t_s 0.977
passing_cpoe 0.973
pass_comp_pct 0.935
passer_rating 0.863
cmpltn_prcnt__ 0.963
The model fits substantially better (though not perfectly) with the additional correlated residuals. Below are the model fit indices:
Code
chisq df pvalue
1439.758 92.000 0.000
chisq.scaled df.scaled pvalue.scaled
840.288 92.000 0.000
chisq.scaling.factor baseline.chisq baseline.df
1.713 45294.523 153.000
baseline.pvalue rmsea cfi
0.000 0.084 0.970
tli srmr rmsea.robust
0.950 0.150 0.203
cfi.robust tli.robust
0.937 0.895
$type
[1] "cor.bollen"
$cov
cmplPG attmPG pssng_yPG pssng_tPG
completionsPerGame 0.000
attemptsPerGame 0.000 0.000
passing_yardsPerGame -0.001 -0.001 0.000
passing_tdsPerGame -0.029 -0.047 0.003 0.000
passing_air_yardsPerGame 0.004 0.003 -0.001 -0.036
passing_yards_after_catchPerGame -0.004 -0.002 0.001 0.016
passing_first_downsPerGame 0.001 0.000 0.000 0.007
avg_completed_air_yards -0.028 -0.021 0.001 0.023
avg_intended_air_yards -0.039 -0.033 -0.027 -0.012
aggressiveness -0.029 -0.027 -0.037 -0.026
max_completed_air_distance 0.070 0.090 0.098 0.115
avg_air_distance -0.025 -0.017 -0.013 -0.003
max_air_distance 0.039 0.038 0.035 0.038
avg_air_yards_to_sticks -0.046 -0.039 -0.032 -0.007
passing_cpoe 0.052 0.056 0.057 0.055
pass_comp_pct 0.002 0.000 0.001 -0.002
passer_rating 0.046 0.047 0.081 0.071
completion_percentage_above_expectation 0.015 0.016 0.015 0.013
pssng_r_PG p___PG pssng_f_PG avg_c__
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame 0.000
passing_yards_after_catchPerGame 0.001 0.000
passing_first_downsPerGame -0.002 0.003 0.000
avg_completed_air_yards -0.036 0.023 -0.002 0.000
avg_intended_air_yards -0.036 -0.008 -0.026 -0.018
aggressiveness -0.010 -0.051 -0.019 0.039
max_completed_air_distance -0.047 0.162 0.070 -0.065
avg_air_distance -0.034 0.015 -0.016 -0.028
max_air_distance 0.037 0.035 0.024 -0.034
avg_air_yards_to_sticks -0.053 0.001 -0.026 -0.017
passing_cpoe -0.067 0.137 0.058 -0.173
pass_comp_pct 0.002 0.000 0.000 -0.057
passer_rating -0.199 0.272 0.077 -0.313
completion_percentage_above_expectation -0.024 0.037 0.018 -0.066
avg_n__ aggrss mx_c__ avg_r_ mx_r_d
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards 0.000
aggressiveness 0.052 0.000
max_completed_air_distance -0.142 -0.049 0.000
avg_air_distance -0.003 0.040 -0.104 0.000
max_air_distance 0.002 -0.022 -0.027 0.025 0.000
avg_air_yards_to_sticks -0.003 0.068 -0.133 -0.003 -0.006
passing_cpoe -0.209 -0.108 -0.178 -0.205 -0.052
pass_comp_pct -0.091 -0.028 -0.069 -0.095 0.044
passer_rating -0.387 -0.244 -0.222 -0.378 -0.193
completion_percentage_above_expectation -0.098 -0.021 -0.125 -0.096 0.026
av____ pssng_ pss_c_ pssr_r cmp___
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards
aggressiveness
max_completed_air_distance
avg_air_distance
max_air_distance
avg_air_yards_to_sticks 0.000
passing_cpoe -0.234 0.000
pass_comp_pct -0.131 0.069 0.000
passer_rating -0.406 -0.053 0.095 0.000
completion_percentage_above_expectation -0.126 -0.009 0.002 -0.112 0.000
$mean
completionsPerGame attemptsPerGame
0.000 0.000
passing_yardsPerGame passing_tdsPerGame
0.000 0.000
passing_air_yardsPerGame passing_yards_after_catchPerGame
0.000 0.000
passing_first_downsPerGame avg_completed_air_yards
0.000 0.205
avg_intended_air_yards aggressiveness
0.087 0.027
max_completed_air_distance avg_air_distance
0.215 0.097
max_air_distance avg_air_yards_to_sticks
-0.040 0.138
passing_cpoe pass_comp_pct
0.108 -0.001
passer_rating completion_percentage_above_expectation
0.182 0.073
Below are factor scores from the model for the first six players:
Code
F1 F2 F3
[1,] -0.03800783 -0.93146647 0.3627520
[2,] 0.19761534 -1.67922007 -1.0810966
[3,] 0.43642705 -2.31968535 -1.5645547
[4,] 0.15680930 0.28497221 0.7875911
[5,] 0.83504342 1.10641648 0.4402613
[6,] -0.13306095 0.07169419 -0.2007779
The path diagram of the modified ESEM model with correlated residuals is in Figure 22.19.
Model fit of nested models can be compared with a chi-square difference test. This allows us to evaluate whether the more complex model fits signficantly better than the less complex model. In this case, our more complex model is the three-factor model with correlated residuals; the less complex model is the three-factor model without correlated residuals. The modified model with the correlated residuals and the original model are considered “nested” models. The original model is nested within the modified model because the modified model includes all of the terms of the original model along with additional terms.
In this case, the model with the correlated residuals fit significantly better (i.e., has a significantly smaller chi-square value) than the model without the correlated residuals.
Here are the variables that had a standardized loading greater than 0.3 on each of the factors:
Code
factor1vars <- c(
"completionsPerGame","attemptsPerGame","passing_yardsPerGame","passing_tdsPerGame",
"passing_air_yardsPerGame","passing_yards_after_catchPerGame","passing_first_downsPerGame")
factor2vars <- c(
"avg_completed_air_yards","avg_intended_air_yards","aggressiveness","max_completed_air_distance",
"avg_air_distance","max_air_distance","avg_air_yards_to_sticks")
factor3vars <- c(
"passing_cpoe","pass_comp_pct","passer_rating","completion_percentage_above_expectation")The variables that loaded most strongly onto factor 1 appear to reflect Quarterback usage: completions per game, passing attempts per game, passing yards per game, passing touchdowns per game, passing air yards (total horizontal distance the ball travels on all pass attempts) per game, passing yards after the catch per game, and first downs gained per game by passing. Quarterbacks who tend to throw more tend to have higher levels on those variables. Thus, we label component 1 as “Usage”, which reflects total Quarterback involvement, regardless of efficiency or outcome.
The variables that loaded most strongly onto factor 2 appear to reflect Quarterback aggressiveness: average air yards on completed passes, average air yards on all attempted passes, aggressiveness (percentage of passing attempts thrown into tight windows, where there is a defender within one yard or less of the receiver at the time of the completion or incompletion), average amount of air yards ahead of or behind the first down marker on passing attempts, average air distance (the true three-dimensional distance the ball travels in the air), maximum air distance, and maximum air distance on completed passes. Quarterbacks who throw the ball farther and into tighter windows tend to have higher values on those variables. Thus, we label component 2 as “Aggressiveness”, which reflects throwing longer, more difficult passes with a tight window.
The variables that loaded most strongly onto factor 3 appear to reflect Quarterback performance: passing completion percentage above expectation, pass completion percentage, and passer rating. Quarterbacks who perform better tend to have higher values on those variables. Thus, we label component 3 as “Performance”.
Here are the players and seasons that showed the highest levels of Quarterback “Usage”:
Code
Here are the players and seasons that showed the lowest levels of Quarterback “Usage”:
Code
Here are the players and seasons that showed the highest levels of Quarterback “Aggressiveness”:
Code
Here are the players and seasons that showed the lowest levels of Quarterback “Aggressiveness”:
Code
Here are the players and seasons that showed the highest levels of Quarterback “Performance”:
Code
If we restrict it to Quarterbacks who played at least 10 games in the season, here are the players and seasons that showed the highest levels of Quarterback “Performance”:
Code
Here are the players and seasons that showed the lowest levels of Quarterback “Performance”:
Code
If we restrict it to Quarterbacks who played at least 10 games in the season, here are the players and seasons that showed the lowest levels of Quarterback “Performance”:
22.7 Example of Confirmatory Factor Analysis
We fit CFA models using the lavaan::cfa() function of the lavaan package (Rosseel, 2012; Rosseel et al., 2024). We compare one-, two-, and three-factor CFA models.
Below is the syntax for the one-factor CFA model:
Code
cfa1factor_syntax <- '
#Factor loadings
F1 =~ completionsPerGame + attemptsPerGame + passing_yardsPerGame + passing_tdsPerGame +
passing_air_yardsPerGame + passing_yards_after_catchPerGame + passing_first_downsPerGame +
avg_completed_air_yards + avg_intended_air_yards + aggressiveness + max_completed_air_distance +
avg_air_distance + max_air_distance + avg_air_yards_to_sticks + passing_cpoe + pass_comp_pct +
passer_rating + completion_percentage_above_expectation
'lavaan 0.7-2 ended normally after 162 iterations
Estimator ML
Optimization method NLMINB
Number of model parameters 54
Row rank of the constraints matrix 36
Number of observations 2078
Number of clusters [player_id] 411
Number of missing patterns 4
Model Test User Model:
Standard Scaled
Test Statistic 17935.100 9411.859
Degrees of freedom 135 135
P-value (Chi-square) 0.000 0.000
Scaling correction factor 1.906
Yuan-Bentler correction (Mplus variant)
Model Test Baseline Model:
Test statistic 45294.523 23236.315
Degrees of freedom 153 153
P-value 0.000 0.000
Scaling correction factor 1.949
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.606 0.598
Tucker-Lewis Index (TLI) 0.553 0.545
Robust Comparative Fit Index (CFI) 0.231
Robust Tucker-Lewis Index (TLI) 0.128
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -65895.925 -65895.925
Scaling correction factor 2.635
for the MLR correction
Loglikelihood unrestricted model (H1) -56928.375 -56928.375
Scaling correction factor 2.114
for the MLR correction
Akaike (AIC) 131899.850 131899.850
Bayesian (BIC) 132204.365 132204.365
Sample-size adjusted Bayesian (SABIC) 132032.802 132032.802
Root Mean Square Error of Approximation:
RMSEA 0.252 0.182
90 Percent confidence interval - lower 0.249 0.180
90 Percent confidence interval - upper 0.255 0.184
P-value H_0: RMSEA <= 0.050 0.000 0.000
P-value H_0: RMSEA >= 0.080 1.000 1.000
Robust RMSEA 0.556
90 Percent confidence interval - lower 0.539
90 Percent confidence interval - upper 0.573
P-value H_0: Robust RMSEA <= 0.050 0.000
P-value H_0: Robust RMSEA >= 0.080 1.000
Standardized Root Mean Square Residual:
SRMR 0.377 0.377
Goodness of Fit Index:
Goodness of Fit Index (GFI) 0.512
90 Percent confidence interval - lower 0.506
90 Percent confidence interval - upper 0.519
Robust GFI 0.177
90 Percent confidence interval - lower 0.169
90 Percent confidence interval - upper 0.186
Parameter Estimates:
Standard errors Robust.cluster
Information Observed
Observed information based on Hessian
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 =~
completinsPrGm 7.894 0.120 65.700 0.000 7.894 0.986
attemptsPerGam 12.192 0.167 73.191 0.000 12.192 0.973
pssng_yrdsPrGm 92.154 1.447 63.669 0.000 92.154 0.994
passng_tdsPrGm 0.597 0.018 32.457 0.000 0.597 0.867
pssng_r_yrdsPG 83.787 3.514 23.847 0.000 83.787 0.698
pssng_yrds__PG 50.219 1.493 33.636 0.000 50.219 0.774
pssng_frst_dPG 4.496 0.077 58.026 0.000 4.496 0.992
avg_cmpltd_r_y 0.486 0.098 4.962 0.000 0.486 0.321
avg_ntndd_r_yr 0.273 0.104 2.638 0.008 0.273 0.192
aggressiveness -0.212 0.437 -0.485 0.628 -0.212 -0.042
mx_cmpltd_r_ds 3.012 0.375 8.041 0.000 3.012 0.508
avg_air_distnc 0.077 0.110 0.694 0.488 0.077 0.053
max_air_distnc 2.098 0.410 5.119 0.000 2.098 0.392
avg_r_yrds_t_s 0.378 0.108 3.489 0.000 0.378 0.251
passing_cpoe 3.501 0.507 6.902 0.000 3.501 0.296
pass_comp_pct 0.038 0.005 7.560 0.000 0.038 0.292
passer_rating 11.627 1.156 10.059 0.000 11.627 0.621
cmpltn_prcnt__ 2.865 0.399 7.190 0.000 2.865 0.502
Intercepts:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 13.241 0.384 34.439 0.000 13.241 1.654
.attemptsPerGam 21.833 0.570 38.276 0.000 21.833 1.743
.pssng_yrdsPrGm 147.988 4.539 32.604 0.000 147.988 1.597
.passng_tdsPrGm 0.849 0.035 24.185 0.000 0.849 1.231
.pssng_r_yrdsPG 134.335 5.653 23.764 0.000 134.335 1.119
.pssng_yrds__PG 86.971 2.908 29.910 0.000 86.971 1.340
.pssng_frst_dPG 7.143 0.224 31.925 0.000 7.143 1.576
.avg_cmpltd_r_y 5.484 0.097 56.251 0.000 5.484 3.615
.avg_ntndd_r_yr 7.865 0.104 75.300 0.000 7.865 5.535
.aggressiveness 16.556 0.379 43.719 0.000 16.556 3.287
.mx_cmpltd_r_ds 37.694 0.391 96.429 0.000 37.694 6.359
.avg_air_distnc 21.149 0.104 204.079 0.000 21.149 14.529
.max_air_distnc 46.534 0.403 115.348 0.000 46.534 8.690
.avg_r_yrds_t_s -1.139 0.109 -10.412 0.000 -1.139 -0.757
.passing_cpoe -2.740 0.421 -6.515 0.000 -2.740 -0.231
.pass_comp_pct 0.590 0.004 145.517 0.000 0.590 4.504
.passer_rating 80.208 1.188 67.538 0.000 80.208 4.283
.cmpltn_prcnt__ -2.238 0.394 -5.673 0.000 -2.238 -0.392
Variances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 1.796 0.134 13.440 0.000 1.796 0.028
.attemptsPerGam 8.238 0.473 17.421 0.000 8.238 0.053
.pssng_yrdsPrGm 95.749 8.719 10.982 0.000 95.749 0.011
.passng_tdsPrGm 0.118 0.007 17.672 0.000 0.118 0.249
.pssng_r_yrdsPG 7397.699 512.389 14.438 0.000 7397.699 0.513
.pssng_yrds__PG 1692.683 129.397 13.081 0.000 1692.683 0.402
.pssng_frst_dPG 0.334 0.024 13.736 0.000 0.334 0.016
.avg_cmpltd_r_y 2.064 0.182 11.362 0.000 2.064 0.897
.avg_ntndd_r_yr 1.944 0.167 11.668 0.000 1.944 0.963
.aggressiveness 25.333 3.034 8.349 0.000 25.333 0.998
.mx_cmpltd_r_ds 26.062 2.590 10.063 0.000 26.062 0.742
.avg_air_distnc 2.113 0.168 12.587 0.000 2.113 0.997
.max_air_distnc 24.273 2.291 10.596 0.000 24.273 0.847
.avg_r_yrds_t_s 2.121 0.199 10.631 0.000 2.121 0.937
.passing_cpoe 128.080 13.460 9.516 0.000 128.080 0.913
.pass_comp_pct 0.016 0.001 11.804 0.000 0.016 0.915
.passer_rating 215.507 22.310 9.660 0.000 215.507 0.615
.cmpltn_prcnt__ 24.304 2.583 9.409 0.000 24.304 0.748
F1 1.000 1.000 1.000
R-Square:
Estimate
completinsPrGm 0.972
attemptsPerGam 0.947
pssng_yrdsPrGm 0.989
passng_tdsPrGm 0.751
pssng_r_yrdsPG 0.487
pssng_yrds__PG 0.598
pssng_frst_dPG 0.984
avg_cmpltd_r_y 0.103
avg_ntndd_r_yr 0.037
aggressiveness 0.002
mx_cmpltd_r_ds 0.258
avg_air_distnc 0.003
max_air_distnc 0.153
avg_r_yrds_t_s 0.063
passing_cpoe 0.087
pass_comp_pct 0.085
passer_rating 0.385
cmpltn_prcnt__ 0.252
The one-factor model did not fit well according to the fit indices:
Code
chisq df pvalue
17935.100 135.000 0.000
chisq.scaled df.scaled pvalue.scaled
9411.859 135.000 0.000
chisq.scaling.factor baseline.chisq baseline.df
1.906 45294.523 153.000
baseline.pvalue rmsea cfi
0.000 0.252 0.606
tli srmr rmsea.robust
0.553 0.377 0.556
cfi.robust tli.robust
0.231 0.128
$type
[1] "cor.bollen"
$cov
cmplPG attmPG pssng_yPG pssng_tPG
completionsPerGame 0.000
attemptsPerGame 0.025 0.000
passing_yardsPerGame -0.003 -0.003 0.000
passing_tdsPerGame -0.026 -0.050 0.010 0.000
passing_air_yardsPerGame 0.012 0.010 0.003 -0.020
passing_yards_after_catchPerGame -0.008 0.014 0.009 0.003
passing_first_downsPerGame -0.001 -0.006 0.001 0.013
avg_completed_air_yards -0.396 -0.419 -0.361 -0.276
avg_intended_air_yards -0.323 -0.345 -0.303 -0.239
aggressiveness -0.129 -0.115 -0.126 -0.110
max_completed_air_distance -0.216 -0.233 -0.185 -0.112
avg_air_distance -0.221 -0.240 -0.201 -0.152
max_air_distance -0.219 -0.237 -0.215 -0.170
avg_air_yards_to_sticks -0.359 -0.380 -0.337 -0.257
passing_cpoe -0.090 -0.174 -0.096 -0.034
pass_comp_pct -0.001 -0.087 -0.021 0.022
passer_rating -0.255 -0.333 -0.233 -0.069
completion_percentage_above_expectation -0.328 -0.413 -0.341 -0.252
pssng_r_PG p___PG pssng_f_PG avg_c__
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame 0.000
passing_yards_after_catchPerGame -0.424 0.000
passing_first_downsPerGame 0.000 -0.003 0.000
avg_completed_air_yards 0.355 -0.855 -0.364 0.000
avg_intended_air_yards 0.437 -0.807 -0.304 0.883
aggressiveness 0.327 -0.451 -0.113 0.609
max_completed_air_distance 0.285 -0.535 -0.213 0.537
avg_air_distance 0.481 -0.698 -0.206 0.892
max_air_distance 0.399 -0.629 -0.228 0.604
avg_air_yards_to_sticks 0.393 -0.816 -0.333 0.856
passing_cpoe 0.167 -0.330 -0.087 0.276
pass_comp_pct -0.029 -0.056 -0.010 -0.121
passer_rating -0.142 -0.260 -0.228 -0.073
completion_percentage_above_expectation 0.073 -0.591 -0.329 0.324
avg_n__ aggrss mx_c__ avg_r_ mx_r_d
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards 0.000
aggressiveness 0.660 0.000
max_completed_air_distance 0.533 0.358 0.000
avg_air_distance 0.962 0.633 0.608 0.000
max_air_distance 0.715 0.471 0.571 0.762 0.000
avg_air_yards_to_sticks 0.936 0.660 0.516 0.946 0.678
passing_cpoe 0.242 -0.074 0.258 0.264 0.218
pass_comp_pct -0.182 -0.361 -0.010 -0.162 -0.101
passer_rating -0.109 -0.257 -0.002 -0.043 -0.102
completion_percentage_above_expectation 0.321 0.106 0.211 0.368 0.223
av____ pssng_ pss_c_ pssr_r cmp___
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards
aggressiveness
max_completed_air_distance
avg_air_distance
max_air_distance
avg_air_yards_to_sticks 0.000
passing_cpoe 0.222 0.000
pass_comp_pct -0.206 0.780 0.000
passer_rating -0.139 0.663 0.704 0.000
completion_percentage_above_expectation 0.284 0.810 0.643 0.470 0.000
$mean
completionsPerGame attemptsPerGame
0.000 0.000
passing_yardsPerGame passing_tdsPerGame
0.000 0.000
passing_air_yardsPerGame passing_yards_after_catchPerGame
0.000 0.000
passing_first_downsPerGame avg_completed_air_yards
0.000 -0.270
avg_intended_air_yards aggressiveness
-0.338 -0.166
max_completed_air_distance avg_air_distance
-0.205 -0.321
max_air_distance avg_air_yards_to_sticks
-0.349 -0.306
passing_cpoe pass_comp_pct
-0.220 0.013
passer_rating completion_percentage_above_expectation
-0.061 -0.274
Below are factor scores from the model for the first six players:
F1
[1,] -0.06075553
[2,] 0.16538125
[3,] 0.41828466
[4,] 0.12172056
[5,] 0.89018664
[6,] -0.07266684
The path diagram of the one-factor CFA model is in Figure 22.20.
Below is the syntax for the two-factor CFA model:
Code
cfa2factor_syntax <- '
#Factor loadings
F1 =~ completionsPerGame + attemptsPerGame + passing_yardsPerGame + passing_tdsPerGame +
passing_air_yardsPerGame + passing_yards_after_catchPerGame + passing_first_downsPerGame
F2 =~ avg_completed_air_yards + avg_intended_air_yards + aggressiveness + max_completed_air_distance +
avg_air_distance + max_air_distance + avg_air_yards_to_sticks + passing_cpoe + pass_comp_pct +
passer_rating + completion_percentage_above_expectation
'lavaan 0.7-2 ended normally after 4303 iterations
Estimator ML
Optimization method NLMINB
Number of model parameters 55
Row rank of the constraints matrix 37
Number of observations 2078
Number of clusters [player_id] 411
Number of missing patterns 4
Model Test User Model:
Standard Scaled
Test Statistic 14355.318 7350.061
Degrees of freedom 134 134
P-value (Chi-square) 0.000 0.000
Scaling correction factor 1.953
Yuan-Bentler correction (Mplus variant)
Model Test Baseline Model:
Test statistic 45294.523 23236.315
Degrees of freedom 153 153
P-value 0.000 0.000
Scaling correction factor 1.949
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.685 0.687
Tucker-Lewis Index (TLI) 0.640 0.643
Robust Comparative Fit Index (CFI) 0.516
Robust Tucker-Lewis Index (TLI) 0.447
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -64106.034 -64106.034
Scaling correction factor 2.506
for the MLR correction
Loglikelihood unrestricted model (H1) -56928.375 -56928.375
Scaling correction factor 2.114
for the MLR correction
Akaike (AIC) 128322.067 128322.067
Bayesian (BIC) 128632.221 128632.221
Sample-size adjusted Bayesian (SABIC) 128457.481 128457.481
Root Mean Square Error of Approximation:
RMSEA 0.226 0.161
90 Percent confidence interval - lower 0.223 0.159
90 Percent confidence interval - upper 0.229 0.163
P-value H_0: RMSEA <= 0.050 0.000 0.000
P-value H_0: RMSEA >= 0.080 1.000 1.000
Robust RMSEA 0.443
90 Percent confidence interval - lower 0.426
90 Percent confidence interval - upper 0.460
P-value H_0: Robust RMSEA <= 0.050 0.000
P-value H_0: Robust RMSEA >= 0.080 1.000
Standardized Root Mean Square Residual:
SRMR 0.330 0.330
Goodness of Fit Index:
Goodness of Fit Index (GFI) 0.568
90 Percent confidence interval - lower 0.561
90 Percent confidence interval - upper 0.575
Robust GFI 0.255
90 Percent confidence interval - lower 0.241
90 Percent confidence interval - upper 0.270
Parameter Estimates:
Standard errors Robust.cluster
Information Observed
Observed information based on Hessian
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 =~
cmpltnsPG 7.884 0.119 66.406 0.000 7.884 0.987
attmptsPG 12.181 0.164 74.277 0.000 12.181 0.974
pssng_yPG 91.922 1.433 64.158 0.000 91.922 0.994
pssng_tPG 0.595 0.018 32.408 0.000 0.595 0.864
pssng__PG 83.633 3.497 23.918 0.000 83.633 0.697
pssn___PG 50.112 1.485 33.751 0.000 50.112 0.773
pssng__PG 4.486 0.077 58.358 0.000 4.486 0.992
F2 =~
avg_cmp__ (ub) 2.622 2.622 0.948
avg_ntn__ 3.158 0.075 41.977 0.000 3.158 0.995
aggrssvns 4.227 0.570 7.413 0.000 4.227 0.670
mx_cmpl__ 6.777 0.503 13.474 0.000 6.777 0.819
avg_r_dst 2.907 0.119 24.475 0.000 2.907 0.971
mx_r_dstn 6.266 0.562 11.141 0.000 6.266 0.818
avg_r_y__ 3.203 3.203 0.989
passng_cp 10.253 0.477 21.490 0.000 10.253 0.866
pss_cmp_p 0.107 0.005 21.945 0.000 0.107 0.833
pssr_rtng 8.095 2.436 3.323 0.001 8.095 0.435
cmpltn___ 2.589 0.741 3.495 0.000 2.589 0.441
Covariances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 ~~
F2 0.289 0.039 7.337 0.000 0.289 0.289
Intercepts:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 13.241 0.384 34.439 0.000 13.241 1.657
.attemptsPerGam 21.833 0.570 38.276 0.000 21.833 1.746
.pssng_yrdsPrGm 147.988 4.539 32.604 0.000 147.988 1.600
.passng_tdsPrGm 0.849 0.035 24.186 0.000 0.849 1.233
.pssng_r_yrdsPG 134.335 5.653 23.764 0.000 134.335 1.120
.pssng_yrds__PG 86.971 2.908 29.910 0.000 86.971 1.341
.pssng_frst_dPG 7.143 0.224 31.925 0.000 7.143 1.579
.avg_cmpltd_r_y 5.068 0.109 46.435 0.000 5.068 1.832
.avg_ntndd_r_yr 7.217 0.129 55.852 0.000 7.217 2.274
.aggressiveness 15.415 0.320 48.146 0.000 15.415 2.442
.mx_cmpltd_r_ds 37.449 0.372 100.626 0.000 37.449 4.526
.avg_air_distnc 20.469 0.130 157.182 0.000 20.469 6.836
.max_air_distnc 45.982 0.390 118.045 0.000 45.982 6.006
.avg_r_yrds_t_s -1.749 0.129 -13.580 0.000 -1.749 -0.540
.passing_cpoe -3.245 0.370 -8.766 0.000 -3.245 -0.274
.pass_comp_pct 0.591 0.004 147.451 0.000 0.591 4.591
.passer_rating 83.712 1.154 72.519 0.000 83.712 4.503
.cmpltn_prcnt__ -1.520 0.346 -4.389 0.000 -1.520 -0.259
Variances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 1.701 0.130 13.071 0.000 1.701 0.027
.attemptsPerGam 7.893 0.472 16.711 0.000 7.893 0.051
.pssng_yrdsPrGm 104.112 9.183 11.337 0.000 104.112 0.012
.passng_tdsPrGm 0.120 0.007 17.615 0.000 0.120 0.253
.pssng_r_yrdsPG 7395.095 511.943 14.445 0.000 7395.095 0.514
.pssng_yrds__PG 1693.213 129.597 13.065 0.000 1693.213 0.403
.pssng_frst_dPG 0.342 0.026 13.390 0.000 0.342 0.017
.avg_cmpltd_r_y 0.773 0.079 9.838 0.000 0.773 0.101
.avg_ntndd_r_yr 0.098 0.025 3.962 0.000 0.098 0.010
.aggressiveness 21.972 2.747 7.998 0.000 21.972 0.552
.mx_cmpltd_r_ds 22.531 1.950 11.553 0.000 22.531 0.329
.avg_air_distnc 0.516 0.043 12.022 0.000 0.516 0.058
.max_air_distnc 19.344 1.974 9.801 0.000 19.344 0.330
.avg_r_yrds_t_s 0.221 0.037 5.949 0.000 0.221 0.021
.passing_cpoe 35.080 3.162 11.093 0.000 35.080 0.250
.pass_comp_pct 0.005 0.000 11.862 0.000 0.005 0.306
.passer_rating 280.075 22.592 12.397 0.000 280.075 0.810
.cmpltn_prcnt__ 27.708 2.706 10.239 0.000 27.708 0.805
F1 1.000 1.000 1.000
F2 1.000 1.000 1.000
R-Square:
Estimate
completinsPrGm 0.973
attemptsPerGam 0.949
pssng_yrdsPrGm 0.988
passng_tdsPrGm 0.747
pssng_r_yrdsPG 0.486
pssng_yrds__PG 0.597
pssng_frst_dPG 0.983
avg_cmpltd_r_y 0.899
avg_ntndd_r_yr 0.990
aggressiveness 0.448
mx_cmpltd_r_ds 0.671
avg_air_distnc 0.942
max_air_distnc 0.670
avg_r_yrds_t_s 0.979
passing_cpoe 0.750
pass_comp_pct 0.694
passer_rating 0.190
cmpltn_prcnt__ 0.195
The two-factor model did not fit well according to the fit indices:
Code
chisq df pvalue
14355.318 134.000 0.000
chisq.scaled df.scaled pvalue.scaled
7350.061 134.000 0.000
chisq.scaling.factor baseline.chisq baseline.df
1.953 45294.523 153.000
baseline.pvalue rmsea cfi
0.000 0.226 0.685
tli srmr rmsea.robust
0.640 0.330 0.443
cfi.robust tli.robust
0.516 0.447
$type
[1] "cor.bollen"
$cov
cmplPG attmPG pssng_yPG pssng_tPG
completionsPerGame 0.000
attemptsPerGame 0.023 0.000
passing_yardsPerGame -0.003 -0.003 0.000
passing_tdsPerGame -0.024 -0.049 0.012 0.000
passing_air_yardsPerGame 0.013 0.010 0.004 -0.018
passing_yards_after_catchPerGame -0.008 0.014 0.010 0.005
passing_first_downsPerGame -0.002 -0.007 0.002 0.016
avg_completed_air_yards -0.351 -0.374 -0.315 -0.235
avg_intended_air_yards -0.417 -0.438 -0.398 -0.321
aggressiveness -0.362 -0.345 -0.360 -0.314
max_completed_air_distance 0.051 0.030 0.085 0.124
avg_air_distance -0.446 -0.463 -0.428 -0.350
max_air_distance -0.067 -0.086 -0.061 -0.035
avg_air_yards_to_sticks -0.393 -0.414 -0.371 -0.287
passing_cpoe -0.045 -0.131 -0.051 0.006
pass_comp_pct 0.049 -0.037 0.031 0.067
passer_rating 0.233 0.148 0.259 0.360
completion_percentage_above_expectation 0.041 -0.048 0.032 0.073
pssng_r_PG p___PG pssng_f_PG avg_c__
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame 0.000
passing_yards_after_catchPerGame -0.423 0.000
passing_first_downsPerGame 0.000 -0.002 0.000
avg_completed_air_yards 0.387 -0.819 -0.318 0.000
avg_intended_air_yards 0.371 -0.880 -0.398 0.001
aggressiveness 0.162 -0.633 -0.346 -0.039
max_completed_air_distance 0.475 -0.325 0.056 -0.076
avg_air_distance 0.322 -0.875 -0.432 -0.011
max_air_distance 0.508 -0.509 -0.075 -0.046
avg_air_yards_to_sticks 0.369 -0.843 -0.367 -0.001
passing_cpoe 0.199 -0.295 -0.042 -0.450
pass_comp_pct 0.007 -0.016 0.041 -0.817
passer_rating 0.204 0.123 0.263 -0.286
completion_percentage_above_expectation 0.335 -0.301 0.043 0.066
avg_n__ aggrss mx_c__ avg_r_ mx_r_d
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards 0.000
aggressiveness -0.015 0.000
max_completed_air_distance -0.185 -0.212 0.000
avg_air_distance 0.007 -0.020 -0.161 0.000
max_air_distance -0.024 -0.093 0.100 -0.012 0.000
avg_air_yards_to_sticks 0.000 -0.013 -0.167 -0.002 -0.033
passing_cpoe -0.563 -0.666 -0.302 -0.561 -0.375
pass_comp_pct -0.955 -0.931 -0.543 -0.955 -0.669
passer_rating -0.423 -0.574 -0.043 -0.433 -0.215
completion_percentage_above_expectation -0.022 -0.211 0.104 -0.034 0.059
av____ pssng_ pss_c_ pssr_r cmp___
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards
aggressiveness
max_completed_air_distance
avg_air_distance
max_air_distance
avg_air_yards_to_sticks 0.000
passing_cpoe -0.561 0.000
pass_comp_pct -0.957 0.145 0.000
passer_rating -0.413 0.469 0.522 0.000
completion_percentage_above_expectation -0.026 0.577 0.422 0.590 0.000
$mean
completionsPerGame attemptsPerGame
0.000 0.000
passing_yardsPerGame passing_tdsPerGame
0.000 0.000
passing_air_yardsPerGame passing_yards_after_catchPerGame
0.000 0.000
passing_first_downsPerGame avg_completed_air_yards
0.000 -0.112
avg_intended_air_yards aggressiveness
-0.144 0.009
max_completed_air_distance avg_air_distance
-0.169 -0.103
max_air_distance avg_air_yards_to_sticks
-0.276 -0.115
passing_cpoe pass_comp_pct
-0.182 0.009
passer_rating completion_percentage_above_expectation
-0.196 -0.338
Below are factor scores from the model for the first six players:
F1 F2
[1,] -0.06929933 0.7734179
[2,] 0.16494620 -0.1865603
[3,] 0.42729983 -0.3000272
[4,] 0.11364569 0.6632185
[5,] 0.89308841 0.1156339
[6,] -0.06839754 -0.4100497
The path diagram of the two-factor CFA model is in Figure 22.21.
Because the one-factor model is nested within the two-factor model, we can compare them using a chi-square difference test. The two factor model fit considerably better than the one-factor model in terms of a lower chi-square value:
Below is the syntax for the three-factor model:
Code
cfa3factor_syntax <- '
#Factor loadings
F1 =~ completionsPerGame + attemptsPerGame + passing_yardsPerGame + passing_tdsPerGame +
passing_air_yardsPerGame + passing_yards_after_catchPerGame + passing_first_downsPerGame
F2 =~ avg_completed_air_yards + avg_intended_air_yards + aggressiveness + max_completed_air_distance +
avg_air_distance + max_air_distance + avg_air_yards_to_sticks
F3 =~ passing_cpoe + pass_comp_pct + passer_rating + completion_percentage_above_expectation
'lavaan 0.7-2 ended normally after 352 iterations
Estimator ML
Optimization method NLMINB
Number of model parameters 57
Row rank of the constraints matrix 39
Number of observations 2078
Number of clusters [player_id] 411
Number of missing patterns 4
Model Test User Model:
Standard Scaled
Test Statistic 11508.846 6133.408
Degrees of freedom 132 132
P-value (Chi-square) 0.000 0.000
Scaling correction factor 1.876
Yuan-Bentler correction (Mplus variant)
Model Test Baseline Model:
Test statistic 45294.523 23236.315
Degrees of freedom 153 153
P-value 0.000 0.000
Scaling correction factor 1.949
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.748 0.740
Tucker-Lewis Index (TLI) 0.708 0.699
Robust Comparative Fit Index (CFI) 0.706
Robust Tucker-Lewis Index (TLI) 0.659
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -62682.798 -62682.798
Scaling correction factor 2.664
for the MLR correction
Loglikelihood unrestricted model (H1) -56928.375 -56928.375
Scaling correction factor 2.114
for the MLR correction
Akaike (AIC) 125479.595 125479.595
Bayesian (BIC) 125801.028 125801.028
Sample-size adjusted Bayesian (SABIC) 125619.933 125619.933
Root Mean Square Error of Approximation:
RMSEA 0.204 0.148
90 Percent confidence interval - lower 0.201 0.146
90 Percent confidence interval - upper 0.207 0.150
P-value H_0: RMSEA <= 0.050 0.000 0.000
P-value H_0: RMSEA >= 0.080 1.000 1.000
Robust RMSEA 0.348
90 Percent confidence interval - lower 0.331
90 Percent confidence interval - upper 0.365
P-value H_0: Robust RMSEA <= 0.050 0.000
P-value H_0: Robust RMSEA >= 0.080 1.000
Standardized Root Mean Square Residual:
SRMR 0.321 0.321
Goodness of Fit Index:
Goodness of Fit Index (GFI) 0.622
90 Percent confidence interval - lower 0.615
90 Percent confidence interval - upper 0.629
Robust GFI 0.361
90 Percent confidence interval - lower 0.338
90 Percent confidence interval - upper 0.384
Parameter Estimates:
Standard errors Robust.cluster
Information Observed
Observed information based on Hessian
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 =~
completinsPrGm 7.890 0.118 66.961 0.000 7.890 0.987
attemptsPerGam 12.190 0.164 74.430 0.000 12.190 0.974
pssng_yrdsPrGm 92.000 1.426 64.510 0.000 92.000 0.994
passng_tdsPrGm 0.596 0.018 32.600 0.000 0.596 0.865
pssng_r_yrdsPG 83.701 3.491 23.975 0.000 83.701 0.697
pssng_yrds__PG 50.152 1.490 33.663 0.000 50.152 0.773
pssng_frst_dPG 4.490 0.076 58.704 0.000 4.490 0.992
F2 =~
avg_cmpltd_r_y 1.191 0.077 15.509 0.000 1.191 0.797
avg_ntndd_r_yr 1.412 0.064 22.030 0.000 1.412 0.991
aggressiveness 1.904 0.273 6.983 0.000 1.904 0.378
mx_cmpltd_r_ds 2.796 0.266 10.507 0.000 2.796 0.499
avg_air_distnc 1.288 0.063 20.467 0.000 1.288 0.878
max_air_distnc 2.732 0.283 9.666 0.000 2.732 0.526
avg_r_yrds_t_s 1.410 0.077 18.222 0.000 1.410 0.941
F3 =~
passing_cpoe 12.032 0.467 25.764 0.000 12.032 0.995
pass_comp_pct 0.119 0.005 25.124 0.000 0.119 0.921
passer_rating 25.930 25.930 0.920
cmpltn_prcnt__ 9.985 0.500 19.966 0.000 9.985 0.967
Covariances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 ~~
F2 0.196 0.071 2.766 0.006 0.196 0.196
F3 0.306 0.037 8.259 0.000 0.306 0.306
F2 ~~
F3 0.139 0.121 1.155 0.248 0.139 0.139
Intercepts:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 13.241 0.384 34.439 0.000 13.241 1.656
.attemptsPerGam 21.833 0.570 38.276 0.000 21.833 1.745
.pssng_yrdsPrGm 147.988 4.539 32.604 0.000 147.988 1.599
.passng_tdsPrGm 0.849 0.035 24.186 0.000 0.849 1.232
.pssng_r_yrdsPG 134.335 5.653 23.764 0.000 134.335 1.119
.pssng_yrds__PG 86.971 2.908 29.910 0.000 86.971 1.341
.pssng_frst_dPG 7.143 0.224 31.925 0.000 7.143 1.578
.avg_cmpltd_r_y 5.597 0.097 57.684 0.000 5.597 3.747
.avg_ntndd_r_yr 7.856 0.106 74.176 0.000 7.856 5.511
.aggressiveness 16.269 0.275 59.074 0.000 16.269 3.226
.mx_cmpltd_r_ds 38.844 0.323 120.261 0.000 38.844 6.932
.avg_air_distnc 21.059 0.100 210.395 0.000 21.059 14.348
.max_air_distnc 47.257 0.328 144.158 0.000 47.257 9.103
.avg_r_yrds_t_s -1.099 0.108 -10.137 0.000 -1.099 -0.733
.passing_cpoe -3.481 0.372 -9.371 0.000 -3.481 -0.288
.pass_comp_pct 0.590 0.004 146.870 0.000 0.590 4.557
.passer_rating 80.451 0.965 83.389 0.000 80.451 2.855
.cmpltn_prcnt__ -2.906 0.341 -8.513 0.000 -2.906 -0.282
Variances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 1.708 0.131 13.073 0.000 1.708 0.027
.attemptsPerGam 7.922 0.472 16.778 0.000 7.922 0.051
.pssng_yrdsPrGm 103.549 9.158 11.307 0.000 103.549 0.012
.passng_tdsPrGm 0.120 0.007 17.617 0.000 0.120 0.252
.pssng_r_yrdsPG 7395.111 511.978 14.444 0.000 7395.111 0.514
.pssng_yrds__PG 1693.291 129.590 13.066 0.000 1693.291 0.402
.pssng_frst_dPG 0.341 0.025 13.418 0.000 0.341 0.017
.avg_cmpltd_r_y 0.813 0.084 9.621 0.000 0.813 0.364
.avg_ntndd_r_yr 0.037 0.020 1.847 0.065 0.037 0.018
.aggressiveness 21.813 2.655 8.217 0.000 21.813 0.857
.mx_cmpltd_r_ds 23.587 2.028 11.632 0.000 23.587 0.751
.avg_air_distnc 0.495 0.038 12.970 0.000 0.495 0.230
.max_air_distnc 19.482 1.974 9.870 0.000 19.482 0.723
.avg_r_yrds_t_s 0.258 0.041 6.297 0.000 0.258 0.115
.passing_cpoe 1.420 1.040 1.366 0.172 1.420 0.010
.pass_comp_pct 0.003 0.000 7.955 0.000 0.003 0.151
.passer_rating 121.493 15.971 7.607 0.000 121.493 0.153
.cmpltn_prcnt__ 6.822 1.069 6.381 0.000 6.822 0.064
F1 1.000 1.000 1.000
F2 1.000 1.000 1.000
F3 1.000 1.000 1.000
R-Square:
Estimate
completinsPrGm 0.973
attemptsPerGam 0.949
pssng_yrdsPrGm 0.988
passng_tdsPrGm 0.748
pssng_r_yrdsPG 0.486
pssng_yrds__PG 0.598
pssng_frst_dPG 0.983
avg_cmpltd_r_y 0.636
avg_ntndd_r_yr 0.982
aggressiveness 0.143
mx_cmpltd_r_ds 0.249
avg_air_distnc 0.770
max_air_distnc 0.277
avg_r_yrds_t_s 0.885
passing_cpoe 0.990
pass_comp_pct 0.849
passer_rating 0.847
cmpltn_prcnt__ 0.936
The three-factor model did not fit well according to fit indices:
Code
chisq df pvalue
11508.846 132.000 0.000
chisq.scaled df.scaled pvalue.scaled
6133.408 132.000 0.000
chisq.scaling.factor baseline.chisq baseline.df
1.876 45294.523 153.000
baseline.pvalue rmsea cfi
0.000 0.204 0.748
tli srmr rmsea.robust
0.708 0.321 0.348
cfi.robust tli.robust
0.706 0.659
However, we know that the three-factor model fit improved considerably when accounting for correlated residuals. So, we plan to examine modification indices to see if we can account for covariances between variables that were not explained by their underlying latent factors.
$type
[1] "cor.bollen"
$cov
cmplPG attmPG pssng_yPG pssng_tPG
completionsPerGame 0.000
attemptsPerGame 0.023 0.000
passing_yardsPerGame -0.003 -0.003 0.000
passing_tdsPerGame -0.024 -0.049 0.012 0.000
passing_air_yardsPerGame 0.012 0.010 0.004 -0.019
passing_yards_after_catchPerGame -0.008 0.014 0.010 0.005
passing_first_downsPerGame -0.002 -0.007 0.002 0.015
avg_completed_air_yards -0.235 -0.260 -0.198 -0.133
avg_intended_air_yards -0.325 -0.347 -0.306 -0.240
aggressiveness -0.244 -0.228 -0.241 -0.210
max_completed_air_distance 0.188 0.166 0.223 0.244
avg_air_distance -0.339 -0.357 -0.320 -0.256
max_air_distance 0.065 0.043 0.071 0.080
avg_air_yards_to_sticks -0.293 -0.316 -0.271 -0.199
passing_cpoe -0.098 -0.183 -0.105 -0.041
pass_comp_pct 0.009 -0.077 -0.010 0.031
passer_rating 0.079 -0.003 0.105 0.226
completion_percentage_above_expectation -0.125 -0.212 -0.135 -0.073
pssng_r_PG p___PG pssng_f_PG avg_c__
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame 0.000
passing_yards_after_catchPerGame -0.423 0.000
passing_first_downsPerGame 0.000 -0.002 0.000
avg_completed_air_yards 0.469 -0.729 -0.202 0.000
avg_intended_air_yards 0.436 -0.808 -0.306 0.155
aggressiveness 0.246 -0.541 -0.228 0.294
max_completed_air_distance 0.572 -0.218 0.194 0.302
avg_air_distance 0.398 -0.791 -0.325 0.209
max_air_distance 0.601 -0.406 0.058 0.310
avg_air_yards_to_sticks 0.440 -0.765 -0.266 0.187
passing_cpoe 0.161 -0.336 -0.096 0.260
pass_comp_pct -0.022 -0.047 0.000 -0.130
passer_rating 0.095 0.002 0.108 0.024
completion_percentage_above_expectation 0.218 -0.431 -0.124 0.377
avg_n__ aggrss mx_c__ avg_r_ mx_r_d
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards 0.000
aggressiveness 0.278 0.000
max_completed_air_distance 0.136 0.148 0.000
avg_air_distance 0.103 0.299 0.196 0.000
max_air_distance 0.269 0.256 0.507 0.321 0.000
avg_air_yards_to_sticks 0.053 0.294 0.174 0.133 0.282
passing_cpoe 0.162 -0.139 0.338 0.158 0.261
pass_comp_pct -0.253 -0.421 0.075 -0.259 -0.054
passer_rating -0.117 -0.331 0.250 -0.123 0.074
completion_percentage_above_expectation 0.284 0.034 0.399 0.277 0.349
av____ pssng_ pss_c_ pssr_r cmp___
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards
aggressiveness
max_completed_air_distance
avg_air_distance
max_air_distance
avg_air_yards_to_sticks 0.000
passing_cpoe 0.165 0.000
pass_comp_pct -0.253 -0.050 0.000
passer_rating -0.103 -0.070 0.037 0.000
completion_percentage_above_expectation 0.284 -0.004 -0.102 -0.108 0.000
$mean
completionsPerGame attemptsPerGame
0.000 0.000
passing_yardsPerGame passing_tdsPerGame
0.000 0.000
passing_air_yardsPerGame passing_yards_after_catchPerGame
0.000 0.000
passing_first_downsPerGame avg_completed_air_yards
0.000 -0.313
avg_intended_air_yards aggressiveness
-0.335 -0.122
max_completed_air_distance avg_air_distance
-0.373 -0.292
max_air_distance avg_air_yards_to_sticks
-0.444 -0.318
passing_cpoe pass_comp_pct
-0.164 0.012
passer_rating completion_percentage_above_expectation
-0.070 -0.215
Below are the modification indices, suggesting potential model modifications that would improve model fit such as correlated residuals and cross-loadings.
Below are factor scores from the model for the first six players:
F1 F2 F3
[1,] -0.06823463 0.064247007 0.8665671
[2,] 0.16471197 0.009245551 -0.2137862
[3,] 0.42627152 0.041341533 -0.3542607
[4,] 0.11409350 0.075841072 0.6454550
[5,] 0.89250979 0.171978399 0.2344557
[6,] -0.06871879 -0.050261431 -0.4411253
The path diagram of the three-factor CFA model is in Figure 22.22.
Because the two-factor model is nested within the three-factor model, we can compare them using a chi-square difference test. The three factor model fit considerably better than the two-factor model in terms of a lower chi-square value:
Based on the model modifications suggested by the modification indices for the three-factor model, we modify the model to account for correlated residuals, using the syntax below:
Code
cfa3factorModified_syntax <- '
# Factor loadings
F1 =~ NA*completionsPerGame + attemptsPerGame + passing_yardsPerGame + passing_tdsPerGame +
passing_air_yardsPerGame + passing_yards_after_catchPerGame + passing_first_downsPerGame
F2 =~ NA*avg_completed_air_yards + avg_intended_air_yards + aggressiveness + max_completed_air_distance +
avg_air_distance + max_air_distance + avg_air_yards_to_sticks
F3 =~ NA*passing_cpoe + pass_comp_pct + passer_rating + completion_percentage_above_expectation
# Cross loadings
#F3 =~ attemptsPerGame
# Correlated residuals
passing_air_yardsPerGame ~~ passing_yards_after_catchPerGame
completionsPerGame ~~ attemptsPerGame
attemptsPerGame ~~ passing_yards_after_catchPerGame
passing_yards_after_catchPerGame ~~ passing_first_downsPerGame
completionsPerGame ~~ passing_first_downsPerGame
attemptsPerGame ~~ passing_tdsPerGame
completionsPerGame ~~ passing_tdsPerGame
passing_tdsPerGame ~~ passing_air_yardsPerGame
max_completed_air_distance ~~ max_air_distance
avg_completed_air_yards ~~ max_completed_air_distance
avg_completed_air_yards ~~ avg_air_distance
avg_completed_air_yards ~~ max_air_distance
avg_intended_air_yards ~~ max_completed_air_distance
completionsPerGame ~~ pass_comp_pct
passing_yardsPerGame ~~ avg_completed_air_yards
passing_yardsPerGame ~~ max_completed_air_distance
passing_tdsPerGame ~~ passer_rating
aggressiveness ~~ completion_percentage_above_expectation
# Variances
F1 ~~ 1*F1
F2 ~~ 1*F2
'lavaan 0.7-2 ended normally after 3681 iterations
Estimator ML
Optimization method NLMINB
Number of model parameters 76
Row rank of the constraints matrix 40
Number of observations 2078
Number of clusters [player_id] 411
Number of missing patterns 4
Model Test User Model:
Standard Scaled
Test Statistic 2414.652 1307.899
Degrees of freedom 113 113
P-value (Chi-square) 0.000 0.000
Scaling correction factor 1.846
Yuan-Bentler correction (Mplus variant)
Model Test Baseline Model:
Test statistic 45294.523 23236.315
Degrees of freedom 153 153
P-value 0.000 0.000
Scaling correction factor 1.949
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.949 0.948
Tucker-Lewis Index (TLI) 0.931 0.930
Robust Comparative Fit Index (CFI) NA
Robust Tucker-Lewis Index (TLI) NA
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -58135.701 -58135.701
Scaling correction factor 2.512
for the MLR correction
Loglikelihood unrestricted model (H1) -56928.375 -56928.375
Scaling correction factor 2.114
for the MLR correction
Akaike (AIC) 116423.402 116423.402
Bayesian (BIC) 116851.978 116851.978
Sample-size adjusted Bayesian (SABIC) 116610.520 116610.520
Root Mean Square Error of Approximation:
RMSEA 0.099 0.071
90 Percent confidence interval - lower 0.096 0.069
90 Percent confidence interval - upper 0.102 0.074
P-value H_0: RMSEA <= 0.050 0.000 0.000
P-value H_0: RMSEA >= 0.080 1.000 0.000
Robust RMSEA NA
90 Percent confidence interval - lower NA
90 Percent confidence interval - upper NA
P-value H_0: Robust RMSEA <= 0.050 NA
P-value H_0: Robust RMSEA >= 0.080 NA
Standardized Root Mean Square Residual:
SRMR 0.319 0.319
Goodness of Fit Index:
Goodness of Fit Index (GFI) 0.890
90 Percent confidence interval - lower 0.884
90 Percent confidence interval - upper 0.897
Robust GFI NA
90 Percent confidence interval - lower NA
90 Percent confidence interval - upper NA
Parameter Estimates:
Standard errors Robust.cluster
Information Observed
Observed information based on Hessian
Latent Variables:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 =~
completinsPrGm 7.850 0.120 65.246 0.000 7.850 0.981
attemptsPerGam 12.133 0.167 72.436 0.000 12.133 0.968
pssng_yrdsPrGm 91.974 1.429 64.358 0.000 91.974 0.995
passng_tdsPrGm 0.595 0.018 33.358 0.000 0.595 0.874
pssng_r_yrdsPG 84.316 3.542 23.807 0.000 84.316 0.701
pssng_yrds__PG 50.703 1.503 33.739 0.000 50.703 0.780
pssng_frst_dPG 4.491 0.078 57.814 0.000 4.491 0.991
F2 =~
avg_cmpltd_r_y 1.165 0.070 16.617 0.000 1.165 0.817
avg_ntndd_r_yr 1.416 0.065 21.620 0.000 1.416 0.986
aggressiveness 1.658 0.272 6.094 0.000 1.658 0.334
mx_cmpltd_r_ds 2.895 0.267 10.848 0.000 2.895 0.518
avg_air_distnc 1.301 0.064 20.463 0.000 1.301 0.881
max_air_distnc 2.764 0.286 9.662 0.000 2.764 0.531
avg_r_yrds_t_s 1.429 0.079 18.199 0.000 1.429 0.947
F3 =~
passing_cpoe 7.323 2.054 3.566 0.000 12.128 0.992
pass_comp_pct 0.069 0.019 3.657 0.000 0.114 0.913
passer_rating 16.475 5.007 3.290 0.001 27.285 0.933
cmpltn_prcnt__ 6.521 1.760 3.704 0.000 10.799 0.974
Covariances:
Estimate Std.Err z-value P(>|z|)
.passing_air_yardsPerGame ~~
.pssng_yrds__PG -3404.760 253.438 -13.434 0.000
.completionsPerGame ~~
.attemptsPerGam 3.554 0.187 19.021 0.000
.attemptsPerGame ~~
.pssng_yrds__PG 15.273 0.990 15.421 0.000
.passing_yards_after_catchPerGame ~~
.pssng_frst_dPG -3.551 0.280 -12.669 0.000
.completionsPerGame ~~
.pssng_frst_dPG 0.121 0.026 4.717 0.000
.attemptsPerGame ~~
.passng_tdsPrGm -0.430 0.034 -12.760 0.000
.completionsPerGame ~~
.passng_tdsPrGm -0.148 0.014 -10.406 0.000
.passing_tdsPerGame ~~
.pssng_r_yrdsPG -2.840 0.292 -9.724 0.000
.max_completed_air_distance ~~
.max_air_distnc 10.807 1.375 7.862 0.000
.avg_completed_air_yards ~~
.mx_cmpltd_r_ds 1.205 0.230 5.232 0.000
.avg_air_distnc -0.104 0.024 -4.343 0.000
.max_air_distnc -0.425 0.153 -2.780 0.005
.avg_intended_air_yards ~~
.mx_cmpltd_r_ds -0.449 0.130 -3.448 0.001
.completionsPerGame ~~
.pass_comp_pct 0.023 0.002 14.505 0.000
.passing_yardsPerGame ~~
.avg_cmpltd_r_y 4.962 0.321 15.480 0.000
.mx_cmpltd_r_ds 16.340 1.406 11.626 0.000
.passing_tdsPerGame ~~
.passer_rating 1.313 0.131 10.049 0.000
.aggressiveness ~~
.cmpltn_prcnt__ 5.767 0.930 6.200 0.000
F1 ~~
F2 0.262 0.067 3.929 0.000
F3 0.521 0.158 3.292 0.001
F2 ~~
F3 0.280 0.214 1.309 0.191
Std.lv Std.all
-3404.760 -0.974
3.554 0.730
15.273 0.119
-3.551 -0.143
0.121 0.129
-0.430 -0.412
-0.148 -0.290
-2.840 -0.100
10.807 0.512
1.205 0.307
-0.104 -0.181
-0.425 -0.117
-0.449 -0.387
0.023 0.291
4.962 0.684
16.340 0.386
1.313 0.377
5.767 0.488
0.262 0.262
0.315 0.315
0.169 0.169
Intercepts:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
.completinsPrGm 13.241 0.384 34.439 0.000 13.241 1.655
.attemptsPerGam 21.833 0.570 38.276 0.000 21.833 1.742
.pssng_yrdsPrGm 147.988 4.539 32.604 0.000 147.988 1.602
.passng_tdsPrGm 0.849 0.035 24.186 0.000 0.849 1.246
.pssng_r_yrdsPG 134.335 5.653 23.764 0.000 134.335 1.116
.pssng_yrds__PG 86.971 2.908 29.910 0.000 86.971 1.338
.pssng_frst_dPG 7.143 0.224 31.925 0.000 7.143 1.576
.avg_cmpltd_r_y 5.577 0.090 61.723 0.000 5.577 3.915
.avg_ntndd_r_yr 7.816 0.103 76.221 0.000 7.816 5.442
.aggressiveness 16.247 0.273 59.474 0.000 16.247 3.273
.mx_cmpltd_r_ds 38.788 0.321 120.923 0.000 38.788 6.932
.avg_air_distnc 21.021 0.097 215.909 0.000 21.021 14.236
.max_air_distnc 47.177 0.326 144.687 0.000 47.177 9.068
.avg_r_yrds_t_s -1.140 0.106 -10.806 0.000 -1.140 -0.755
.passing_cpoe -3.269 0.371 -8.805 0.000 -3.269 -0.268
.pass_comp_pct 0.590 0.004 146.976 0.000 0.590 4.711
.passer_rating 80.773 1.076 75.063 0.000 80.773 2.762
.cmpltn_prcnt__ -2.850 0.356 -8.008 0.000 -2.850 -0.257
Variances:
Estimate Std.Err z-value P(>|z|) Std.lv Std.all
F1 1.000 1.000 1.000
F2 1.000 1.000 1.000
.completinsPrGm 2.383 0.115 20.729 0.000 2.383 0.037
.attemptsPerGam 9.948 0.440 22.619 0.000 9.948 0.063
.pssng_yrdsPrGm 78.287 6.381 12.269 0.000 78.287 0.009
.passng_tdsPrGm 0.110 0.006 17.368 0.000 0.110 0.237
.pssng_r_yrdsPG 7378.515 517.714 14.252 0.000 7378.515 0.509
.pssng_yrds__PG 1656.455 125.333 13.216 0.000 1656.455 0.392
.pssng_frst_dPG 0.372 0.028 13.109 0.000 0.372 0.018
.avg_cmpltd_r_y 0.673 0.065 10.438 0.000 0.673 0.332
.avg_ntndd_r_yr 0.059 0.019 3.162 0.002 0.059 0.028
.aggressiveness 21.887 2.683 8.157 0.000 21.887 0.888
.mx_cmpltd_r_ds 22.921 1.809 12.673 0.000 22.921 0.732
.avg_air_distnc 0.489 0.039 12.490 0.000 0.489 0.224
.max_air_distnc 19.430 1.969 9.867 0.000 19.430 0.718
.avg_r_yrds_t_s 0.236 0.035 6.670 0.000 0.236 0.104
.passing_cpoe 2.249 0.790 2.849 0.004 2.249 0.015
.pass_comp_pct 0.003 0.000 7.927 0.000 0.003 0.166
.passer_rating 110.606 14.821 7.463 0.000 110.606 0.129
.cmpltn_prcnt__ 6.380 0.982 6.498 0.000 6.380 0.052
F3 2.743 1.585 1.731 0.083 1.000 1.000
R-Square:
Estimate
completinsPrGm 0.963
attemptsPerGam 0.937
pssng_yrdsPrGm 0.991
passng_tdsPrGm 0.763
pssng_r_yrdsPG 0.491
pssng_yrds__PG 0.608
pssng_frst_dPG 0.982
avg_cmpltd_r_y 0.668
avg_ntndd_r_yr 0.972
aggressiveness 0.112
mx_cmpltd_r_ds 0.268
avg_air_distnc 0.776
max_air_distnc 0.282
avg_r_yrds_t_s 0.896
passing_cpoe 0.985
pass_comp_pct 0.834
passer_rating 0.871
cmpltn_prcnt__ 0.948
The three-factor model with correlated residuals fit was acceptable according to CFI and TLI. The model fit was subpar for RMSEA and SRMR.
Code
chisq df pvalue
2414.652 113.000 0.000
chisq.scaled df.scaled pvalue.scaled
1307.899 113.000 0.000
chisq.scaling.factor baseline.chisq baseline.df
1.846 45294.523 153.000
baseline.pvalue rmsea cfi
0.000 0.099 0.949
tli srmr rmsea.robust
0.931 0.319 NA
cfi.robust tli.robust
NA NA
$type
[1] "cor.bollen"
$cov
cmplPG attmPG pssng_yPG pssng_tPG
completionsPerGame 0.000
attemptsPerGame -0.001 0.000
passing_yardsPerGame 0.001 0.002 0.000
passing_tdsPerGame -0.001 -0.002 0.002 0.000
passing_air_yardsPerGame 0.013 0.011 0.000 0.007
passing_yards_after_catchPerGame -0.011 -0.006 0.002 -0.008
passing_first_downsPerGame 0.001 0.000 0.001 0.007
avg_completed_air_yards -0.291 -0.315 -0.293 -0.185
avg_intended_air_yards -0.387 -0.408 -0.369 -0.298
aggressiveness -0.257 -0.241 -0.255 -0.223
max_completed_air_distance 0.151 0.130 0.153 0.210
avg_air_distance -0.396 -0.413 -0.378 -0.309
max_air_distance 0.030 0.010 0.036 0.048
avg_air_yards_to_sticks -0.354 -0.376 -0.334 -0.256
passing_cpoe -0.105 -0.189 -0.114 -0.051
pass_comp_pct -0.018 -0.081 -0.016 0.024
passer_rating 0.069 -0.013 0.092 0.147
completion_percentage_above_expectation -0.134 -0.220 -0.146 -0.085
pssng_r_PG p___PG pssng_f_PG avg_c__
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame 0.000
passing_yards_after_catchPerGame 0.005 0.000
passing_first_downsPerGame -0.002 0.004 0.000
avg_completed_air_yards 0.428 -0.775 -0.259 0.000
avg_intended_air_yards 0.390 -0.859 -0.369 0.139
aggressiveness 0.236 -0.551 -0.241 0.322
max_completed_air_distance 0.545 -0.248 0.156 0.126
avg_air_distance 0.356 -0.838 -0.383 0.239
max_air_distance 0.575 -0.434 0.022 0.353
avg_air_yards_to_sticks 0.395 -0.815 -0.329 0.163
passing_cpoe 0.154 -0.345 -0.104 0.233
pass_comp_pct -0.027 -0.054 -0.005 -0.154
passer_rating 0.086 -0.009 0.096 -0.003
completion_percentage_above_expectation 0.209 -0.441 -0.134 0.350
avg_n__ aggrss mx_c__ avg_r_ mx_r_d
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards 0.000
aggressiveness 0.322 0.000
max_completed_air_distance 0.176 0.163 0.000
avg_air_distance 0.104 0.336 0.178 0.000
max_air_distance 0.267 0.277 0.124 0.315 0.000
avg_air_yards_to_sticks 0.052 0.333 0.153 0.125 0.274
passing_cpoe 0.134 -0.143 0.321 0.131 0.245
pass_comp_pct -0.278 -0.425 0.059 -0.282 -0.069
passer_rating -0.146 -0.336 0.232 -0.149 0.058
completion_percentage_above_expectation 0.255 -0.075 0.381 0.250 0.333
av____ pssng_ pss_c_ pssr_r cmp___
completionsPerGame
attemptsPerGame
passing_yardsPerGame
passing_tdsPerGame
passing_air_yardsPerGame
passing_yards_after_catchPerGame
passing_first_downsPerGame
avg_completed_air_yards
avg_intended_air_yards
aggressiveness
max_completed_air_distance
avg_air_distance
max_air_distance
avg_air_yards_to_sticks 0.000
passing_cpoe 0.137 0.000
pass_comp_pct -0.279 -0.040 0.000
passer_rating -0.132 -0.080 0.033 0.000
completion_percentage_above_expectation 0.255 -0.008 -0.100 -0.127 0.000
$mean
completionsPerGame attemptsPerGame
0.000 0.000
passing_yardsPerGame passing_tdsPerGame
0.000 0.000
passing_air_yardsPerGame passing_yards_after_catchPerGame
0.000 0.000
passing_first_downsPerGame avg_completed_air_yards
0.000 -0.306
avg_intended_air_yards aggressiveness
-0.323 -0.118
max_completed_air_distance avg_air_distance
-0.364 -0.280
max_air_distance avg_air_yards_to_sticks
-0.433 -0.305
passing_cpoe pass_comp_pct
-0.180 0.011
passer_rating completion_percentage_above_expectation
-0.083 -0.219
Error:
! lavaan->unknown():
could not compute modification indices; information matrix is singular
Below are factor scores from the model for the first six players:
Code
F1 F2 F3
[1,] 0.167661096 0.12916815 1.5548802
[2,] 0.158888863 0.02555950 -0.1940753
[3,] 0.240176157 0.04341408 -0.2110882
[4,] 0.103119285 0.08457674 1.0446940
[5,] 0.985270026 0.24947265 0.3630310
[6,] -0.004871388 -0.04442209 -0.7454311
The path diagram of the modified three-factor CFA model with correlated residuals is in Figure 22.23.
Here are the variables that loaded onto each of the factors:
Code
factor1vars <- c(
"completionsPerGame","attemptsPerGame","passing_yardsPerGame","passing_tdsPerGame",
"passing_air_yardsPerGame","passing_yards_after_catchPerGame","passing_first_downsPerGame")
factor2vars <- c(
"avg_completed_air_yards","avg_intended_air_yards","aggressiveness","max_completed_air_distance",
"avg_air_distance","max_air_distance","avg_air_yards_to_sticks")
factor3vars <- c(
"passing_cpoe","pass_comp_pct","passer_rating","completion_percentage_above_expectation")The variables that loaded most strongly onto factor 1 appear to reflect Quarterback usage: completions per game, passing attempts per game, passing yards per game, passing touchdowns per game, passing air yards (total horizontal distance the ball travels on all pass attempts) per game, passing yards after the catch per game, and first downs gained per game by passing. Quarterbacks who tend to throw more tend to have higher levels on those variables. Thus, we label component 1 as “Usage”, which reflects total Quarterback involvement, regardless of efficiency or outcome.
The variables that loaded most strongly onto factor 2 appear to reflect Quarterback aggressiveness: average air yards on completed passes, average air yards on all attempted passes, aggressiveness (percentage of passing attempts thrown into tight windows, where there is a defender within one yard or less of the receiver at the time of the completion or incompletion), average amount of air yards ahead of or behind the first down marker on passing attempts, average air distance (the true three-dimensional distance the ball travels in the air), maximum air distance, and maximum air distance on completed passes. Quarterbacks who throw the ball farther and into tighter windows tend to have higher values on those variables. Thus, we label component 2 as “Aggressiveness”, which reflects throwing longer, more difficult passes with a tight window.
The variables that loaded most strongly onto factor 3 appear to reflect Quarterback performance: passing completion percentage above expectation, pass completion percentage, and passer rating. Quarterbacks who perform better tend to have higher values on those variables. Thus, we label component 3 as “Performance”.
Here are the players and seasons that showed the highest levels of Quarterback “Usage”:
Code
Here are the players and seasons that showed the lowest levels of Quarterback “Usage”:
Code
Here are the players and seasons that showed the highest levels of Quarterback “Aggressiveness”:
Code
Here are the players and seasons that showed the lowest levels of Quarterback “Aggressiveness”:
Code
Here are the players and seasons that showed the highest levels of Quarterback “Performance”:
Code
If we restrict it to Quarterbacks who played at least 10 games in the season, here are the players and seasons that showed the highest levels of Quarterback “Performance”:
Code
Here are the players and seasons that showed the lowest levels of Quarterback “Performance”:
Code
If we restrict it to Quarterbacks who played at least 10 games in the season, here are the players and seasons that showed the lowest levels of Quarterback “Performance”:
22.8 Benefits of Factor Analysis
Factor analysis has several benefits. First, factor analysis can be used for data reduction, similar to PCA. This is useful for reducing a large set of variables into a smaller set of factors that can be more easily analyzed. In addition, this data reduction can help to address multicollinearity issues in regression analyses. In addition to data reduction, factor analysis can help identify the structure of measures and constructs. Thus, factor analysis is particularly useful in the development, validation, and improvement of measures.
22.9 Conclusion
Factor analysis is a class of latent variable models that aims to identify the optimal, parsimonious latent structure for a group of variables. Latent variables are ways of studying and operationalizing theoretical constructs that cannot be directly observed or quantified. Factor analysis encompasses two general types: confirmatory factor analysis and exploratory factor analysis. Exploratory factor analysis (EFA) is used when the researcher has no a priori hypotheses about how a set of variables is structured. Confirmatory factor analysis (CFA) is used when a researcher wants to evaluate how well a hypothesized model fits. A goal of factor analysis is to balance accuracy (i.e., variance accounted for) and parsimony (i.e., simplicity). Factor analysis estimates the latent factors as the common variance among the variables that load onto that factor and discards the remaining variance as “error”. There are many decisions to make in factor analysis. These decisions can have important impacts on the resulting solution. Thus, it can be helpful for theory and interpretability to help guide decision-making when conducting factor analysis. Using both exploratory and confirmatory factor analysis, we were able to identify three latent factors that accounted for considerable variance in the variables we examined, pertaining to Quarterbacks: 1) usage; 2) aggressiveness; 3) performance. We were then able to determine which players were highest and lowest on each of these factors.
22.10 Session Info
R version 4.6.1 (2026-06-24)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.5 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
locale:
[1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
[4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
[7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
time zone: UTC
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] lubridate_1.9.5 forcats_1.0.1 stringr_1.6.0 dplyr_1.2.1
[5] purrr_1.2.2 readr_2.2.0 tidyr_1.3.2 tibble_3.3.1
[9] ggplot2_4.0.3 tidyverse_2.0.0 lavaanPlot_0.8.1 lavaan_0.7-2
[13] nFactors_2.4.1.2 psych_2.6.5
loaded via a namespace (and not attached):
[1] GPArotation_2026.8-2 DiagrammeR_1.0.12 generics_0.1.4
[4] stringi_1.8.9 lattice_0.22-9 hms_1.1.4
[7] digest_0.6.39 magrittr_2.0.5 evaluate_1.0.5
[10] grid_4.6.1 timechange_0.4.0 RColorBrewer_1.1-3
[13] fastmap_1.2.0 jsonlite_2.0.0 scales_1.4.0
[16] pbivnorm_0.6.0 numDeriv_2016.8-1.1 mnormt_2.1.2
[19] cli_3.6.6 rlang_1.3.0 visNetwork_2.1.4
[22] withr_3.0.3 yaml_2.3.12 otel_0.2.0
[25] tools_4.6.1 parallel_4.6.1 tzdb_0.5.0
[28] vctrs_0.7.3 R6_2.6.1 stats4_4.6.1
[31] lifecycle_1.0.5 htmlwidgets_1.6.4 MASS_7.3-65
[34] pkgconfig_2.0.3 pillar_1.11.1 gtable_0.3.6
[37] glue_1.8.1 xfun_0.61 tidyselect_1.2.1
[40] rstudioapi_0.19.0 knitr_1.52 farver_2.1.2
[43] htmltools_0.5.9 nlme_3.1-169 rmarkdown_2.32
[46] compiler_4.6.1 quadprog_1.5-8 S7_0.2.2















