[Courtesy of pxhere](https://pxhere.com/de/photo/1459637) Courtesy of pxhere

Itemanalyse

Daten laden

Bevor wir mit der Analyse beginnen können, muss der Datensatz eingelesen werden. Hierfür gibt es mehrere Packages, die diesen ersten Schritt erleichtern. Mit dem Package foreign können besonders gut SPSS-Dateien (.sav) geladen werden. Mit dem Package readr (aus der Familie des tidyverse) können ansonsten die typischsten Datenformate (wie unter anderem .csv, .tsv und .txt) geladen werden. Schließlich gibt es noch das readxl Package, mit dem Excel-Dateien (.xls und .xlsx) eingelesen werden können. Für unseren Beispieldatensatz benötigen wir das Package readr. Wir laden hierbei das komplette tidyvere-package, welches im Hintergrund das readr-Package sowie noch andere relevante Packages läd (z.B. dplyr).

library(tidyverse) 
library(here)
data_gis_raw <- read_csv(url("https://raw.githubusercontent.com/jlschnatz/PsyBSc8_Diagnostik/main/src/data/GIS-data.csv"))
head(data_gis_raw) 
## # A tibble: 6 × 28
##      id   sex   ses marital profession education  GIS1  GIS2
##   <dbl> <dbl> <dbl>   <dbl>      <dbl>     <dbl> <dbl> <dbl>
## 1     1     1     2       1          3         5     4     3
## 2     2     1     2       1          1         3     4     3
## 3     3     1     2       3          1         3     4     3
## 4     4     1     2       1          1         3     4     3
## 5     5     0     1       1          5         5     3     4
## 6     6     1     1       2          2         5     4     3
## # ℹ 20 more variables: GIS3 <dbl>, GIS4 <dbl>, GIS5 <dbl>,
## #   GIS6 <dbl>, GIS7 <dbl>, GIS8 <dbl>, GIS9 <dbl>,
## #   GIS10 <dbl>, GIS11 <dbl>, GIS12 <dbl>, GIS13 <dbl>,
## #   GIS14 <dbl>, GIS15 <dbl>, GIS16 <dbl>, GIS17 <dbl>,
## #   GIS18 <dbl>, GIS19 <dbl>, GIS20 <dbl>, GIS21 <dbl>,
## #   Age <dbl>

Erstellen des Hauptdatensatzes

Für die Itemanalyse brauchen wir nur die Items ohne den soziodemographischen Angaben. Daher erstellen wir einen Datensatz, der nur die Items und keine soziodemographische Angaben enthält. Dies ist mit der select() Funktion des dplyr-Packages sehr einfach. Da alle Items mit dem Prefix “GIS” beginnen können wir uns eine kleine Helper-Funktion starts_with() des dplyr-Packages zu Nutze machen, um die Variables auszuwählen.

data_gis_item <- select(data_gis_raw, starts_with("GIS"))
data_gis_item <- select(data_gis_raw, 7:27) # Alternative 
colnames(data_gis_item) # Ausgabe der Spalten des Datensatzes
##  [1] "GIS1"  "GIS2"  "GIS3"  "GIS4"  "GIS5"  "GIS6"  "GIS7" 
##  [8] "GIS8"  "GIS9"  "GIS10" "GIS11" "GIS12" "GIS13" "GIS14"
## [15] "GIS15" "GIS16" "GIS17" "GIS18" "GIS19" "GIS20" "GIS21"

Bevor wir mit der deskriptiven Analyse beginnen, sollten wir noch überprüfen, ob es fehlende Werte (NAs) im Datensatz gibt.

anyNA(data_gis_item)
## [1] FALSE
sum(is.na(data_gis_item)) # Alternative
## [1] 0

In diesem Fall, sind keine fehlenden Werte vorhanden. Es müssen also keine Werte entfernt werden. Wenn in eurem eigenen Fragebogen fehlenden Werte vorkommen sollten, können sie durch die Funktion na.omit() oder drop_na() (tidyr-Package der tidyverse Familie) entfernt werden.

na.omit(data_gis_item)
drop_na(data_gis_item)

Deskriptive Analyse

Bevor wir die Itemanalyse durchführen, wollen wir uns zunächst ein wenig mit den Daten vertraut machen. Hierfür benötigen wir zwei Packages: . Das psych-Package beinhaltet sehr viele Funktionen und Befehle, die auch für viele andere Analysen hilfreich sind. Das Package janitor ist eine bessere Alternative zum Basis-Befehl table() und ist besonders informativ bei Häufigkeitstabellen.

Für die Berechnung deskriptiver Kennwerte (Mittelwert, Standardabweichung, Median, etc.) können wir die describe() Funktion des psych-Packages verwenden:

library(psych)
describe(data_gis_item) 
##       vars   n mean   sd median trimmed  mad min max range
## GIS1     1 300 3.44 0.74      4    3.55 0.00   0   4     4
## GIS2     2 300 3.29 0.79      3    3.42 1.48   0   4     4
## GIS3     3 300 3.25 0.89      3    3.40 1.48   0   4     4
## GIS4     4 300 3.39 0.73      4    3.52 0.00   0   4     4
## GIS5     5 300 3.23 0.68      3    3.31 0.00   1   4     3
## GIS6     6 300 3.11 0.82      3    3.19 1.48   0   4     4
## GIS7     7 300 3.27 0.90      3    3.44 1.48   0   4     4
## GIS8     8 300 3.26 0.97      3    3.46 1.48   0   4     4
## GIS9     9 300 3.15 1.03      3    3.35 1.48   0   4     4
## GIS10   10 300 2.98 0.88      3    3.08 1.48   0   4     4
## GIS11   11 300 3.27 0.73      3    3.37 0.00   0   4     4
## GIS12   12 300 3.41 0.81      4    3.56 0.00   0   4     4
## GIS13   13 300 3.29 0.82      3    3.43 1.48   0   4     4
## GIS14   14 300 3.13 0.92      3    3.28 1.48   0   4     4
## GIS15   15 300 3.16 0.75      3    3.26 0.00   1   4     3
## GIS16   16 300 3.22 0.96      3    3.42 1.48   0   4     4
## GIS17   17 300 3.11 0.93      3    3.26 1.48   0   4     4
## GIS18   18 300 2.57 1.23      3    2.71 1.48   0   4     4
## GIS19   19 300 3.22 0.87      3    3.35 1.48   0   4     4
## GIS20   20 300 2.96 0.91      3    3.06 1.48   0   4     4
## GIS21   21 300 3.03 0.92      3    3.15 1.48   0   4     4
##        skew kurtosis   se
## GIS1  -1.78     4.54 0.04
## GIS2  -1.49     3.30 0.05
## GIS3  -1.23     1.30 0.05
## GIS4  -1.21     1.68 0.04
## GIS5  -0.76     1.02 0.04
## GIS6  -0.79     0.63 0.05
## GIS7  -1.52     2.34 0.05
## GIS8  -1.56     2.12 0.06
## GIS9  -1.37     1.42 0.06
## GIS10 -0.88     0.84 0.05
## GIS11 -1.14     2.18 0.04
## GIS12 -1.62     2.89 0.05
## GIS13 -1.64     3.73 0.05
## GIS14 -1.20     1.41 0.05
## GIS15 -0.90     1.03 0.04
## GIS16 -1.64     2.70 0.06
## GIS17 -1.31     1.84 0.05
## GIS18 -0.87    -0.31 0.07
## GIS19 -1.26     1.76 0.05
## GIS20 -0.81     0.38 0.05
## GIS21 -1.00     0.88 0.05

Wenn wir nur eine spezifische Variable deskriptiv betrachten wollen (z.B. das Alter), kann in der gleichen Funktion die Variable direkt angesteuert werden.

describe(data_gis_raw$Age)
##    vars   n  mean   sd median trimmed  mad min max range skew
## X1    1 300 66.53 6.17     65   65.77 5.93  55  90    35  1.2
##    kurtosis   se
## X1     1.58 0.36

Für alle kategoriellen Daten (z.B. Geschlecht, SÖS, Bildung) benötigen keine Mittelwerte oder Standardabweichungen, sondern nutzen Häufigkeitsverteilung zur deskriptiven Beschreibung. Hier kommt jetzt das janitor-Package zum Einsatz.

library(janitor)
tabyl(data_gis_raw$sex)
##  data_gis_raw$sex   n   percent
##                 0 104 0.3466667
##                 1 196 0.6533333

Wir bekommen die relativen und absoluten Häufigkeiten für männliche und weibliche Probanden ausgegeben. Falls es fehlenden Werte gäbe, müssten diese im Bericht auch angegeben werden. Dies ist ebenfalls mit der gleichen Funktion durch die Spezifizierung eineszusätzlichen Arguments möglich.

tabyl(data_gis_raw$sex, show_na = TRUE) 

Hier ist der Output genau gleich (das ist ja in dem Datensatz keine NAs gibt).

Tipp für den Bericht:

Für die Abschlussberichte, braucht ihr die ganzen deskriptiven Informationen in APA7 formatierten Tabellen. Hierfür eignet sich besonders das Pacakge sjPlot. Als Beispiel speichern wir zunächst die die vorherige deskriptive Statistik bezüglich des Alters als ein Objekt ab. Danach erstellen wir mit einer Funktion des genannten Packages eine schön formatierte Tabelle.

library(sjPlot)
descr_age <- describe(data_gis_raw$Age)
tab_df(x = descr_age)
varsnmeansdmediantrimmedmadminmaxrangeskewkurtosisse
130066.536.176565.775.935590351.201.580.36

Die erstellte Tabelle kann sogar direkt als Word-Dokument abgespeichert werden, um danach noch weiter angepasst zu werden (z.B. Erstellen von Fußnoten, Tabellen-Titel, etc.). Wichtig dabei ist, dass nur die Endung .doc und nicht .docx funktioniert.

tab_df(
  x = descr_age,
  file = "table_descr_age.doc"
  )

Auch für die mit tabyl() erstellten Ergebnisse können wir eine Tabelle erstellen

descr_sex <- tabyl(data_gis_raw$sex)
tab_df(descr_sex)
data_gis_raw.sexnpercent
01040.35
11960.65

Zudem können wir mit dem psych-Package auch eine Tabelle nach Gruppen erstellen. Dieser Output kann dann mit einer ähnlichen Funktion des sjPlot Package in einer Tabelle dargestellt werden.

descr_age_by_sex <- describeBy(x = data_gis_raw$Age,
           group = data_gis_raw$sex) 
print(descr_age_by_sex)
## 
##  Descriptive statistics by group 
## group: 0
##    vars   n  mean   sd median trimmed  mad min max range skew
## X1    1 104 66.88 6.67     65   66.01 5.93  55  90    35 1.25
##    kurtosis   se
## X1     1.76 0.65
## ------------------------------------------------ 
## group: 1
##    vars   n  mean  sd median trimmed  mad min max range skew
## X1    1 196 66.35 5.9     65   65.67 5.93  55  89    34 1.12
##    kurtosis   se
## X1     1.16 0.42
tab_dfs(
  x = descr_age_by_sex,
  titles = c("Weiblich","Männlich"),
  )
Weiblich
varsnmeansdmediantrimmedmadminmaxrangeskewkurtosisse
110466.886.676566.015.935590351.251.760.65

 

Männlich
varsnmeansdmediantrimmedmadminmaxrangeskewkurtosisse
119666.355.906565.675.935589341.121.160.42

Es gibt auch die Möglichkeit mehrere Tabellen in ein Dokument zu packen und diese in einem Word-Dokument zu speichern:

tab_dfs(
  x = list(descr_age, descr_sex), 
  titles = c("Descriptives of Age", "Descriptives of Sex")
  )
Descriptives of Age
varsnmeansdmediantrimmedmadminmaxrangeskewkurtosisse
130066.536.176565.775.935590351.201.580.36

 

Descriptives of Sex
data_gis_raw$sexnpercent
01040.35
11960.65
tab_dfs(
  x = list(descr_age, descr_sex), 
  titles = c("Descriptives of Age","Descriptives of Sex"),
  file = "descriptives_all.doc" # wieder als .doc abspeichern
  )

Itemanalyse

Für die Itemanalyse benötigen wir den Datensatz in denen nur die Items vorhanden sind. Diesen haben wir bereits in einem vorherigen Schritt erstellt. Bevor wir die Itemanalyse jedoch durchführen, müssen wir alle Items, die inverse kodiert sind rekodieren.

Dafür speichern wir alle inversen Items zunächst in einem Vektor ab. Anschließend verwenden wir die mutate() Funktion des dplyr-Package, mit welcher wir Variablen manupulieren/verändern können. Wir müssen dabei den Zusatz across() hinzunehmen, da wir mehreren Variablen gleichzeitig verändern wollen. Das Argument .cols gibt dabei an, welche Variablen wir verändern wollen. Mit dem Argument .fns spezifizieren wir, welche Funktion wir auf die Variablen anwenden wollen. Wir verwenden die Funktion rec() aus dem sjmisc Package. Die etwas ungewöhnliche Schreibweise mit der Tilde ~und dem .x setzt sich wie folgt zusammen: Die Tilde müssen wir immer dann verwenden, wenn wir bei der Funktion, die wir anwenden zusätzlich Argumente spezifizieren (rec = "0=4; 1=3; 2=2; 3=1; 4=0"). Das .x verwenden wir als Platzhalter für alle Variablen, die wir verändern wollen (GIS9, GIS16, GIS17 und GIS18). Schließlich können wir mit dem .names Argument einen Namen für alle veränderten Variablen spezifizieren. Das Prefix {col} steht dabei für den ursprünglichen Variablennamen (z.B. GIS9). Mit dem Zusatz {col_r} wird hängen wir dem Präfix noch ein Suffix an (GIS9 -> GIS9_r). Das Suffix kennzeichnet dabei, dass wir die Items rekodiert haben.

library(sjmisc)
inverse_items <- c("GIS9","GIS16","GIS17","GIS18") 
data_gis_rec <- data_gis_item %>% 
  mutate(across(
    .cols = inverse_items, 
    .fns = ~rec(x = .x, rec = "0=4; 1=3; 2=2; 3=1; 4=0"),
    .names = "{col}_r")
    ) %>% 
  select(-inverse_items) 
colnames(data_gis_rec)
##  [1] "GIS1"    "GIS2"    "GIS3"    "GIS4"    "GIS5"    "GIS6"   
##  [7] "GIS7"    "GIS8"    "GIS10"   "GIS11"   "GIS12"   "GIS13"  
## [13] "GIS14"   "GIS15"   "GIS19"   "GIS20"   "GIS21"   "GIS9_r" 
## [19] "GIS16_r" "GIS17_r" "GIS18_r"

Jetzt können wir die Itemanalyse durchführen. Wir verwenden dafür eine Funktion aus dem sjPlot Package.

sjt.itemanalysis(
  df = data_gis_rec,
  factor.groups.titles = "Erste Itemanalyse"
  )
Erste Itemanalyse
RowMissingsMeanSDSkewItem DifficultyItem Discriminationα if deleted
GIS10.00 %3.440.74-1.80.860.600.83
GIS20.00 %3.290.79-1.50.820.470.84
GIS30.00 %3.250.89-1.250.810.590.83
GIS40.00 %3.390.73-1.220.850.610.83
GIS50.00 %3.230.68-0.770.810.630.83
GIS60.00 %3.110.82-0.80.780.580.83
GIS70.00 %3.270.9-1.530.820.550.83
GIS80.00 %3.260.97-1.570.810.590.83
GIS100.00 %2.980.88-0.890.750.620.83
GIS110.00 %3.270.73-1.160.820.670.83
GIS120.00 %3.410.81-1.640.850.660.83
GIS130.00 %3.290.82-1.660.820.680.83
GIS140.00 %3.130.92-1.210.780.630.83
GIS150.00 %3.160.75-0.90.790.620.83
GIS190.00 %3.220.87-1.280.800.650.83
GIS200.00 %2.960.91-0.820.740.730.82
GIS210.00 %3.030.92-1.010.760.670.83
GIS9_r0.00 %0.851.031.390.21-0.280.87
GIS16_r0.00 %0.780.961.650.19-0.140.86
GIS17_r0.00 %0.890.931.320.22-0.260.87
GIS18_r0.00 %1.431.230.880.36-0.200.87
Mean inter-item-correlation=0.242 · Cronbach's α=0.845

Wir sehen, dass die Variablen der Reihenfolge nach wie sie im Dataframe auftauchen, in die Tabelle aufgenommen werden. Dadurch sind die rekodierten Variablen am Ende der Tabelle platziert. Wir können die Reihenfolge der Items ändern, indem wir diese in einem Vektor spezifizieren. Anschließend verwenden wir wieder die select() Funktion und bringen dadurch die Variablen in die gewünschte Reihenfolge.

col_order <- c(
  "GIS1","GIS2","GIS3","GIS4","GIS5","GIS6",
  "GIS7","GIS8","GIS9_r","GIS10", "GIS11",
  "GIS12","GIS13","GIS14","GIS15","GIS16_r",
  "GIS17_r","GIS18_r", "GIS19","GIS20","GIS21"
  )
data_gis_rec2 <- select(data_gis_rec, all_of(col_order))
sjt.itemanalysis(
  df = data_gis_rec2,
  factor.groups.titles = "Desktiptive Ergebnisse der Itemanalyse (mit angepasster Reihenfolge)"
  )
Desktiptive Ergebnisse der Itemanalyse (mit angepasster Reihenfolge)
RowMissingsMeanSDSkewItem DifficultyItem Discriminationα if deleted
GIS10.00 %3.440.74-1.80.860.600.83
GIS20.00 %3.290.79-1.50.820.470.84
GIS30.00 %3.250.89-1.250.810.590.83
GIS40.00 %3.390.73-1.220.850.610.83
GIS50.00 %3.230.68-0.770.810.630.83
GIS60.00 %3.110.82-0.80.780.580.83
GIS70.00 %3.270.9-1.530.820.550.83
GIS80.00 %3.260.97-1.570.810.590.83
GIS9_r0.00 %0.851.031.390.21-0.280.87
GIS100.00 %2.980.88-0.890.750.620.83
GIS110.00 %3.270.73-1.160.820.670.83
GIS120.00 %3.410.81-1.640.850.660.83
GIS130.00 %3.290.82-1.660.820.680.83
GIS140.00 %3.130.92-1.210.780.630.83
GIS150.00 %3.160.75-0.90.790.620.83
GIS16_r0.00 %0.780.961.650.19-0.140.86
GIS17_r0.00 %0.890.931.320.22-0.260.87
GIS18_r0.00 %1.431.230.880.36-0.200.87
GIS190.00 %3.220.87-1.280.800.650.83
GIS200.00 %2.960.91-0.820.740.730.82
GIS210.00 %3.030.92-1.010.760.670.83
Mean inter-item-correlation=0.242 · Cronbach's α=0.845

Wir sehen, dass alle invers gestellten Items eine schlechte Trennschärfe besitzen. Deswegen müssen diese aus der weiteren Analyse ausgeschlossen werden. Alle anderen Items besitzen sehr hohe Trennschärfen und können somit beibehalten werden.

drop_discrm <- c("GIS9_r", "GIS16_r","GIS17_r", "GIS18_r")
data_gis_final <- select(data_gis_rec2, -all_of(drop_discrm))

Mit diesem Datensatz können wir nun die finale Itemanalyse durchführen:

sjt.itemanalysis(
  df = data_gis_final,
  factor.groups.titles = "Finale Itemanalyse"
  )
Finale Itemanalyse
RowMissingsMeanSDSkewItem DifficultyItem Discriminationα if deleted
GIS10.00 %3.440.74-1.80.860.620.93
GIS20.00 %3.290.79-1.50.820.490.94
GIS30.00 %3.250.89-1.250.810.640.93
GIS40.00 %3.390.73-1.220.850.670.93
GIS50.00 %3.230.68-0.770.810.670.93
GIS60.00 %3.110.82-0.80.780.640.93
GIS70.00 %3.270.9-1.530.820.600.94
GIS80.00 %3.260.97-1.570.810.680.93
GIS100.00 %2.980.88-0.890.750.630.93
GIS110.00 %3.270.73-1.160.820.700.93
GIS120.00 %3.410.81-1.640.850.730.93
GIS130.00 %3.290.82-1.660.820.750.93
GIS140.00 %3.130.92-1.210.780.670.93
GIS150.00 %3.160.75-0.90.790.660.93
GIS190.00 %3.220.87-1.280.800.700.93
GIS200.00 %2.960.91-0.820.740.760.93
GIS210.00 %3.030.92-1.010.760.690.93
Mean inter-item-correlation=0.472 · Cronbach's α=0.937

Abschließend gibt es noch die Möglichkeit, McDonald´s $\omega$ als ein alternatives Reliabilitätsmaß (zusätzlich zu Cronbach´s $\alpha$) zu bestimmen.

omega_items <- omega(data_gis_final,
                     plot = FALSE)
omega_items$omega.tot
## [1] 0.948647
omega_items$alpha
## [1] 0.9381681