Um den Übergang von einfachen Kreuztabellen zur Regressionsanalyse nachvollziehbar zu machen, bietet es sich an, mit einem Beispiel zu beginnen, in dem sowohl die abhängige Variable (Lesekompetenz) als auch die unabhängigen Variablen (ESCS und Schulentfremdung) metrisch sind. Durch eine Einteilung in Kategorien lassen sich zunächst Kreuztabellen erstellen, die den Zusammenhang zwischen ESCS und Lesekompetenz in Form gruppierter Mittelwerte oder spaltenbedingter Prozentwerte zeigen.
Diese Darstellung ist intuitiv, aber grob: Sie zeigt den Trend nur stufenweise. Die lineare Regression kann anschließend als geglättete Version der Kreuztabelle eingeführt werden. Während die Kreuztabelle Sprünge zwischen Kategorien zeigt, fasst die Regression den Trend in einer einzigen Steigung zusammen und nutzt die volle Information der metrischen Variablen.
Im nächsten Schritt wird eine Drittvariable eingeführt – hier: Schulentfremdung. Betrachtet man Kreuztabellen innerhalb der Kategorien dieser Drittvariable, kann der Zusammenhang zwischen ESCS und Lesekompetenz verschwinden oder sich verändern. Genau das leistet auch die multiple Regression: Sie hält Schulentfremdung konstant und schätzt den „bereinigten“ Zusammenhang zwischen ESCS und Lesekompetenz.
So wird sichtbar, dass Kreuztabellen und Regression dieselbe Logik verfolgen – nur mit unterschiedlicher Präzision. Kreuztabellen sind grobe, kategoriale Darstellungen; Regressionen sind ihre kontinuierlich geglätteten Gegenstücke.
set.seed(123)
N <- 6000
# Schulentfremdung (0 = nein, 1 = ja)
Schulentfremdung_bin <- rbinom(N, 1, 0.5)
# ESCS:
# ENTGEGEN deinem letzten Code:
# - Entfremdete haben NIEDRIGEREN ESCS
# - Nicht-entfremdete haben HÖHEREN ESCS
# - Effekt moderat, damit Überlappung bleibt
ESCS <- rnorm(N, mean = 0.4 - 0.8*Schulentfremdung_bin, sd = 0.8)
# Lesekompetenz:
# - ESCS-Effekt klein (innerhalb der Gruppen fast kein Zusammenhang)
# - Schulentfremdung starker negativer Effekt
Lesekompetenz <- 500 +
4*ESCS + # kleiner Effekt innerhalb der Gruppen
-80*Schulentfremdung_bin + # großer Unterschied zwischen Gruppen
rnorm(N, 0, 25)
# Datensatz
dat <- data.frame(ESCS, Lesekompetenz, Schulentfremdung_bin)
dat$Schulentfremdung <- ifelse(Schulentfremdung_bin == 1, "ja", "nein")
library(dplyr)
library(knitr)
library(kableExtra)
library(lsr) # für Cramer's V
## Warning: Paket 'lsr' wurde unter R Version 4.5.3 erstellt
dat <- dat %>%
mutate(
ESCS_cat = cut(ESCS,
breaks = quantile(ESCS, c(0, .33, .66, 1)),
include.lowest = TRUE,
labels = c("niedrig", "mittel", "hoch")),
Lese_cat = cut(Lesekompetenz,
breaks = quantile(Lesekompetenz, c(0, .33, .66, 1)),
include.lowest = TRUE,
labels = c("niedrig", "mittel", "hoch"))
)
tab_all <- table(dat$Lese_cat, dat$ESCS_cat)
kable(prop.table(tab_all, 2),
digits = 2,
caption = "Lesekompetenz × ESCS (spaltenweise Prozente)") %>%
add_header_above(c(" " = 1, "ESCS" = 3)) %>% # Spaltenüberschrift
group_rows("Lesekompetenz", 1, 3) %>% # Zeilenüberschrift
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| niedrig | mittel | hoch | |
|---|---|---|---|
| Lesekompetenz | |||
| niedrig | 0.54 | 0.31 | 0.14 |
| mittel | 0.31 | 0.36 | 0.32 |
| hoch | 0.15 | 0.32 | 0.54 |
# Cramer's V
cv <- suppressWarnings(cramersV(tab_all))
kable(
data.frame(
Kennwert = "Cramer's V",
Wert = round(cv, 3)
),
col.names = c("Kennwert", "Wert"),
align = c("l", "r")
) %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped"))
| Kennwert | Wert |
|---|---|
| Cramer’s V | 0.28 |
# Daten filtern
dat_nein <- dat %>% filter(Schulentfremdung == "nein")
dat_ja <- dat %>% filter(Schulentfremdung == "ja")
# Tabellen
tab_nein <- table(dat_nein$Lese_cat, dat_nein$ESCS_cat)
tab_ja <- table(dat_ja$Lese_cat, dat_ja$ESCS_cat)
# -------------------------
# Tabelle: Schulentfremdung = NEIN
# -------------------------
kable(prop.table(tab_nein, 2),
digits = 2,
caption = "Lesekompetenz × ESCS – ohne Schulentfremdung") %>%
add_header_above(c(" " = 1, "ESCS" = 3)) %>% # Spaltenbeschriftung
group_rows("Lesekompetenz", 1, 3) %>% # Zeilenbeschriftung
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| niedrig | mittel | hoch | |
|---|---|---|---|
| Lesekompetenz | |||
| niedrig | 0.01 | 0.00 | 0.00 |
| mittel | 0.37 | 0.36 | 0.29 |
| hoch | 0.62 | 0.64 | 0.71 |
# Cramer's V
cv_nein <- suppressWarnings(cramersV(tab_nein))
kable(
data.frame(
Kennwert = "Cramer's V",
Wert = round(cv_nein, 3)
),
col.names = c("Kennwert", "Wert"),
align = c("l", "r")
) %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped"))
| Kennwert | Wert |
|---|---|
| Cramer’s V | 0.055 |
# -------------------------
# Tabelle: Schulentfremdung = JA
# -------------------------
kable(prop.table(tab_ja, 2),
digits = 2,
caption = "Lesekompetenz × ESCS – mit Schulentfremdung") %>%
add_header_above(c(" " = 1, "ESCS" = 3)) %>% # Spaltenbeschriftung
group_rows("Lesekompetenz", 1, 3) %>% # Zeilenbeschriftung
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| niedrig | mittel | hoch | |
|---|---|---|---|
| Lesekompetenz | |||
| niedrig | 0.71 | 0.63 | 0.59 |
| mittel | 0.29 | 0.37 | 0.41 |
| hoch | 0.00 | 0.00 | 0.01 |
# Cramer's V
cv_ja <- suppressWarnings(cramersV(tab_ja))
kable(
data.frame(
Kennwert = "Cramer's V",
Wert = round(cv_ja, 3)
),
col.names = c("Kennwert", "Wert"),
align = c("l", "r")
) %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped"))
| Kennwert | Wert |
|---|---|
| Cramer’s V | 0.076 |
Die folgende Regression nutzt die volle metrische Information von ESCS und zeigt denselben Trend wie die Kreuztabelle – nur geglättet. Die Steigung beschreibt den durchschnittlichen Anstieg der Lesekompetenz pro Einheit ESCS.
library(broom)
library(ggplot2)
model1 <- lm(Lesekompetenz ~ ESCS, data = dat)
model1_tidy <- tidy(model1)
kable(model1_tidy,
caption = "Einfache Regression: Lesekompetenz ~ ESCS",
digits = 3) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | 460.307 | 0.554 | 831.421 | 0 |
| ESCS | 24.460 | 0.613 | 39.903 | 0 |
ggplot(dat, aes(ESCS, Lesekompetenz)) +
geom_point(alpha = .2, color = "grey60") +
geom_smooth(method = "lm", color = "#0072B2", size = 1.1) +
labs(title = "Einfache Regression: klarer positiver Zusammenhang",
x = "ESCS (metrisch)",
y = "Lesekompetenz") +
theme_minimal(base_size = 14)
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using formula = 'y ~ x'
model2 <- lm(Lesekompetenz ~ ESCS + Schulentfremdung_bin, data = dat)
model2_tidy <- tidy(model2)
kable(model2_tidy,
caption = "Multiple Regression: Lesekompetenz ~ ESCS + Schulentfremdung",
digits = 3) %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | 499.213 | 0.482 | 1034.773 | 0 |
| ESCS | 4.567 | 0.401 | 11.386 | 0 |
| Schulentfremdung_bin | -78.473 | 0.725 | -108.308 | 0 |
ggplot(dat, aes(x = ESCS, y = Lesekompetenz,
color = Schulentfremdung)) +
geom_point(alpha = .15) +
geom_smooth(method = "lm", se = FALSE, size = 1.2) +
scale_color_manual(values = c("nein" = "#0072B2", "ja" = "#D55E00"),
name = "Schulentfremdung") +
labs(
title = "Multiple Regression: Parallele Linien nach Schulentfremdung",
subtitle = "Bereinigter Effekt von ESCS ist gleich – Niveauunterschied durch Entfremdung",
x = "ESCS",
y = "Lesekompetenz"
) +
theme_minimal(base_size = 14)
## `geom_smooth()` using formula = 'y ~ x'
library(broom)
library(dplyr)
library(tidyr)
library(knitr)
library(kableExtra)
# Modelle
model1 <- lm(Lesekompetenz ~ ESCS, data = dat)
model2 <- lm(Lesekompetenz ~ ESCS + Schulentfremdung_bin, data = dat)
model1_tidy <- tidy(model1)
model2_tidy <- tidy(model2)
# Spalten sinnvoll benennen
model1_tidy <- model1_tidy %>%
mutate(Modell = "Modell 1")
model2_tidy <- model2_tidy %>%
mutate(Modell = "Modell 2")
# Zusammenführen
reg_tab <- bind_rows(model1_tidy, model2_tidy) %>%
select(Modell, term, estimate) %>%
mutate(
term = recode(term,
"(Intercept)" = "Intercept",
"ESCS" = "ESCS",
"Schulentfremdung_bin" = "Schulentfremdung"),
estimate = round(estimate, 3)
) %>%
pivot_wider(
names_from = Modell,
values_from = estimate
)
reg_tab <- reg_tab %>%
mutate(across(everything(), ~replace(., is.na(.), "-")))
# Ausgabe
kable(reg_tab,
col.names = c("Prädiktor", "Modell 1", "Modell 2"),
align = c("l","r","r"),
caption = "Regressionsergebnisse (Modelle nebeneinander)") %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| Prädiktor | Modell 1 | Modell 2 |
|---|---|---|
| Intercept | 460.307 | 499.213 |
| ESCS | 24.46 | 4.567 |
| Schulentfremdung |
|
-78.473 |
# Modellgüte
fit1 <- glance(model1)
fit2 <- glance(model2)
# Werte extrahieren
fit_tab <- data.frame(
Kennwert = c("R²", "Root MSE"),
`Modell 1` = c(
round(fit1$r.squared, 3),
round(fit1$sigma, 3)
),
`Modell 2` = c(
round(fit2$r.squared, 3),
round(fit2$sigma, 3)
)
)
# Modellgüte-Tabelle
kable(fit_tab,
col.names = c("Kennwert", "Modell 1", "Modell 2"),
align = c("l","r","r"),
caption = "Modellgüte (R² und Root MSE)") %>%
kable_styling(full_width = FALSE,
bootstrap_options = c("striped", "hover"))
| Kennwert | Modell 1 | Modell 2 |
|---|---|---|
| R² | 0.210 | 0.733 |
| Root MSE | 42.885 | 24.945 |
Ein zentraler Punkt – und für die Praxis vielleicht der wichtigste – ist, dass wir das wahre Modell nie kennen. Jede Regression, jede Glättung und jede Darstellung eines Zusammenhangs beruht auf Annahmen, die wir treffen. Manche dieser Annahmen können wir prüfen, viele jedoch nicht oder nur unvollständig. Und selbst wenn wir diagnostische Werkzeuge einsetzen, bleibt immer ein Rest Unsicherheit darüber, ob das Modell die Datenstruktur wirklich angemessen beschreibt.
Regressionen – egal ob einfach oder multipel – liefern daher keine Nachweise, sondern Hinweise. Sie beschreiben, wie die Daten unter den getroffenen Annahmen aussehen könnten. Die geschätzten Koeffizienten sind keine objektiven Eigenschaften der Welt, sondern Ergebnisse eines Modells, das wir gewählt haben.
In diesem Sinne geht es bei Regressionsmodellen nicht darum, „die Wahrheit“ zu finden, sondern darum, plausible Beschreibungen von Assoziationen zu erhalten. Modelle helfen uns dabei, die Hauptzusammenhänge zu erfassen („capturing the main associations“) und Strukturen sichtbar zu machen, die in rohen Tabellen oder Grafiken schwer erkennbar wären. Aber sie ersetzen nicht die kritische Reflexion darüber, warum ein Zusammenhang entsteht und ob er auch unter alternativen Modellannahmen bestehen würde.
Gerade deshalb ist es wichtig, sich bewusst zu machen:
Diese Haltung – kritisch, reflektiert und modellbewusst – ist entscheidend für jede empirische Analyse. Sie schützt davor, aus statistischen Ergebnissen mehr herauszulesen, als sie tatsächlich hergeben, und hilft gleichzeitig, die Stärken von Modellen sinnvoll zu nutzen: Strukturen sichtbar machen, Hypothesen systematisch diskutieren und Assoziationen beschreiben.