QQ-Plots

Verteilungen
Visualisierung
Interpretation
R
Python
Verteilungen grafisch vergleichen und typische Muster deuten.

Kernideen

  • Der QQ-Plot vergleicht beobachtete Quantile mit theoretischen.
  • Punkte auf der Geraden bedeuten Übereinstimmung mit der Referenzverteilung.
  • Er zeigt die Art der Abweichung; ein Test nur ihr Vorhandensein.
  • Die vier Muster: Krümmung heisst Schiefe, S-Form heisst schwere oder leichte Ränder.
  • Abweichungen an den Enden sind bei kleinen Stichproben normal.
  • Bei grossem \(n\) schlägt jeder Normalitätstest an, der Plot bleibt lesbar.
  • Die Referenzverteilung muss nicht die Normalverteilung sein.

Erklärung

Vorwissen: Lage- und Streuungsmasse für Quantile, denn der QQ-Plot besteht aus nichts anderem, und die Normalverteilung als übliche Referenz. Die zugehörigen Tests stehen unter Normalitätstests.

Wie ein QQ-Plot entsteht

Ob eine Verteilungsannahme passt, lässt sich am Histogramm nur grob beurteilen, weil dessen Aussehen von der Klassenzahl abhängt. Der QQ-Plot ist präziser und entsteht in drei Schritten:

  1. Die \(n\) Beobachtungen sortieren. Der kleinste Wert ist das empirische \(\tfrac{0.5}{n}\)-Quantil, der zweitkleinste das \(\tfrac{1.5}{n}\)-Quantil und so weiter.
  2. Zu jeder dieser Positionen das theoretische Quantil der Referenzverteilung berechnen.
  3. Beides gegeneinander auftragen.

Passt die Annahme, liegen die Punkte auf einer Geraden und zwar auf der mit Steigung \(\sigma\) und Achsenabschnitt \(\mu\). Die Gerade wird nicht angepasst, sie folgt aus den Daten.

Warum \(\tfrac{i - 0.5}{n}\) und nicht \(\tfrac{i}{n}\)? Weil sonst der grösste Wert das 100-Prozent-Quantil wäre, und das ist bei einer unbegrenzten Verteilung unendlich. Es gibt mehrere gebräuchliche Korrekturen; R verwendet bei kleinen Stichproben \(\tfrac{i - 3/8}{n + 1/4}\), scipy dieselbe Formel. Die Unterschiede sind in der Mitte unsichtbar und an den Rändern klein.

Die vier Muster

Bild Bedeutung Was in den Daten steckt
Punkte auf der Geraden Annahme passt
durchgehende Krümmung nach oben (links unter, rechts über der Linie in einem Bogen) rechtsschief ein langer Rand nach rechts
durchgehende Krümmung nach unten linksschief ein langer Rand nach links
S-Form: links unter, rechts über der Geraden schwerere Ränder mehr extreme Werte als erwartet
umgekehrte S-Form: links über, rechts unter leichtere Ränder weniger extreme Werte, etwa bei begrenzten Skalen

Der Unterschied zwischen Krümmung und S-Form ist der entscheidende: Eine Krümmung geht in eine Richtung, eine S-Form knickt an beiden Enden in entgegengesetzte Richtungen ab.

QQ-Plot oder Test?

QQ-Plot Normalitätstest
Sagt wie abgewichen wird ob abgewichen wird
Bei kleinem \(n\) schwer zu lesen findet fast nichts
Bei grossem \(n\) bleibt lesbar schlägt immer an
Braucht ein Urteil eine Schwelle

Die praktische Empfehlung ist eindeutig: Der Plot entscheidet, der Test ergänzt. Der Grund steht in Beispiel 4.

Beispiele

Frage und Datenlage

Zehn Messwerte. Der QQ-Plot wird Schritt für Schritt gebaut, damit klar wird, was auf den Achsen steht.

Rechnung

x <- round(qnorm(lehmer(1122, 10), mean = 20, sd = 4), 1)
sortiert <- sort(x)

n <- length(x)
position <- (seq_len(n) - 0.5) / n      # empirische Quantilstufen
theoretisch <- qnorm(position)          # theoretische Quantile

round(data.frame(rang = seq_len(n), wert = sortiert,
                 position = round(position, 4),
                 theoretisch = round(theoretisch, 4)), 4)
   rang wert position theoretisch
1     1 10.5     0.05     -1.6449
2     2 16.2     0.15     -1.0364
3     3 20.9     0.25     -0.6745
4     4 21.1     0.35     -0.3853
5     5 21.8     0.45     -0.1257
6     6 21.9     0.55      0.1257
7     7 22.4     0.65      0.3853
8     8 22.9     0.75      0.6745
9     9 23.1     0.85      1.0364
10   10 23.2     0.95      1.6449
par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
plot(theoretisch, sortiert, pch = 16, main = "von Hand",
     xlab = "theoretisches Quantil", ylab = "beobachteter Wert")
abline(a = mean(x), b = sd(x), lwd = 2)
qqnorm(x, pch = 16, main = "qqnorm()")
qqline(x, lwd = 2)
par(mfrow = c(1, 1))

round(c(mittel = mean(x), sd = sd(x),
        steigung_der_geraden = coef(lm(sortiert ~ theoretisch))[2]), 3)
                          mittel                               sd 
                          20.400                            4.027 
steigung_der_geraden.theoretisch 
                           3.399 
Abbildung 1: Beispiel 1: links der selbst gebaute QQ-Plot, rechts der von qqnorm().
x = np.round(stats.norm.ppf(lehmer(1122, 10), loc=20, scale=4), 1)
sortiert = np.sort(x)

n = len(x)
position = (np.arange(1, n + 1) - 0.5) / n
theoretisch = stats.norm.ppf(position)

print(pd.DataFrame({"rang": np.arange(1, n + 1), "wert": sortiert,
                    "position": position.round(4),
                    "theoretisch": theoretisch.round(4)}).to_string(index=False))
 rang  wert  position  theoretisch
    1  10.5      0.05      -1.6449
    2  16.2      0.15      -1.0364
    3  20.9      0.25      -0.6745
    4  21.1      0.35      -0.3853
    5  21.8      0.45      -0.1257
    6  21.9      0.55       0.1257
    7  22.4      0.65       0.3853
    8  22.9      0.75       0.6745
    9  23.1      0.85       1.0364
   10  23.2      0.95       1.6449
fig, achsen = plt.subplots(1, 2, figsize=(7, 3.2))
_ = achsen[0].scatter(theoretisch, sortiert, color="black")
gitter = np.array([theoretisch.min(), theoretisch.max()])
_ = achsen[0].plot(gitter, x.mean() + x.std(ddof=1) * gitter, lw=2, color="black")
_ = achsen[0].set_title("von Hand")
_ = achsen[0].set_xlabel("theoretisches Quantil")
_ = achsen[0].set_ylabel("beobachteter Wert")
_ = stats.probplot(x, dist="norm", plot=achsen[1])
_ = achsen[1].set_title("probplot()")
plt.tight_layout()
plt.show()
Abbildung 2: Beispiel 1: derselbe Aufbau in Python.

steigung, achsenabschnitt = np.polyfit(theoretisch, sortiert, 1)
print({"mittel": round(float(x.mean()), 3),
       "sd": round(float(x.std(ddof=1)), 3),
       "steigung_der_geraden": round(float(steigung), 3)})

Output Zeile für Zeile

Rang beobachteter Wert Position \(\tfrac{i-0.5}{10}\) theoretisches Quantil
1 10.5 0.05 -1.6449
2 16.2 0.15 -1.0364
3 20.9 0.25 -0.6745
4 21.1 0.35 -0.3853
5 21.8 0.45 -0.1257
6 21.9 0.55 0.1257
7 22.4 0.65 0.3853
8 22.9 0.75 0.6745
9 23.1 0.85 1.0364
10 23.2 0.95 1.6449
Ausgabe Wert hier Wie er zu lesen ist
Die Position ist der Rang, auf 0 bis 1 umgerechnet 0.05 bis 0.95 Der kleinste von zehn Werten steht für das 5-Prozent-Quantil, nicht für das 10-Prozent-Quantil und nicht für das Minimum.
Das theoretische Quantil ist \(\Phi^{-1}(\text{Position})\) -1.645 bis +1.645 Bei der Standardnormalverteilung. Die Werte sind symmetrisch, weil die Positionen es sind.
Die Steigung der Geraden 3.399 Ungefähr die Standardabweichung der Daten (4.027). Dass sie kleiner ausfällt, liegt an den beiden tiefen Werten links.
Der Achsenabschnitt 20.4 Genau der Mittelwert.
Die beiden linken Punkte liegen unter der Geraden 10.5 und 16.2 Bei zehn Beobachtungen ist das nicht deutbar, Beispiel 3 zeigt, wie stark die Ränder streuen.

Was der QQ-Plot dem Histogramm voraushat: Er braucht keine Klassenzahl. Ein Histogramm derselben zehn Werte sähe je nach Klassenbreite völlig verschieden aus; die zehn Punkte hier sind eindeutig bestimmt.

Und was er ihm nicht voraushat: Er zeigt die Verteilung nicht direkt. Wer sehen will, ob es zwei Gipfel gibt, braucht das Histogramm, im QQ-Plot äussert sich Zweigipfligkeit als schwer erkennbare Stufe.

Interpretation

Die zehn Punkte liegen ungefähr auf einer Geraden. Bei \(n = 10\) ist das aber kaum mehr als die Feststellung, dass nichts offensichtlich Falsches vorliegt.

Frage und Datenlage

Fünf Stichproben zu je 200 Werten, alle aus demselben Strom gleichverteilter Zufallszahlen über die jeweilige Quantilfunktion erzeugt: normal, rechtsschief, linksschief, mit schweren Rändern (t-Verteilung mit 3 Freiheitsgraden) und mit leichten Rändern (Gleichverteilung).

Rechnung

arten <- c("normal", "rechtsschief", "linksschief",
           "schwere_raender", "leichte_raender")

par(mfrow = c(2, 3), mar = c(4, 4, 3, 1))
for (art in arten) {
  d <- stichprobe(2233, art)
  qqnorm(d, pch = 16, cex = 0.5, main = art)
  qqline(d, lwd = 2)
}
plot.new()
par(mfrow = c(1, 1))

schiefe <- function(x) {
  z <- (x - mean(x)) / sqrt(mean((x - mean(x))^2))
  mean(z^3)
}
woelbung <- function(x) {
  z <- (x - mean(x)) / sqrt(mean((x - mean(x))^2))
  mean(z^4) - 3
}

for (art in arten) {
  d <- stichprobe(2233, art)
  cat(sprintf("%-16s Schiefe %+.3f | Wölbung %+.3f | Shapiro-Wilk p %.2e\n",
              art, schiefe(d), woelbung(d), shapiro.test(d)$p.value))
}
normal           Schiefe +0.073 | Wölbung -0.439 | Shapiro-Wilk p 3.16e-01
rechtsschief     Schiefe +1.363 | Wölbung +1.529 | Shapiro-Wilk p 5.96e-13
linksschief      Schiefe -1.363 | Wölbung +1.529 | Shapiro-Wilk p 5.96e-13
schwere_raender  Schiefe +0.832 | Wölbung +4.206 | Shapiro-Wilk p 7.18e-07
leichte_raender  Schiefe -0.038 | Wölbung -1.347 | Shapiro-Wilk p 1.20e-07
Abbildung 3: Beispiel 2: fünf Muster im QQ-Plot gegen die Normalverteilung.
arten = ["normal", "rechtsschief", "linksschief",
         "schwere_raender", "leichte_raender"]

fig, achsen = plt.subplots(2, 3, figsize=(7, 5))
for achse, art in zip(achsen.ravel(), arten):
    _ = stats.probplot(stichprobe(2233, art), dist="norm", plot=achse)
    _ = achse.set_title(art, fontsize=9)
    _ = achse.get_lines()[0].set_markersize(3)
_ = achsen.ravel()[-1].axis("off")
plt.tight_layout()
plt.show()
Abbildung 4: Beispiel 2: dieselben fünf Muster in Python.
for art in arten:
    d = stichprobe(2233, art)
    print(f"{art:16s} Schiefe {stats.skew(d):+.3f}"
          f" | Wölbung {stats.kurtosis(d):+.3f}"
          f" | Shapiro-Wilk p {stats.shapiro(d).pvalue:.2e}")
normal           Schiefe +0.073 | Wölbung -0.439 | Shapiro-Wilk p 3.16e-01
rechtsschief     Schiefe +1.363 | Wölbung +1.529 | Shapiro-Wilk p 5.96e-13
linksschief      Schiefe -1.363 | Wölbung +1.529 | Shapiro-Wilk p 5.96e-13
schwere_raender  Schiefe +0.832 | Wölbung +4.206 | Shapiro-Wilk p 7.18e-07
leichte_raender  Schiefe -0.038 | Wölbung -1.347 | Shapiro-Wilk p 1.20e-07

Output Zeile für Zeile

Stichprobe Schiefe Wölbung Shapiro-Wilk p Muster im Plot
normal +0.073 -0.439 0.316 Punkte auf der Geraden
rechtsschief +1.363 +1.529 6·10⁻¹³ Bogen nach oben
linksschief -1.363 +1.529 6·10⁻¹³ Bogen nach unten
schwere Ränder +0.832 +4.206 7·10⁻⁷ S-Form, beide Enden weg von der Geraden
leichte Ränder -0.038 -1.347 1·10⁻⁷ umgekehrte S-Form, beide Enden zur Geraden hin
Beobachtung Erklärung
Schiefe unterscheidet die beiden Bögen +1.363 gegen -1.363, exakt gespiegelt, die linksschiefe Stichprobe ist die rechtsschiefe mit umgekehrtem Vorzeichen.
Wölbung unterscheidet die beiden S-Formen +4.21 gegen -1.35. Positive Wölbung heisst schwerere Ränder als die Normalverteilung, negative leichtere. Die Normalverteilung hat definitionsgemäss die Wölbung 0.
Die leichten Ränder sind die Gleichverteilung Sie hat gar keine Ränder, bei 25 und 75 ist Schluss. Im QQ-Plot äussert sich das darin, dass die äussersten Punkte weniger extrem sind, als die Normalverteilung erwarten liesse.
Der Test unterscheidet nichts davon Vier p-Werte zwischen 10⁻⁷ und 10⁻¹³, alle “hochsignifikant nicht normal”, ohne dass daraus hervorginge, was los ist.

Warum die Art der Abweichung wichtiger ist als ihr Vorhandensein:

Abweichung Praktische Folge Was hilft
Schiefe Mittelwert ist nicht mehr der typische Wert; symmetrische Intervalle passen nicht Transformation (Logarithmus), oder Median und Rangverfahren
schwere Ränder mehr Ausreisser als erwartet; Mittelwert und Standardabweichung werden instabil robuste Verfahren, getrimmte Mittelwerte
leichte Ränder meist harmlos; Tests werden eher konservativ nichts

Ein Test, der nur “nicht normal” meldet, führt zu keiner dieser drei Entscheidungen. Der Plot schon.

Interpretation und Ergebnissatz

Die Stichprobe zeigt im QQ-Plot einen durchgehenden Bogen nach oben und eine Schiefe von +1.36. Die Abweichung von der Normalverteilung ist eine Rechtsschiefe; vor weiteren Auswertungen wird eine Logarithmierung geprüft.

Frage und Datenlage

Die häufigste Fehldeutung: Punkte, die an den Enden von der Geraden abweichen, werden für einen Befund gehalten. Sechs Stichproben aus derselben Normalverteilung, dreimal mit \(n = 20\), dreimal mit \(n = 200\).

Rechnung

par(mfrow = c(2, 3), mar = c(4, 4, 3, 1))
for (n in c(20, 200)) {
  for (k in 1:3) {
    d <- qnorm(lehmer(5000 + 10 * k + n, n))
    qqnorm(d, pch = 16, cex = 0.6, main = paste("n =", n))
    qqline(d, lwd = 2)
  }
}
par(mfrow = c(1, 1))

for (n in c(20, 200)) {
  for (k in 1:3) {
    d <- qnorm(lehmer(5000 + 10 * k + n, n))
    cat(sprintf("n = %3d, Stichprobe %d | Schiefe %+.3f | Shapiro-Wilk p %.3f\n",
                n, k, schiefe(d), shapiro.test(d)$p.value))
  }
}
n =  20, Stichprobe 1 | Schiefe -1.068 | Shapiro-Wilk p 0.011
n =  20, Stichprobe 2 | Schiefe -0.289 | Shapiro-Wilk p 0.640
n =  20, Stichprobe 3 | Schiefe +0.494 | Shapiro-Wilk p 0.525
n = 200, Stichprobe 1 | Schiefe +0.086 | Shapiro-Wilk p 0.279
n = 200, Stichprobe 2 | Schiefe +0.287 | Shapiro-Wilk p 0.199
n = 200, Stichprobe 3 | Schiefe +0.093 | Shapiro-Wilk p 0.174
Abbildung 5: Beispiel 3: sechsmal dieselbe Normalverteilung. Oben n = 20, unten n = 200.
fig, achsen = plt.subplots(2, 3, figsize=(7, 4.6))
for zeile, n in enumerate([20, 200]):
    for k in range(1, 4):
        d = stats.norm.ppf(lehmer(5000 + 10 * k + n, n))
        _ = stats.probplot(d, dist="norm", plot=achsen[zeile, k - 1])
        _ = achsen[zeile, k - 1].set_title(f"n = {n}", fontsize=9)
        _ = achsen[zeile, k - 1].get_lines()[0].set_markersize(3)
plt.tight_layout()
plt.show()
Abbildung 6: Beispiel 3: dieselben sechs Stichproben in Python.
for n in [20, 200]:
    for k in range(1, 4):
        d = stats.norm.ppf(lehmer(5000 + 10 * k + n, n))
        print(f"n = {n:3d}, Stichprobe {k} | Schiefe {stats.skew(d):+.3f}"
              f" | Shapiro-Wilk p {stats.shapiro(d).pvalue:.3f}")
n =  20, Stichprobe 1 | Schiefe -1.068 | Shapiro-Wilk p 0.011
n =  20, Stichprobe 2 | Schiefe -0.289 | Shapiro-Wilk p 0.640
n =  20, Stichprobe 3 | Schiefe +0.494 | Shapiro-Wilk p 0.525
n = 200, Stichprobe 1 | Schiefe +0.086 | Shapiro-Wilk p 0.279
n = 200, Stichprobe 2 | Schiefe +0.287 | Shapiro-Wilk p 0.199
n = 200, Stichprobe 3 | Schiefe +0.093 | Shapiro-Wilk p 0.174

Output Zeile für Zeile

n Stichprobe Schiefe Shapiro-Wilk p
20 1 -1.068 0.011
20 2 -0.289 0.640
20 3 +0.494 0.525
200 1 +0.086 0.279
200 2 +0.287 0.199
200 3 +0.093 0.174
Beobachtung Erklärung
Alle sechs Stichproben stammen aus exakt derselben Normalverteilung Jede Abweichung von der Geraden ist damit definitionsgemäss Zufall.
Bei \(n = 20\) schwankt die Schiefe zwischen -1.07 und +0.49 In jeder der drei Stichproben anders. Wer eine davon einzeln sähe, könnte “linksschief” oder “schwere Ränder” hineinlesen.
Bei \(n = 200\) liegt sie zwischen +0.09 und +0.29 Deutlich enger, und die Punkte liegen im Bild näher an der Geraden, am weitesten entfernt noch immer an den Enden.
Die erste Stichprobe bei \(n = 20\) hat p = 0.011 Ein Fehlalarm: Der Test verwirft die Normalverteilung, obwohl die Daten aus ihr stammen. Genau das ist der Fehler erster Art, und bei einem Niveau von 5 Prozent passiert er bei einer von zwanzig Stichproben. Hier bei einer von sechs, auch das ist Zufall.
Die übrigen fünf p-Werte liegen zwischen 0.17 und 0.64 Unauffällig, wie zu erwarten.

Warum die Ränder immer stärker streuen, ist eine einfache Rechnung: Der grösste von \(n\) Werten hat eine Verteilung, deren Streuung mit \(n\) wächst, er kann beliebig weit hinausrutschen. Der Median dagegen wird mit wachsendem \(n\) immer genauer bestimmt.

Deshalb sind die Punkte in der Mitte eines QQ-Plots verlässlich und die an den Enden nicht. Manche Umsetzungen zeichnen deshalb ein Konfidenzband ein, in R etwa car::qqPlot(), das die Punktweise-Streuung berücksichtigt. Wo es fehlt, muss man es im Kopf haben.

Die praktische Faustregel: Unter etwa 30 Beobachtungen ist ein QQ-Plot nur für grobe Befunde brauchbar. Was dort wie eine Abweichung aussieht, ist meist keine.

Interpretation

Ein QQ-Plot wird nicht Punkt für Punkt gelesen, sondern auf ein durchgehendes Muster hin. Ein einzelner Ausreisser am Rand ist keins und ein einzelner p-Wert unter 0.05 auch nicht, wie die erste Stichprobe zeigt.

Frage und Datenlage

Eine Verteilung, die von der Normalverteilung praktisch bedeutungslos abweicht: eine log-normale mit \(\sigma = 0.15\), also eine Schiefe von rund 0.45. Getestet bei wachsender Stichprobengrösse.

Rechnung

leicht_schief <- function(n) exp(0.15 * qnorm(lehmer(4455, n)))

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
for (n in c(50, 5000)) {
  d <- leicht_schief(n)
  qqnorm(d, pch = 16, cex = 0.4, main = paste("n =", n))
  qqline(d, lwd = 2)
}
par(mfrow = c(1, 1))

for (n in c(20, 50, 200, 1000, 5000)) {
  d <- leicht_schief(n)
  rein <- qnorm(lehmer(3344, n))
  cat(sprintf("n = %4d | leicht schief: Schiefe %+.3f, p %.2e | rein normal: p %.3f\n",
              n, schiefe(d), shapiro.test(d)$p.value,
              shapiro.test(rein)$p.value))
}
n =   20 | leicht schief: Schiefe +1.502, p 1.51e-02 | rein normal: p 0.723
n =   50 | leicht schief: Schiefe +1.480, p 4.75e-04 | rein normal: p 0.077
n =  200 | leicht schief: Schiefe +0.836, p 2.62e-05 | rein normal: p 0.733
n = 1000 | leicht schief: Schiefe +0.531, p 1.03e-08 | rein normal: p 0.317
n = 5000 | leicht schief: Schiefe +0.479, p 2.18e-20 | rein normal: p 0.538
Abbildung 7: Beispiel 4: dieselbe leicht schiefe Verteilung bei n = 50 und n = 5000.
def leicht_schief(n):
    return np.exp(0.15 * stats.norm.ppf(lehmer(4455, n)))


fig, achsen = plt.subplots(1, 2, figsize=(7, 3.2))
for achse, n in zip(achsen, [50, 5000]):
    _ = stats.probplot(leicht_schief(n), dist="norm", plot=achse)
    _ = achse.set_title(f"n = {n}", fontsize=9)
    _ = achse.get_lines()[0].set_markersize(2)
plt.tight_layout()
plt.show()
Abbildung 8: Beispiel 4: dieselbe Gegenüberstellung in Python.
for n in [20, 50, 200, 1000, 5000]:
    d = leicht_schief(n)
    rein = stats.norm.ppf(lehmer(3344, n))
    print(f"n = {n:4d} | leicht schief: Schiefe {stats.skew(d):+.3f},"
          f" p {stats.shapiro(d).pvalue:.2e}"
          f" | rein normal: p {stats.shapiro(rein).pvalue:.3f}")
n =   20 | leicht schief: Schiefe +1.502, p 1.51e-02 | rein normal: p 0.723
n =   50 | leicht schief: Schiefe +1.480, p 4.75e-04 | rein normal: p 0.077
n =  200 | leicht schief: Schiefe +0.836, p 2.62e-05 | rein normal: p 0.733
n = 1000 | leicht schief: Schiefe +0.531, p 1.03e-08 | rein normal: p 0.317
n = 5000 | leicht schief: Schiefe +0.479, p 2.18e-20 | rein normal: p 0.538

Output Zeile für Zeile

n Schiefe Shapiro-Wilk p (leicht schief) Shapiro-Wilk p (rein normal)
20 +1.502 0.015 0.723
50 +1.480 4.8·10⁻⁴ 0.077
200 +0.836 2.6·10⁻⁵ 0.733
1 000 +0.531 1.0·10⁻⁸ 0.317
5 000 +0.479 2.2·10⁻²⁰ 0.538
Beobachtung Erklärung
Die Schiefe sinkt mit wachsendem n Von 1.50 auf 0.48, sie nähert sich dem wahren Wert der Verteilung an. Bei kleinen Stichproben ist die geschätzte Schiefe selbst sehr unsicher.
Der p-Wert fällt um 18 Grössenordnungen Und die Verteilung ändert sich dabei nicht. Was wächst, ist allein die Fähigkeit des Tests, eine feste Abweichung zu entdecken.
Bei \(n = 5000\) ist \(p = 2 \cdot 10^{-20}\) Für eine Verteilung, die im QQ-Plot fast gerade aussieht und deren Schiefe unter 0.5 liegt. Statistisch hochsignifikant, praktisch bedeutungslos.
Die rein normale Stichprobe bleibt bei jedem n unauffällig 0.08 bis 0.73. Der Test macht also keinen Fehler, er entdeckt korrekt, was da ist.
Die beiden QQ-Plots sehen bei n = 50 und n = 5000 fast gleich aus. Nur der zweite hat mehr Punkte.

Das ist keine Schwäche des Tests, sondern seine Eigenschaft. Ein Normalitätstest prüft die Nullhypothese “exakt normalverteilt”, und die ist bei echten Daten praktisch immer falsch. Mit genügend Beobachtungen wird jede noch so kleine Abweichung nachweisbar.

Daraus folgt die Umkehrung, die genauso wichtig ist: Bei kleinen Stichproben findet der Test nichts, gerade dort, wo die Normalitätsannahme am meisten zählt, denn bei grossem \(n\) sorgt der zentrale Grenzwertsatz ohnehin dafür, dass Mittelwertverfahren funktionieren.

Der Test ist also genau dort blind, wo man ihn bräuchte, und genau dort empfindlich, wo man ihn nicht braucht. Deshalb: Plot ansehen, Grössenordnung der Abweichung beurteilen, und die Frage stellen, ob das gewählte Verfahren darauf empfindlich reagiert.

Interpretation und Ergebnissatz

Die Normalitätsannahme ist formal verletzt (Shapiro-Wilk p < .001 bei n = 5000), die Abweichung ist mit einer Schiefe von 0.48 jedoch gering und im QQ-Plot kaum erkennbar. Für Verfahren, die auf Mittelwerten beruhen, ist sie bei dieser Stichprobengrösse ohne Belang.

Frage und Datenlage

Der QQ-Plot ist nicht auf die Normalverteilung festgelegt. Getestet wird eine Stichprobe von Wartezeiten gegen zwei Referenzen: die Normalverteilung und die Exponentialverteilung.

Rechnung

wartezeit <- -log(lehmer(6677, 300)) / 0.5      # exponential, Rate 0.5

n <- length(wartezeit)
position <- (seq_len(n) - 0.5) / n

par(mfrow = c(1, 2), mar = c(4, 4, 3, 1))
qqnorm(wartezeit, pch = 16, cex = 0.5, main = "Referenz: Normalverteilung")
qqline(wartezeit, lwd = 2)

plot(qexp(position, rate = 0.5), sort(wartezeit), pch = 16, cex = 0.5,
     main = "Referenz: Exponential", xlab = "theoretisches Quantil",
     ylab = "beobachteter Wert")
abline(a = 0, b = 1, lwd = 2)
par(mfrow = c(1, 1))

round(c(mittel = mean(wartezeit), sd = sd(wartezeit),
        schiefe = schiefe(wartezeit),
        erwartete_schiefe_exponential = 2), 3)
                       mittel                            sd 
                        2.054                         1.957 
                      schiefe erwartete_schiefe_exponential 
                        1.858                         2.000 
Abbildung 9: Beispiel 5: dieselben Wartezeiten gegen zwei Referenzverteilungen.
wartezeit = -np.log(lehmer(6677, 300)) / 0.5

n = len(wartezeit)
position = (np.arange(1, n + 1) - 0.5) / n

fig, achsen = plt.subplots(1, 2, figsize=(7, 3.2))
_ = stats.probplot(wartezeit, dist="norm", plot=achsen[0])
_ = achsen[0].set_title("Referenz: Normalverteilung", fontsize=9)
_ = achsen[0].get_lines()[0].set_markersize(3)

_ = achsen[1].scatter(stats.expon.ppf(position, scale=2), np.sort(wartezeit),
                  s=8, color="black")
grenze = [0, np.sort(wartezeit).max()]
_ = achsen[1].plot(grenze, grenze, lw=2, color="black")
_ = achsen[1].set_title("Referenz: Exponential", fontsize=9)
_ = achsen[1].set_xlabel("theoretisches Quantil")
_ = achsen[1].set_ylabel("beobachteter Wert")
plt.tight_layout()
plt.show()
Abbildung 10: Beispiel 5: dieselbe Gegenüberstellung in Python.

print({"mittel": round(float(wartezeit.mean()), 3),
       "sd": round(float(wartezeit.std(ddof=1)), 3),
       "schiefe": round(float(stats.skew(wartezeit)), 3),
       "erwartete_schiefe_exponential": 2})

Output Zeile für Zeile

Referenz Bild Schluss
Normalverteilung deutlicher Bogen nach oben Die Annahme passt nicht.
Exponentialverteilung Punkte auf der Winkelhalbierenden Die Annahme passt.
Kennzahl beobachtet erwartet bei Exponential mit Rate 0.5
Mittelwert 2.054 \(1/0.5 = 2\)
Standardabweichung 1.957 ebenfalls \(1/0.5 = 2\)
Schiefe 1.858 genau 2, unabhängig von der Rate
Beobachtung Erklärung
Bei der Exponentialverteilung ist die Referenzgerade die Winkelhalbierende Weil die Rate hier bekannt vorgegeben wurde. Wäre sie zu schätzen, ergäbe sich wie beim Normalfall eine Gerade mit anderer Steigung.
Mittelwert gleich Standardabweichung Die charakteristische Eigenschaft der Exponentialverteilung. Sie allein ist schon ein brauchbarer erster Hinweis.
Die Schiefe von 2 ist fest Bei jeder Rate. Auch das ist prüfbar, ohne einen Plot zu zeichnen.

Wann sich eine andere Referenz lohnt:

Referenz Typischer Anlass
Exponential Wartezeiten, Lebensdauern, Abstände zwischen Ereignissen
Weibull Ausfallzeiten mit Alterung, siehe Zensierung
log-normal Löhne, Preise, alles multiplikativ Entstehende
t-Verteilung Residuen mit mehr Ausreissern als erwartet
Gleichverteilung p-Werte unter der Nullhypothese, siehe Hypothesentests

Der letzte Fall ist der praktisch nützlichste: Ein QQ-Plot der p-Werte aus vielen Tests gegen die Gleichverteilung zeigt sofort, ob mehr kleine p-Werte auftreten als unter der Nullhypothese zu erwarten.

Interpretation und Ergebnissatz

Die Wartezeiten sind nicht normalverteilt (deutlicher Bogen im QQ-Plot, Schiefe 1.86), passen aber gut zu einer Exponentialverteilung mit Rate 0.5: Mittelwert (2.05) und Standardabweichung (1.96) stimmen wie erwartet überein, und im QQ-Plot gegen diese Referenz liegen die Punkte auf der Winkelhalbierenden.

Verständnisfragen

Ein QQ-Plot zeigt Punkte, die links unterhalb und rechts oberhalb der Geraden liegen, also eine S-Form. Was heisst das?

Die Daten sind rechtsschief
Schiefe zeigt sich als durchgehender Bogen in eine Richtung, nicht als Abknicken an beiden Enden.
Die Verteilung hat schwerere Ränder als die Normalverteilung
Richtig. An beiden Enden liegen extremere Werte, als das Modell erwarten lässt. Praktisch heisst das mehr Ausreisser, was Verfahren stört, die auf dem Mittelwert beruhen.
Die Stichprobe ist zu klein
Bei kleinen Stichproben streuen die Ränder stärker, aber dann in beide Richtungen zufällig, nicht systematisch.

Bei n = 5000 ergibt ein Shapiro-Wilk-Test p = 2·10⁻²⁰, der QQ-Plot sieht aber fast gerade aus. Was folgt daraus?

Die Abweichung ist nachweisbar, aber klein
Richtig. Mit wachsender Stichprobe wird jede noch so kleine Abweichung signifikant, weil die Nullhypothese “exakt normal” bei echten Daten praktisch immer falsch ist. Entscheidend ist die Grössenordnung, nicht der p-Wert.
Der Plot ist falsch gezeichnet
Beide zeigen dieselben Daten korrekt.
Die Daten müssen transformiert werden
Nicht wegen des p-Werts. Ob eine Transformation nötig ist, entscheidet die Grösse der Abweichung und die Empfindlichkeit des geplanten Verfahrens.

Sechs QQ-Plots aus derselben Normalverteilung sehen an den Rändern verschieden aus. Warum?

Die äussersten Punkte streuen am stärksten, weil dort die wenigsten Beobachtungen liegen
Richtig. Der grösste von n Werten hat eine breite Verteilung, der Median eine enge. Deshalb wird ein QQ-Plot auf ein durchgehendes Muster hin gelesen, nicht Punkt für Punkt.
Der Zufallszahlengenerator ist ungeeignet
Alle sechs stammen aus derselben Verteilung; die Unterschiede sind der Zufall selbst.
Die Stichproben sind unterschiedlich gross
Je drei sind gleich gross, und auch innerhalb dieser drei unterscheiden sich die Ränder.

Wozu dient die Position \((i - 0.5)/n\) statt \(i/n\)?

Sonst wäre der grösste Wert das 100-Prozent-Quantil, das bei unbegrenzten Verteilungen unendlich ist
Richtig. Die Korrektur setzt jeden Rang in die Mitte seines Anteilsintervalls. Es gibt mehrere gebräuchliche Varianten, die sich nur an den Rändern leicht unterscheiden.
Um die Punkte gleichmässiger zu verteilen
Sie sind so oder so gleichmässig verteilt.
Um den Mittelwert zu treffen
Mit dem Mittelwert hat die Korrektur nichts zu tun.

Eine Stichprobe hat Mittelwert 2.0, Standardabweichung 2.0 und Schiefe 2.0. Welche Verteilung liegt nahe?

Die Exponentialverteilung
Richtig. Bei ihr sind Mittelwert und Standardabweichung immer gleich, und die Schiefe beträgt unabhängig von der Rate genau 2. Diese drei Kennzahlen ersetzen den QQ-Plot zwar nicht, sind aber ein starker Hinweis, im Beispiel ergeben sich 2.05, 1.96 und 1.86.
Die Normalverteilung
Ihre Schiefe ist null.
Die Gleichverteilung
Ihre Schiefe ist ebenfalls null, und ihre Standardabweichung ist deutlich kleiner als ihr Mittelwert.

Verlinkte Ressourcen