Bei der konfirmatorischen Faktorenanalyse (CFA) sind wir etwas sicherer als bei der explorativen Schwester, denn hier haben wir bereits eine Idee davon welche Faktorenstruktur unseren Daten zugrunde liegt. Mit der CFA können wir also überprüfen, ob unsere Daten zu dem theoretischen Modell passen. Das beurteilen wir anhand des Modell-Fits.

Pakete

Alle Berechnungen und Abbildungen können wir mit unseren Standardpaketen durchführen. Wir benötigen das tidyverse zum Data Wrangling und zur Visualisierung der Daten. haven benötigen wir für den Import von SPSS-Dateien und rstatix für statistische Analysen. Wenn man sich den Import und das Bereinigen der Daten sparen möchte (Schritte, die man dennoch üben sollte), findet man die Daten auch im Paket costatcompanion.

Für die CFA benötigen wir das Paket lavaan, für Korrelogramme das Paket corrgram.

library(tidyverse)
library(haven)
library(rstatix)
library(costatcompanion)
library(corrgram)
library(lavaan)

Beispiel

Bei der explorativen Faktorenanalyse haben wir bereits den Fragebogen zur Freude an Festivitäten (FFF) kennengelernt, bei dem wir eine zugrundeliegende Struktur mit drei latenten Faktoren vermuten. Diese drei Faktoren waren “Vorfreude”, “Tanzen” und “Geselligkeit”. Nun sind wir auf mehrere Feiern und in Clubs im Raum Dortmund gegangen und haben den Fragebogen 442 Probanden erneut ausfüllen lassen. Nun wollen wir überprüfen, ob die in der ersten Stichprobe gefundene Faktorenstruktur auch für diese Stichprobe angemessen ist.

Die neuen Daten finden wir in der Datei party_people.sav im GitHub-Repository, oder als party_people im costatcompanion.

party_people
## # A tibble: 442 x 16
##    id    item_1 item_2 item_3 item_4 item_5 item_6 item_7 item_8 item_9 item_10
##    <chr>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>   <dbl>
##  1 1       5.95   3.96   3.11   4.29   5.69   5.53   7.36   4.82   4.07    6.14
##  2 2       4.77   2.74   3.80   4.93   4.61   6.83   6.32   6.67   4.70    2.64
##  3 3       5.34   6.44   6.98   5.24   3.91   3.30   4.90   5.41   4.60    6.24
##  4 4       3.25   4.61   5.49   2.83   5.03   5.52   2.17   2.72   3.39    3.06
##  5 5       7.17   6.64   6.28   6.78   5.03   5.17   4.16   5.53   6.29    7.92
##  6 6       4.29   7.51   6.01   8.74   7.42   3.43   5.67   6.31   7.90   10   
##  7 7       1.60   2.91   2.99   5.16   4.29   4.16   5.06   6.58   3.48    5.92
##  8 8       6.20   6.50   3.48   3.33   4.70   3.58   4.94   1.18   4.49    3.72
##  9 9       6.18   6.05   5.44   5.94   4.28   4.53   3.99   4.13   5.43    6.18
## 10 10      3.77   6.63   6.01   5.01   5.06   3.65   2.59   3.46   5.26    6.11
## # ... with 432 more rows, and 5 more variables: item_11 <dbl>, item_12 <dbl>,
## #   item_13 <dbl>, item_14 <dbl>, item_15 <dbl>

Voraussetzungen

Siehe die Voraussetzungen bei der EFA, wobei Bartlett und KMO hier entfallen. Die Daten sollten pro Item nicht unbedingt schief sein und jeder Faktor sollte mit mindestens 3 Items/Variablen geschätzt werden.

Vorgehen

Eine gute Übersicht über das Vorgehen ist bei Schreiber (2017, S. 635) dargestellt.

EDA

Eine gute Idee ist es, wie immer, sich die Daten vorher einmal anzuschauen. Um uns die Arbeit einfacher zu machen, entfernen wir aber zunächst wieder die Probanden ID.

party_people_raw <- party_people %>% 
  select(-id)

party_people_raw
## # A tibble: 442 x 15
##    item_1 item_2 item_3 item_4 item_5 item_6 item_7 item_8 item_9 item_10
##     <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>   <dbl>
##  1   5.95   3.96   3.11   4.29   5.69   5.53   7.36   4.82   4.07    6.14
##  2   4.77   2.74   3.80   4.93   4.61   6.83   6.32   6.67   4.70    2.64
##  3   5.34   6.44   6.98   5.24   3.91   3.30   4.90   5.41   4.60    6.24
##  4   3.25   4.61   5.49   2.83   5.03   5.52   2.17   2.72   3.39    3.06
##  5   7.17   6.64   6.28   6.78   5.03   5.17   4.16   5.53   6.29    7.92
##  6   4.29   7.51   6.01   8.74   7.42   3.43   5.67   6.31   7.90   10   
##  7   1.60   2.91   2.99   5.16   4.29   4.16   5.06   6.58   3.48    5.92
##  8   6.20   6.50   3.48   3.33   4.70   3.58   4.94   1.18   4.49    3.72
##  9   6.18   6.05   5.44   5.94   4.28   4.53   3.99   4.13   5.43    6.18
## 10   3.77   6.63   6.01   5.01   5.06   3.65   2.59   3.46   5.26    6.11
## # ... with 432 more rows, and 5 more variables: item_11 <dbl>, item_12 <dbl>,
## #   item_13 <dbl>, item_14 <dbl>, item_15 <dbl>
party_people_raw %>% 
  get_summary_stats()
## # A tibble: 15 x 13
##    variable     n   min   max median    q1    q3   iqr   mad  mean    sd    se
##    <chr>    <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1 item_1     442 0     10      4.80  3.71  6.13  2.42  1.76  4.91  1.78 0.085
##  2 item_10    442 0.059 10      4.96  3.85  6.13  2.28  1.71  5.01  1.74 0.083
##  3 item_11    442 0.904  9.60   5.12  4.01  6.16  2.15  1.57  5.14  1.67 0.079
##  4 item_12    442 0      9.78   5.04  3.90  6.27  2.36  1.72  5.05  1.68 0.08 
##  5 item_13    442 0     10      5.06  3.76  6.14  2.37  1.74  4.97  1.71 0.081
##  6 item_14    442 0      9.81   4.97  3.92  6.15  2.22  1.64  4.98  1.74 0.083
##  7 item_15    442 1.04   9.88   4.99  3.86  6.2   2.34  1.72  5.03  1.61 0.077
##  8 item_2     442 0.329  9.03   4.94  3.87  6.05  2.17  1.60  5.00  1.62 0.077
##  9 item_3     442 0      9.93   5.05  3.96  6.22  2.25  1.69  5.07  1.65 0.079
## 10 item_4     442 0.097 10      4.90  3.91  6.08  2.17  1.65  4.94  1.71 0.081
## 11 item_5     442 0.213  9.59   5.05  4.14  6.11  1.97  1.52  5.11  1.56 0.074
## 12 item_6     442 0      9.86   5.03  3.96  6.02  2.06  1.55  4.96  1.50 0.072
## 13 item_7     442 0.43   8.71   4.93  3.95  5.84  1.89  1.41  4.88  1.48 0.071
## 14 item_8     442 0.087 10      5.02  3.73  6.32  2.59  1.93  5.00  1.91 0.091
## 15 item_9     442 0      9.88   5.16  3.90  6.34  2.44  1.79  5.10  1.82 0.086
## # ... with 1 more variable: ci <dbl>
party_people_raw %>% 
  corrgram(panel = panel.fill, order = TRUE)

Bei der CFA müssen wir falsch herum codierte Items übrigens nicht umkodieren; dadurch würde sich nur das Vorzeichen des standardisierten Pfadkoeffizienten ändern, der Modell-Fit jedoch nicht.

Durchführung

Zunächst müssen wir das hypothetische Modell definieren. Aus der EFA ging hervor, dass auf dem Faktor “Vorfreude” die Items 1, 4, 7, 12, 13 und 15 laden. Zum Faktor “Tanzen” sollen die Items 3, 5, 9 und 11 gehören und zum Faktor “Geselligkeit” die Items 2, 6, 8, 10 und 14. Das Modell müssen wir in der lavaan-Syntax formulieren, die relativ selbsterklärend ist. (Reflektive) Faktoren werden durch =~ definiert und die Syntax muss in Anführgungszeichen stehen ('').

party_model <- '
anticipation =~ item_1 + item_4 + item_7 + item_12 + item_13 + item_15
dancing =~ item_3 + item_5 + item_9 + item_11
socializing =~ item_2 + item_6 + item_8 + item_10 + item_14
'

Dieses Modell können wir dann mit der Funktion cfa() schätzen lassen. Die Ergebnisse speichern wir in einem Objekt, um uns danach eine Zusammenfassung ausgeben zu lassen.

party_cfa <-  cfa(party_model, data = party_people)

summary(party_cfa)
## lavaan 0.6-6 ended normally after 43 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of free parameters                         33
##                                                       
##   Number of observations                           442
##                                                       
## Model Test User Model:
##                                                       
##   Test statistic                               320.076
##   Degrees of freedom                                87
##   P-value (Chi-square)                           0.000
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Expected
##   Information saturated (h1) model          Structured
## 
## Latent Variables:
##                    Estimate  Std.Err  z-value  P(>|z|)
##   anticipation =~                                     
##     item_1            1.000                           
##     item_4            0.853    0.069   12.343    0.000
##     item_7            0.633    0.060   10.589    0.000
##     item_12           0.835    0.068   12.302    0.000
##     item_13           0.931    0.070   13.356    0.000
##     item_15          -0.826    0.065  -12.663    0.000
##   dancing =~                                          
##     item_3            1.000                           
##     item_5            0.766    0.080    9.548    0.000
##     item_9            1.094    0.099   11.077    0.000
##     item_11           0.953    0.089   10.738    0.000
##   socializing =~                                      
##     item_2            1.000                           
##     item_6           -0.805    0.080  -10.011    0.000
##     item_8            1.318    0.108   12.219    0.000
##     item_10           1.189    0.098   12.111    0.000
##     item_14           1.005    0.094   10.685    0.000
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)
##   anticipation ~~                                     
##     dancing           0.763    0.110    6.948    0.000
##     socializing       0.853    0.109    7.834    0.000
##   dancing ~~                                          
##     socializing       0.562    0.090    6.245    0.000
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)
##    .item_1            1.485    0.127   11.704    0.000
##    .item_4            1.687    0.131   12.851    0.000
##    .item_7            1.526    0.112   13.645    0.000
##    .item_12           1.637    0.127   12.875    0.000
##    .item_13           1.471    0.121   12.104    0.000
##    .item_15           1.439    0.114   12.647    0.000
##    .item_3            1.425    0.133   10.676    0.000
##    .item_5            1.676    0.131   12.812    0.000
##    .item_9            1.726    0.161   10.728    0.000
##    .item_11           1.583    0.138   11.452    0.000
##    .item_2            1.484    0.120   12.397    0.000
##    .item_6            1.533    0.115   13.329    0.000
##    .item_8            1.672    0.151   11.037    0.000
##    .item_10           1.442    0.128   11.249    0.000
##    .item_14           1.876    0.145   12.902    0.000
##     anticipation      1.675    0.203    8.240    0.000
##     dancing           1.305    0.182    7.168    0.000
##     socializing       1.124    0.159    7.084    0.000

Der Output ist schon ziemlich ausführlich, aber richtig informativ wird er, wenn wir uns die standardisierten Koeffizienten und zusätzliche Fit-Measures ausgeben lassen. Die standardisierten Koeffizienten finden wir dann in der Spalte Std.all.

summary(party_cfa, standardized = TRUE, fit.measures = TRUE)
## lavaan 0.6-6 ended normally after 43 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of free parameters                         33
##                                                       
##   Number of observations                           442
##                                                       
## Model Test User Model:
##                                                       
##   Test statistic                               320.076
##   Degrees of freedom                                87
##   P-value (Chi-square)                           0.000
## 
## Model Test Baseline Model:
## 
##   Test statistic                              2257.910
##   Degrees of freedom                               105
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.892
##   Tucker-Lewis Index (TLI)                       0.869
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)             -11849.897
##   Loglikelihood unrestricted model (H1)     -11689.859
##                                                       
##   Akaike (AIC)                               23765.794
##   Bayesian (BIC)                             23900.807
##   Sample-size adjusted Bayesian (BIC)        23796.080
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.078
##   90 Percent confidence interval - lower         0.069
##   90 Percent confidence interval - upper         0.087
##   P-value RMSEA <= 0.05                          0.000
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.076
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Expected
##   Information saturated (h1) model          Structured
## 
## Latent Variables:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   anticipation =~                                                       
##     item_1            1.000                               1.294    0.728
##     item_4            0.853    0.069   12.343    0.000    1.104    0.648
##     item_7            0.633    0.060   10.589    0.000    0.819    0.553
##     item_12           0.835    0.068   12.302    0.000    1.081    0.645
##     item_13           0.931    0.070   13.356    0.000    1.204    0.705
##     item_15          -0.826    0.065  -12.663    0.000   -1.069   -0.665
##   dancing =~                                                            
##     item_3            1.000                               1.142    0.691
##     item_5            0.766    0.080    9.548    0.000    0.875    0.560
##     item_9            1.094    0.099   11.077    0.000    1.250    0.689
##     item_11           0.953    0.089   10.738    0.000    1.089    0.654
##   socializing =~                                                        
##     item_2            1.000                               1.060    0.657
##     item_6           -0.805    0.080  -10.011    0.000   -0.854   -0.568
##     item_8            1.318    0.108   12.219    0.000    1.397    0.734
##     item_10           1.189    0.098   12.111    0.000    1.261    0.724
##     item_14           1.005    0.094   10.685    0.000    1.065    0.614
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   anticipation ~~                                                       
##     dancing           0.763    0.110    6.948    0.000    0.516    0.516
##     socializing       0.853    0.109    7.834    0.000    0.622    0.622
##   dancing ~~                                                            
##     socializing       0.562    0.090    6.245    0.000    0.464    0.464
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .item_1            1.485    0.127   11.704    0.000    1.485    0.470
##    .item_4            1.687    0.131   12.851    0.000    1.687    0.581
##    .item_7            1.526    0.112   13.645    0.000    1.526    0.695
##    .item_12           1.637    0.127   12.875    0.000    1.637    0.584
##    .item_13           1.471    0.121   12.104    0.000    1.471    0.503
##    .item_15           1.439    0.114   12.647    0.000    1.439    0.557
##    .item_3            1.425    0.133   10.676    0.000    1.425    0.522
##    .item_5            1.676    0.131   12.812    0.000    1.676    0.686
##    .item_9            1.726    0.161   10.728    0.000    1.726    0.525
##    .item_11           1.583    0.138   11.452    0.000    1.583    0.572
##    .item_2            1.484    0.120   12.397    0.000    1.484    0.569
##    .item_6            1.533    0.115   13.329    0.000    1.533    0.678
##    .item_8            1.672    0.151   11.037    0.000    1.672    0.461
##    .item_10           1.442    0.128   11.249    0.000    1.442    0.476
##    .item_14           1.876    0.145   12.902    0.000    1.876    0.623
##     anticipation      1.675    0.203    8.240    0.000    1.000    1.000
##     dancing           1.305    0.182    7.168    0.000    1.000    1.000
##     socializing       1.124    0.159    7.084    0.000    1.000    1.000

Diesen Output kann man mit dem Paket broom natürlich auch tidy machen, um sie weiter verwenden zu können, oder um mit glance() einen schnellen Blick auf die Fit-Measures zu kriegen.

library(broom)

tidy(party_cfa)

glance(party_cfa)

Beurteilung des Modell-Fits

Es gibt unzählige “Vorgaben” oder “Grenzen”, in die diese Fit-Measures fallen sollen , damit ein Modell “akzeptabel” ist. Wenn man mit diesen Kriterien arbeiten möchte, ist es falsch, ein “Schwarz-Weiß-”, oder “Alles-oder-Nichts-Denken” an den Tag zu legen. Nur weil ein (willkürlich gewähltes) Kriterium knapp nicht erfüllt wird, muss das Modell nicht schlecht sein. Es kommt, wie immer bei Faktorenanalysen, zunächst einmal auf die Interpretierbarkeit des Modells an.

Wer solche Kriterien nutzen möchte, ist bei Schreiber (2017, S. 639) gut aufgehoben. Die wichtigsten Fit-Measures sind in der unteren Tabelle.

Name Abkürzung Regel N-sensitiv Bestraft Komplexität
Chi-Quadrat - Verhältnis von Chi-Quadrat/df < 3 oder < 2 für geschachtelte Modelle Ja Nein
Tucker-Lewis Index TLI >= .95, sollte nahe 1 sein Nein Ja
Comparative Fit Index CFI >= .95, sollte nahe 1 sein Nein Ja
Root Mean Square Residual RMR Je keiner, desto besser; 0 wäre perfekt Ja Nein
Standardized RMR SRMR <= .08 Ja Nein
Root Mean Square Error of Approximation RMSEA < .05 Ja (bei kleinem N) Ja

Unser Modell passt für diese Daten also nicht optimal, jediglich der SRMR wäre im akzeptierten Bereich. An dieser Stelle aber noch einmal der Hinweis: Diese Grenzen sind willkürlich!

Aus der Praxis

Holzinger & Swineford (1939)

holzinger_model <- '
visual  =~ x1 + x2 + x3      
textual =~ x4 + x5 + x6
speed   =~ x7 + x8 + x9
'

holzinger_fit <- cfa(holzinger_model, data = HolzingerSwineford1939)


summary(holzinger_fit, standardized = TRUE, fit.measures = TRUE)
## lavaan 0.6-6 ended normally after 35 iterations
## 
##   Estimator                                         ML
##   Optimization method                           NLMINB
##   Number of free parameters                         21
##                                                       
##   Number of observations                           301
##                                                       
## Model Test User Model:
##                                                       
##   Test statistic                                85.306
##   Degrees of freedom                                24
##   P-value (Chi-square)                           0.000
## 
## Model Test Baseline Model:
## 
##   Test statistic                               918.852
##   Degrees of freedom                                36
##   P-value                                        0.000
## 
## User Model versus Baseline Model:
## 
##   Comparative Fit Index (CFI)                    0.931
##   Tucker-Lewis Index (TLI)                       0.896
## 
## Loglikelihood and Information Criteria:
## 
##   Loglikelihood user model (H0)              -3737.745
##   Loglikelihood unrestricted model (H1)      -3695.092
##                                                       
##   Akaike (AIC)                                7517.490
##   Bayesian (BIC)                              7595.339
##   Sample-size adjusted Bayesian (BIC)         7528.739
## 
## Root Mean Square Error of Approximation:
## 
##   RMSEA                                          0.092
##   90 Percent confidence interval - lower         0.071
##   90 Percent confidence interval - upper         0.114
##   P-value RMSEA <= 0.05                          0.001
## 
## Standardized Root Mean Square Residual:
## 
##   SRMR                                           0.065
## 
## Parameter Estimates:
## 
##   Standard errors                             Standard
##   Information                                 Expected
##   Information saturated (h1) model          Structured
## 
## Latent Variables:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   visual =~                                                             
##     x1                1.000                               0.900    0.772
##     x2                0.554    0.100    5.554    0.000    0.498    0.424
##     x3                0.729    0.109    6.685    0.000    0.656    0.581
##   textual =~                                                            
##     x4                1.000                               0.990    0.852
##     x5                1.113    0.065   17.014    0.000    1.102    0.855
##     x6                0.926    0.055   16.703    0.000    0.917    0.838
##   speed =~                                                              
##     x7                1.000                               0.619    0.570
##     x8                1.180    0.165    7.152    0.000    0.731    0.723
##     x9                1.082    0.151    7.155    0.000    0.670    0.665
## 
## Covariances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##   visual ~~                                                             
##     textual           0.408    0.074    5.552    0.000    0.459    0.459
##     speed             0.262    0.056    4.660    0.000    0.471    0.471
##   textual ~~                                                            
##     speed             0.173    0.049    3.518    0.000    0.283    0.283
## 
## Variances:
##                    Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all
##    .x1                0.549    0.114    4.833    0.000    0.549    0.404
##    .x2                1.134    0.102   11.146    0.000    1.134    0.821
##    .x3                0.844    0.091    9.317    0.000    0.844    0.662
##    .x4                0.371    0.048    7.779    0.000    0.371    0.275
##    .x5                0.446    0.058    7.642    0.000    0.446    0.269
##    .x6                0.356    0.043    8.277    0.000    0.356    0.298
##    .x7                0.799    0.081    9.823    0.000    0.799    0.676
##    .x8                0.488    0.074    6.573    0.000    0.488    0.477
##    .x9                0.566    0.071    8.003    0.000    0.566    0.558
##     visual            0.809    0.145    5.564    0.000    1.000    1.000
##     textual           0.979    0.112    8.737    0.000    1.000    1.000
##     speed             0.384    0.086    4.451    0.000    1.000    1.000

Literatur

Holzinger, K. J., & Swineford, F. (1939). A study in factor analysis: the stability of a bi-factor solution. Supplementary Educational Monographs, 48, xi + 91.

Schreiber, J. B. (2017). Update to core reporting practices in structural equation modeling. Research in Social & Administrative Pharmacy, 13(3), 634–643. https://doi.org/10.1016/j.sapharm.2016.06.006