·  METHODEN-NOTIZEN
KENNZAHLEN · QUALITÄTSSICHERUNG · R

Kennzahlenberichte in R automatisieren: Funnel Plots statt fester Schwellenwerte

Methoden-Notizen. Verfahren, die eine Entscheidung tragen, mit der Prüfgröße, die dazugehört.
10. September 2026 · ~10 Min · Nico Schäfer
ZIELSTELLUNG UND ERGEBNIS

Automatisierung skaliert den Bericht und den methodischen Fehler darin gleich mit. Ein fester Referenzwert erzeugt im simulierten Beispiel 2,05 Fehlalarme je Bericht, fallzahlabhängige Grenzen 0,16. Fast neun von zehn dieser Fehlalarme treffen Einheiten aus der kleineren Hälfte.

Das Praxisproblem

Ein monatlich automatisch laufender Kennzahlenbericht spart Handarbeit, Copy-Paste-Fehler und Formatdiskussionen. Genau deshalb liest ihn irgendwann niemand mehr gegen.

Und irgendwo in diesem Bericht steht eine Zeile wie diese:

auffaellig <- ergebnis < referenz

Sie ist schnell geschrieben und sofort erklärbar. Sie hat nur einen Nachteil: Sie behandelt eine Einheit mit 30 Fällen genauso wie eine mit 900.

Das ist keine Feinheit. Wenn die wahre Qualität einer Einheit bei 0,86 liegt und der Referenzwert bei 0,80, dann rutscht eine Einheit mit 30 Fällen allein durch Zufall mit Wahrscheinlichkeit 0,117 unter die Marke. Bei 900 Fällen passiert das praktisch nie:

library(dplyr)

tibble(n = c(30, 60, 120, 300, 900)) |>
  mutate(p_fehlalarm = pbinom(ceiling(0.80 * n) - 1, n, 0.86))
Fallzahl n3060120300900
P(fälschlich auffällig)0,1170,0690,0260,0020,000

binomial, wahre Qualität 0,86, Referenzwert 0,80

Wahrscheinlichkeit einer fälschlichen Auffälligkeit in Abhängigkeit von der Fallzahl p(faelschlich auffaellig) bei wahrer qualitaet 0.86, referenzwert 0.80 0.00 0.03 0.06 0.09 0.117 n = 30 0.069 n = 60 0.026 n = 120 0.002 n = 300 0.000 n = 900 dieselbe wahre qualitaet, nur die fallzahl unterscheidet sich
Dieselbe wahre Qualität, derselbe Referenzwert: Eine kleine Einheit wird mit rund 12 Prozent Wahrscheinlichkeit auffällig, eine große praktisch nie. Der feste Schwellenwert misst hier die Fallzahl und nicht die Leistung.

Der Bericht sagt also nicht „diese Einheit ist schlecht". Er sagt „diese Einheit ist klein". Und weil er jeden Monat läuft, sagt er das jeden Monat neu.

Goldstein und Spiegelhalter haben die statistischen Grenzen von Einrichtungsvergleichen 1996 beschrieben. Anhøj und Hellesøe haben 2017 in BMJ Quality & Safety nachgelegt: Ampellogik über feste Zielwerte lenkt von Zufallsvariation ab, statt sie sichtbar zu machen. Und Schmidtke und Kollegen haben im selben Jahr nachgesehen, wie oft das in der Praxis berücksichtigt wird: In 30 englischen NHS-Board-Berichten fanden sie 1.488 Diagramme, von denen 88, also sechs Prozent, überhaupt darstellten, welche Rolle der Zufall spielt.

Die deutsche Qualitätssicherung im Gesundheitswesen stellt seit 2022 schrittweise um, weg vom festen Schwellenwert und hin zu einer fallzahlabhängigen Einstufung, Verfahren für Verfahren. Die Begründung dort lautet, dass die Variabilität einer Kennzahl fallzahlabhängig ist und ihr Ignorieren dazu führt, dass allein aus stochastischen Gründen bei Leistungserbringern mit geringer Fallzahl viele Auffälligkeiten auftreten. Die Einstufung selbst läuft in diesem Rahmen bayesianisch; der Funnel Plot dient der Darstellung. In automatisierten Berichten außerhalb der Qualitätssicherung steht die Zeile von oben weiter drin.

Die Idee in einem Satz

Die Vergleichsgrenze muss mit der Fallzahl mitwachsen: eng bei großen Einheiten, weit bei kleinen, und um genau den Betrag weiter, den die Streuung tatsächlich hergibt.

Trägt man die Grenzen über der Fallzahl auf, ergibt sich ein Trichter. Daher der Name.

Ein Satz Abgrenzung, weil der Begriff auf Deutsch belegt ist: Wer „Funnel Plot" sucht, landet fast immer beim Trichterdiagramm der Meta-Analyse: Effektschätzer gegen Präzision, Asymmetrie als Hinweis auf Publikationsbias, Egger-Test. Das ist ein anderes Verfahren mit demselben Bild. Hier geht es nicht um Asymmetrie über Studien hinweg, sondern darum, ob eine einzelne Einheit ihre fallzahlabhängige Kontrollgrenze verlässt. Auch in R sind das zwei getrennte Paketfamilien: metaviz und metafor für die Meta-Analyse, FunnelPlotR und controlcharts für den Einrichtungsvergleich.

Der Code

50 Einheiten, sehr unterschiedliche Fallzahlen (Median 190, Spanne 25 bis 1213). Die wahre Qualität liegt bei 0,86 und streut wenig, außer bei zwei Einheiten, die tatsächlich schlechter sind (0,72). Im simulierten Datensatz ist bekannt, welche beiden das sind. Im Betrieb ist es das nicht, und genau darum geht es.

funnel_kennzahlen.R
library(dplyr)
set.seed(2026)

N <- 50; ZIELWERT <- 0.86; TAU <- 0.015
REFERENZ <- 0.80          # der Wert, gegen den naiv geprueft wird
AUSREISSER <- c(8, 24)    # diese beiden sind wirklich schlechter
Z <- 3.09                 # ~99,8 %, Konvention nach Spiegelhalter (2005)

n_faelle <- pmin(pmax(round(exp(rnorm(N, log(180), 1.0))), 25), 2000)
p_wahr   <- pmin(pmax(rnorm(N, ZIELWERT, TAU), 0.01), 0.99)
p_wahr[AUSREISSER] <- 0.72

dat <- tibble(
  einheit  = sprintf("E%02d", seq_len(N)),
  n_faelle = n_faelle,
  erfuellt = rbinom(N, n_faelle, p_wahr)
) |>
  mutate(ergebnis = erfuellt / n_faelle)

Weg A: der Schwellenwert. Eine Zeile, kein Modell, keine Unsicherheit:

dat <- dat |> mutate(auffaellig_schwelle = ergebnis < REFERENZ)

Weg B: Funnel-Grenzen mit Overdispersion-Korrektur. Erst der gepoolte Zielwert, dann der Standardfehler je Einheit, dann φ:

theta_hat <- dat |>
  summarise(theta = sum(erfuellt) / sum(n_faelle)) |>
  pull(theta)

dat <- dat |>
  mutate(se = sqrt(theta_hat * (1 - theta_hat) / n_faelle),
         z  = (ergebnis - theta_hat) / se)

# phi faengt die Streuung auf, die ueber die reine Binomialstreuung hinausgeht:
# unbeobachtete Risikofaktoren, Fallmix, alles, was das Modell nicht kennt.
# Winsorisierung bei 10/90 %, damit die echten Ausreisser phi nicht aufblasen;
# sonst versteckt das Verfahren genau das, was es finden soll.
phi <- dat |>
  mutate(z_w = pmin(pmax(z, quantile(z, .10)), quantile(z, .90))) |>
  summarise(phi = max(mean(z_w^2), 1)) |>
  pull(phi)

dat <- dat |>
  mutate(untere_grenze     = theta_hat - Z * se * sqrt(phi),
         auffaellig_funnel = ergebnis < untere_grenze)

Das war es. theta_hat = 0,8509, phi = 1,01 in diesem Lauf.

Drei Entscheidungen fallen dabei an und gehören dokumentiert: welche Konstruktion der Kontrollgrenzen, welches z, ob und wie winsorisiert wird. Beiwerk ist das nicht. Verburg und Kollegen haben 2018 eine Leitlinie dazu geschrieben, weil in Anwendungsstudien regelmäßig unklar bleibt, welche Variante verwendet wurde; verschiedene Wahlen führen zu verschiedenen Ergebnissen. Wer es produktiv einsetzt, schreibt die Wahl in den Bericht.

Das Ergebnis

Einzellauf. Der Schwellenwert markiert 2 Einheiten, die Funnel-Grenzen 1. Beide markierten Einheiten des Schwellenwerts sind echte Ausreißer, die des Funnels ebenfalls. Hier tut noch niemandem etwas weh. Ein Einzellauf trägt aber keine Aussage. Also 1000 Wiederholungen:

fester SchwellenwertFunnel-Grenzen
Fehlalarme je Bericht2,0500,159 (Faktor 12,9)
davon in der kleineren Hälfte der Einheiten87,6 %27,0 %
gefundene echte Ausreißer (von 2)1,9261,552

φ über die 1000 Läufe: Median 1,135, Spanne 1,000 bis 2,366.

Zwei Zahlen zum Mitnehmen. Erstens: Der Schwellenwert erzeugt fast dreizehnmal so viele Fehlalarme. Zweitens, und das ist die eigentliche Pointe: fast neun von zehn dieser Fehlalarme treffen Einheiten aus der kleineren Hälfte, also aus der Hälfte mit einer Fallzahl unter dem Median. Bei größenneutralen Fehlalarmen wären 50 Prozent zu erwarten. Beim Funnel sind es 27 Prozent, also sogar weniger: Die Grenzen werden bei kleiner Fallzahl so weit, dass dort kaum noch etwas markiert wird.

Und genau das ist der Preis. Der Schwellenwert findet mehr echte Ausreißer: 1,926 gegen 1,552 von zwei, ein Rückgang um 19 Prozent. Wer die Fehlalarme loswerden will, verliert Trennschärfe, und zwar dort, wo sie ohnehin am schwächsten ist. Seaton und Kollegen haben 2013 genau das untersucht: Die Wahrscheinlichkeit, dass ein tatsächlich schlechter Leistungserbringer im Funnel Plot auffällt, ist niedrig, besonders bei kleinen Fallzahlen. Der Trichter schützt die kleinen Einheiten vor falschen Anschuldigungen. Er versteckt sie damit auch.

Wer den Rückgang der Trefferquote weglässt, berichtet das halbe Ergebnis.

Ein Wort zu φ. In diesem Beispiel liegt es bei 1,01, im Median über 1000 Läufe bei 1,135. Die Overdispersion-Korrektur greift hier also kaum. Der ganze Gewinn kommt aus der Fallzahlabhängigkeit der Grenzen. φ ist die Versicherung für den anderen Fall: Daten, in denen mehr streut, als das Binomialmodell erlaubt, weil Fallmix und unbeobachtete Risikofaktoren mitspielen. In echten Qualitätsdaten ist das die Regel. Im simulierten Beispiel ist es das nicht, und das gehört dazugesagt.

So nutze ich das produktiv

Drei Dinge ändern sich, wenn das nicht in einem Skript steht, sondern in einer Pipeline, die monatlich läuft.

Erstens: das Ergebnis-Objekt vom Rendering trennen. Nicht der Bericht rechnet, sondern eine Funktion, die ein geprüftes Ergebnis-Objekt zurückgibt; der Bericht rendert es nur noch. Das ist der Unterschied zwischen „wir haben die Zahl im Bericht gefunden" und „wir können die Zahl testen". In der klinischen Statistik heißt das Muster Analysis Results Data; die Pakete cards und gtsummary bauen darauf auf. Praktisch heißt es: berechne_auffaelligkeiten() gibt einen Data Frame zurück, testthat prüft ihn, und erst danach kommt Word ins Spiel.

Zweitens: die Grenze zeichnen. Für den Bericht statisch: FunnelPlotR (Mainey, NHS-R Community) implementiert Spiegelhalters Methode und nimmt einem laut Paketbeschreibung die additive Overdispersion-Korrektur samt Winsorisierung ab. Für die interaktive Anlage controlcharts (Johnson mit dem Healthcare Quality Intelligence Unit, Western Australia Health, seit August 2026 auf CRAN): rechnet und zeichnet laut Beschreibung vollständig in JavaScript und läuft damit ohne Shiny-Server in jedem HTML-Bericht. Daraus wird eine Entscheidungsregel: statisch, was gedruckt wird, interaktiv, was geklickt wird.

Drittens: der Weg nach Word. Verlangt der Auftraggeber ein .docx, gibt es drei Wege, und die Wahl ist eine Entscheidung und kein Geschmack:

WegWannGrenze
officer programmatischvolle Kontrolle über das Layout, Berichte mit variabler Strukturjeder Absatz ist Code
R Markdown + officedownwiederkehrende Berichte mit fester Struktur, Corporate Design über Referenz-.docxunterstützt kein Quarto
Quarto + Referenz-.docxwenn der Rest des Hauses ohnehin Quarto istCross-Referenzen bleiben Klartext, Deckblatt ungelöst

Die Faustregel, die sich bei mir durchgesetzt hat: feste Struktur und Corporate Design → officedown; variable Struktur oder Anlagen, die sich je Lauf unterscheiden → officer; Quarto nur, wenn Word nicht das primäre Ziel ist.

WAS DAS FÜR DIE PIPELINE HEISST

Automatisierung skaliert den Bericht und den methodischen Fehler darin gleich mit. Eine Pipeline, die monatlich 40 Berichte erzeugt, kommt bei 2,05 Fehlalarmen je Bericht auf gut 80 im Monat und knapp tausend im Jahr. Das sind Gespräche, die nicht hätten stattfinden müssen, überwiegend mit den kleinen Einheiten.

Fazit

Ein fester Schwellenwert verwechselt Ergebnis und Fallzahl. Fallzahlabhängige Grenzen kosten rund zwanzig Zeilen R und senken die Fehlalarme im Beispiel um den Faktor 12,9, für den Preis von 19 Prozent Trefferquote, die man beim Namen nennen muss.

Die Frage vor dem nächsten automatisierten Bericht lautet deshalb nicht „welches Werkzeug?", sondern: Wonach entscheidet die Bewertungszeile, nach der Leistung oder nach der Größe?

Drei Anlässe, bei denen sich das lohnt

AUS DERSELBEN REIHE

Decision Curve Analysis in R: wann ein Modell eine Entscheidung wirklich verbessert. Warum zwei Modelle mit identischer AUC unterschiedlich nützlich sein können.

Vorhersageintervalle für XGBoost-Prognosen: Conformal Prediction in R. Prognoseintervalle mit nachprüfbarer Überdeckung.

ÄHNLICHE FRAGESTELLUNG?

Ein automatisierter Bericht, der Einheiten sehr unterschiedlicher Größe vergleicht: Im 15-minütigen Erstgespräch kläre ich, welche Prüfgröße dazugehört und wie sie in den Betrieb kommt.

15-MIN-TERMIN BUCHEN →

Wie Kennzahlenberichte bei sove.it gebaut und in Betrieb genommen werden: automatisiertes Reporting und produktive R-Anwendungen.

Alle Zahlen stammen aus einem tatsächlich gelaufenen Beispiel mit simulierten Daten (50 Einheiten, Median 190 Fälle, Spanne 25 bis 1213, set.seed(2026), 1000 Wiederholungen): keine Projektergebnisse und keine illustrativen Werte. Der Lauf erfolgte am 01.09.2026 in einem Container mit R 4.1.2 und den Ubuntu-Paketständen von 2022 (dplyr 1.0.8, ggplot2 3.3.5); CRAN war aus der Umgebung nicht erreichbar. Das betrifft die Werkzeugversionen und nicht die Rechnung: Zielwert, Standardfehler, φ und die Grenzen sind einfache Arithmetik und hängen an keiner Paketversion. Die Angaben zu FunnelPlotR und controlcharts beruhen auf den Paketbeschreibungen und nicht auf einem eigenen Lauf.

Quellen