> ## Documentation Index
> Fetch the complete documentation index at: https://kstats.oremif.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Deskriptive Statistik

> Zusammenfassende Statistiken, Lagemaße, Streuungsmaße, Quantile, Formkennzahlen, Häufigkeitsanalyse und Streaming-Berechnungen in kstats-core.

{/*---IMPORT org.oremif.kstats.sampling.samples.DocsSamples--*/}

`kstats-core` stellt deskriptive Statistiken als Erweiterungsfunktionen auf `DoubleArray` und `Iterable<Double>` bereit. Zusätzlich sind `Int`- und `Long`-Überladungen für die gängigsten Operationen verfügbar.

## Zusammenfassung auf einen Blick

`describe()` ist der schnellste Weg, ein vollständiges Bild einer Stichprobe zu erhalten. Die Methode gibt ein `DescriptiveStatistics`-Objekt zurück, das Anzahl, Lagemaße, Streuung, Quartile, Formkennzahlen und Standardfehler kombiniert.

```kotlin theme={"system"}
val data = doubleArrayOf(2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0)
val stats = data.describe()

stats.count              // 8
stats.mean               // 5.0
stats.median             // 4.5
stats.standardDeviation  // 2.1380
stats.variance           // 4.5714
stats.q1                 // 4.0
stats.q3                 // 5.5
stats.interquartileRange // 1.5
stats.skewness           // 0.6563
stats.kurtosis           // -0.1640
stats.range              // 7.0
stats.standardError      // 0.7559
```

<Note>
  `variance()` und `standardDeviation()` verwenden standardmäßig die **Stichprobenformel** (Division durch $n - 1$). Übergeben Sie `PopulationKind.POPULATION`, um die Populationsformel (Division durch $n$) zu verwenden.
</Note>

## Lagemaße

Verschiedene Begriffe von „Mitte" passen zu verschiedenen Datenformen. `mean()` ist das arithmetische Mittel, `median()` ist der Positionsmittelwert, `mode()` gibt die häufigsten Werte zurück, und die getrimmten sowie gewichteten Varianten behandeln Ausreißer und Gewichtungen.

```kotlin theme={"system"}
val data = doubleArrayOf(2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0)

data.mean()                    // 5.0
data.median()                  // 4.5
data.toList().mode()           // {4.0}

data.trimmedMean(0.1)          // 4.8333 — trims 10% from each tail

val positive = doubleArrayOf(1.0, 2.0, 4.0, 8.0)
positive.geometricMean()       // 2.8284
positive.harmonicMean()        // 2.1333

val values = doubleArrayOf(1.0, 2.0, 3.0)
val weights = doubleArrayOf(3.0, 1.0, 1.0)
values.weightedMean(weights)   // 1.6
```

* **Geometrisches Mittel** — geeignet für Wachstumsraten und Verhältnisse. Erfordert ausschließlich positive Werte.
* **Harmonisches Mittel** — geeignet für Raten und Geschwindigkeitsdurchschnitte. Erfordert ausschließlich positive Werte.
* **Getrimmtes Mittel** — entfernt einen Anteil von jedem Ende, bevor der Durchschnitt berechnet wird. Robust gegenüber Ausreißern.
* **Gewichtetes Mittel** — jede Beobachtung trägt proportional zu ihrem Gewicht bei.

<Accordion title="Mathematische Details">
  $$
  \bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i
  $$

  $$
  \bar{x}_{\text{geo}} = \left(\prod_{i=1}^{n} x_i\right)^{1/n}
  $$

  $$
  \bar{x}_{\text{harm}} = \frac{n}{\sum_{i=1}^{n} \frac{1}{x_i}}
  $$

  $$
  \bar{x}_w = \frac{\sum_{i=1}^{n} w_i x_i}{\sum_{i=1}^{n} w_i}
  $$

  Das getrimmte Mittel entfernt $\lfloor p \cdot n \rfloor$ Beobachtungen von jedem Ende der sortierten Stichprobe und berechnet anschließend das arithmetische Mittel der verbleibenden Werte.
</Accordion>

## Streuungsmaße

Streuungsmaße beschreiben, wie weit die Werte vom Zentrum abweichen.

```kotlin theme={"system"}
val data = doubleArrayOf(2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0)

data.variance()                                    // 4.5714 (sample)
data.variance(PopulationKind.POPULATION)            // 4.0
data.standardDeviation()                            // 2.1380
data.range()                                        // 7.0
data.interquartileRange()                           // 1.5
data.meanAbsoluteDeviation()                        // 1.5

data.trimmedVariance(0.1)                           // trimmed sample variance

data.semiVariance(5.0, SemiVarianceDirection.DOWNSIDE) // 1.7143
data.semiVariance(5.0, SemiVarianceDirection.UPSIDE)   // 2.8571
```

<Note>
  `semiVariance` misst die Variabilität auf einer Seite eines Schwellenwerts. `DOWNSIDE` erfasst das Risiko unterhalb des Schwellenwerts; `UPSIDE` erfasst die Variabilität oberhalb davon.
</Note>

<Accordion title="Mathematische Details">
  $$
  s^2 = \frac{1}{n-1}\sum_{i=1}^{n}(x_i - \bar{x})^2, \qquad
  s = \sqrt{s^2}, \qquad
  SE = \frac{s}{\sqrt{n}}
  $$

  $$
  \text{MAD} = \frac{1}{n}\sum_{i=1}^{n}|x_i - \bar{x}|
  $$

  $$
  \text{SemiVar}_{\text{down}}(t) = \frac{1}{n-1}\sum_{x_i < t}(x_i - t)^2
  $$
</Accordion>

## Quantile und Position

Quantile teilen die Daten an bestimmten Wahrscheinlichkeitsschwellen. `percentile()` erwartet einen Wert auf der Skala 0–100; `quantile()` erwartet einen Wert auf der Skala 0–1.

```kotlin theme={"system"}
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)

data.quantile(0.5)         // 5.5 (median)
data.quantile(0.25)        // 3.25 (Q1)
data.percentile(90.0)      // 9.1 (90th percentile)

val (q1, median, q3) = data.quartiles()
// q1 = 3.25, median = 5.5, q3 = 7.75
```

<Tip>
  `quantile()` unterstützt einen `QuantileInterpolation`-Parameter mit den Optionen: `LINEAR` (Standard), `LOWER`, `HIGHER`, `NEAREST` und `MIDPOINT`. Diese steuern, wie der Wert interpoliert wird, wenn das Quantil zwischen zwei Datenpunkten liegt.
</Tip>

<Accordion title="Mathematische Details">
  Bei linearer Interpolation (Standardverfahren) wird das Quantil an der Wahrscheinlichkeit $p$ für sortierte Daten $x_{(1)}, \ldots, x_{(n)}$ wie folgt berechnet:

  $$
  Q(p) = x_{(\lfloor h \rfloor)} + (h - \lfloor h \rfloor)(x_{(\lceil h \rceil)} - x_{(\lfloor h \rfloor)})
  $$

  wobei $h = p(n - 1) + 1$.
</Accordion>

## Form und Momente

Formkennzahlen beschreiben Asymmetrie und Randschwere über Mittelwert und Varianz hinaus.

```kotlin theme={"system"}
val data = doubleArrayOf(2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0)

data.skewness()            // 0.6563 — positive: right tail is longer
data.kurtosis()            // -0.1640 — negative excess: lighter tails than normal
data.kurtosis(excess = false) // raw kurtosis (not centered at 0)

data.centralMoment(2)      // 4.0 (equals population variance)
data.centralMoment(3)      // 5.25
data.centralMoment(4)      // 44.5

data.kStatistic(1)          // 5.0 (equals mean)
data.kStatistic(2)          // 4.5714 (equals sample variance)
```

Eine Schiefe nahe null deutet auf Symmetrie hin. Positive Schiefe weist auf einen längeren rechten Rand hin; negative auf einen längeren linken Rand. Exzess-Kurtosis nahe null ähnelt einer Normalverteilung; positive Werte deuten auf schwerere Ränder hin.

<Warning>
  `kurtosis()` gibt standardmäßig die **Exzess-Kurtosis** zurück (`excess = true`), d.h. der Wert ist so verschoben, dass eine Normalverteilung eine Exzess-Kurtosis von 0 hat. Übergeben Sie `excess = false` für das rohe vierte standardisierte Moment.
</Warning>

<Accordion title="Mathematische Details">
  $$
  g_1 = \frac{n}{(n-1)(n-2)} \sum_{i=1}^{n} \left(\frac{x_i - \bar{x}}{s}\right)^3
  $$

  $$
  g_2 = \frac{n(n+1)}{(n-1)(n-2)(n-3)} \sum_{i=1}^{n} \left(\frac{x_i - \bar{x}}{s}\right)^4 - \frac{3(n-1)^2}{(n-2)(n-3)}
  $$

  Das $r$-te zentrale Moment ist $\mu_r = \frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^r$. K-Statistiken ($k_r$) sind erwartungstreue Schätzer der entsprechenden Kumulanten.
</Accordion>

## Häufigkeitsanalyse

Die Klasse `Frequency` zählt Vorkommen, Anteile und kumulative Häufigkeiten für beliebige `Comparable`-Typen. `frequencyTable()` gruppiert numerische Daten in gleich breite Klassen.

```kotlin theme={"system"}
val freq = listOf("a", "a", "b", "b", "b", "c").toFrequency()
freq.totalCount            // 6
freq.count("b")            // 3
freq.proportion("b")       // 0.5
freq.cumulativeCount("b")  // 5 (a=2 + b=3)
freq.mode                  // {b}
```

{/*---FUN coreFrequencyTable--*/}

```kotlin theme={"system"}
val data = doubleArrayOf(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0)
val bins = data.asIterable().frequencyTable(3)
// Each FrequencyBin has: range, count, relativeFrequency, cumulativeFrequency
bins[0].count              // number of values in the first bin
bins[0].relativeFrequency  // proportion of total
```

<Tip>
  `Frequency` funktioniert mit jedem `Comparable`-Typ — Strings, Enums, Ganzzahlen oder eigene Klassen. Für numerische Klasseneinteilung verwenden Sie `frequencyTable()` mit einer Klassenanzahl oder einer Klassenbreite.
</Tip>

## Streaming-Statistiken

`OnlineStatistics` berechnet Mittelwert, Varianz, Schiefe und Kurtosis inkrementell, ohne die gesamte Stichprobe zu speichern. Werte können einzeln oder als Stapel hinzugefügt werden.

```kotlin theme={"system"}
val online = OnlineStatistics()
online.addAll(doubleArrayOf(2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0))

online.count               // 8
online.mean                // 5.0
online.sum                 // 40.0
online.min                 // 2.0
online.max                 // 9.0
online.variance()          // 4.5714 (sample)
online.standardDeviation() // 2.1380
online.skewness()          // 0.6563

// Add more data later
online.add(3.0)
online.count               // 9
online.mean                // 4.7778
```

Verwenden Sie `OnlineStatistics`, wenn Daten inkrementell eintreffen (Streaming, ereignisgesteuerte Systeme) oder wenn das Speichern der gesamten Stichprobe im Arbeitsspeicher nicht praktikabel ist.

<Accordion title="Mathematische Details">
  `OnlineStatistics` verwendet Welfords Algorithmus mit der Terriberry-Erweiterung für numerisch stabile Einpass-Berechnung höherer Momente. Der Algorithmus aktualisiert laufende Summen von Potenzen der Abweichungen vom aktuellen Mittelwert und vermeidet so die katastrophale Auslöschung, die naive Zweipass-Formeln bei großen Datensätzen betrifft.
</Accordion>

## Prozessfähigkeit

Prozessfähigkeitsindizes quantifizieren, wie gut ein Prozess innerhalb seiner <Tooltip tip="Die untere (LSL) und obere (USL) Spezifikationsgrenze begrenzen den Bereich akzeptabler Werte für das gemessene Merkmal">Spezifikationsgrenzen</Tooltip> liegt. `processCapability(lsl, usl)` liefert vier SPC-Indizes in einem Aufruf:

* **Cp, Cpk** — potentielle und tatsächliche Fähigkeit mit der Stichproben-Standardabweichung (kurzfristige, innerhalb von Untergruppen auftretende Streuung).
* **Pp, Ppk** — dieselben Formeln mit der Populations-Standardabweichung (langfristige Gesamtstreuung).

Die `-k`-Varianten bestrafen einen Prozess, der relativ zur Spezifikationsmitte dezentriert ist, sodass `Cpk` nie größer als `Cp` ist.

```kotlin theme={"system"}
// Ten parts measured against a spec window of [48, 52]
val measurements = doubleArrayOf(
    50.0, 50.5, 49.5, 50.2, 49.8, 50.1, 49.9, 50.3, 49.7, 50.0
)
val capability = measurements.processCapability(lsl = 48.0, usl = 52.0)

capability.cp   // 2.2646 — potential capability (spread vs tolerance)
capability.cpk  // 2.2646 — actual capability (penalizes off-centering)
capability.pp   // 2.3870 — overall (population σ) counterpart of Cp
capability.ppk  // 2.3870 — overall counterpart of Cpk
```

<Note>
  Verwenden Sie diese Indizes nur für einen bereits statistisch beherrschten Prozess (stabil über die Zeit — siehe [Shewhart-Kontrollkarten](#shewhart-kontrollkarten) unten). Für einen instabilen Prozess ist die gemessene Streuung keine feste Prozesseigenschaft.
</Note>

Werte `≥ 1.33` gelten üblicherweise als fähig, `≥ 1.67` als hochgradig fähig. Wenn `Cpk ≪ Cp`, sollten Sie den Prozess zuerst neu zentrieren, bevor Sie versuchen, die Varianz zu reduzieren.

<Accordion title="Mathematische Details">
  $$
  \mathrm{Cp} = \frac{\mathrm{USL} - \mathrm{LSL}}{6\sigma_s}, \qquad
  \mathrm{Cpk} = \min\!\left(\frac{\mathrm{USL} - \bar{x}}{3\sigma_s},\ \frac{\bar{x} - \mathrm{LSL}}{3\sigma_s}\right)
  $$

  Pp und Ppk verwenden die Populations-Standardabweichung $\sigma_p$ (Divisor $n$) anstelle der Stichproben-Standardabweichung $\sigma_s$ (Divisor $n-1$). `processCapability` berechnet beide in einem einzigen numerisch stabilen Welford-Durchlauf.
</Accordion>

## Shewhart-Kontrollkarten

Shewhart-<Tooltip tip="Zeitreihen-Plot von Untergruppenstatistiken mit ±3σ-Kontrollgrenzen um eine Mittellinie">Kontrollkarten</Tooltip> zeichnen Untergruppenstatistiken über die Zeit mit Drei-Sigma-Grenzen auf. `xBarRChart()` überwacht den Prozessmittelwert zusammen mit der Spannweite innerhalb jeder Untergruppe; `xBarSChart()` nutzt stattdessen die Stichproben-Standardabweichung — effizienter für Untergruppengrößen über 10. Beide benötigen gleich große Untergruppen mit 2–25 Beobachtungen.

```kotlin theme={"system"}
// Five subgroups of four parts; bracket width monitored per batch
val subgroups = listOf(
    doubleArrayOf(72.0, 84.0, 79.0, 49.0),
    doubleArrayOf(56.0, 87.0, 33.0, 42.0),
    doubleArrayOf(55.0, 73.0, 22.0, 60.0),
    doubleArrayOf(44.0, 80.0, 54.0, 74.0),
    doubleArrayOf(97.0, 26.0, 48.0, 58.0),
)
val chart = xBarRChart(subgroups)

chart.centerLine     // 59.65 — grand mean (x-double-bar)
chart.ucl            // 95.6626 — upper control limit for the mean
chart.lcl            // 23.6374 — lower control limit for the mean
chart.rChart.centerLine // 49.4 — average range (R-bar)
chart.rChart.ucl     // 112.7308 — upper limit for within-subgroup range
chart.rChart.lcl     // 0.0 — lower limit (D₃ = 0 for n ≤ 6)
```

Die Kontrollgrenzen basieren auf den Standard-SPC-Konstanten $A_2, A_3, D_3, D_4, B_3, B_4, c_4$ (Montgomery, *Introduction to Statistical Quality Control*, Anhang VI), tabelliert für Untergruppengrößen 2–25 und direkt über `spcConstants(n)` verfügbar.

<Accordion title="Mathematische Details">
  Für $k$ Untergruppen der Größe $n$ mit Untergruppenmittelwerten $\bar{x}_i$, Spannweiten $R_i$ und Standardabweichungen $s_i$:

  $$
  \text{x̄-R:}\quad \mathrm{UCL}/\mathrm{LCL} = \bar{\bar{x}} \pm A_2 \bar{R}, \quad R\text{-Karte: } [D_3 \bar{R},\ D_4 \bar{R}]
  $$

  $$
  \text{x̄-S:}\quad \mathrm{UCL}/\mathrm{LCL} = \bar{\bar{x}} \pm A_3 \bar{s}, \quad S\text{-Karte: } [B_3 \bar{s},\ B_4 \bar{s}]
  $$
</Accordion>

## CUSUM-Karte

Eine Shewhart-Karte reagiert langsam auf Drifts unter 2σ, weil jeder Punkt isoliert bewertet wird. `cusum()` akkumuliert Abweichungen vom Zielwert über die Zeit, sodass eine Drift von 0.5σ–1σ innerhalb weniger Beobachtungen erkannt wird. Die zweiseitige tabellarische Form verfolgt eine obere Summe $C^+$ für Aufwärtsverschiebungen und eine untere Summe $C^-$ für Abwärtsverschiebungen. Ein Alarm wird beim ersten Index ausgelöst, an dem eine der Summen das Entscheidungsintervall $H$ überschreitet.

```kotlin theme={"system"}
// Individual measurements from a process with target 10, drifting upward
val observations = doubleArrayOf(10.2, 10.4, 10.6, 10.9, 11.2, 11.5, 11.8, 12.0)
val result = cusum(observations, target = 10.0, k = 0.5, h = 3.0)

result.sPlus      // [0.0, 0.0, 0.1, 0.5, 1.2, 2.2, 3.5, 5.0]
result.sMinus     // all zero — no downward drift
result.alarmIndex // 6 — first index where C⁺ > H
```

<Tip>
  Stellen Sie `k` auf die Hälfte der zu erkennenden Verschiebungsgröße in Einheiten von σ ein — gebräuchlicher Standard ist $K \approx 0.5\sigma$, was auf eine 1σ-Drift abzielt. Setzen Sie `h` auf 4σ–5σ, um die mittlere Lauflänge in-Kontrolle einer 3σ-Shewhart-Karte zu erreichen, bei deutlich schnellerer Reaktion auf kleine Verschiebungen.
</Tip>

<Accordion title="Mathematische Details">
  $$
  C^+_i = \max\bigl(0,\; C^+_{i-1} + (x_i - \mu_0 - K)\bigr), \qquad
  C^-_i = \max\bigl(0,\; C^-_{i-1} + (\mu_0 - K - x_i)\bigr)
  $$

  startend mit $C^\pm_0 = 0$, Alarm beim ersten $C^+_i > H$ bzw. $C^-_i > H$.
</Accordion>

## EWMA-Karte

EWMA (Roberts, 1959) ist das zweite klassische Werkzeug zur Erkennung kleiner Verschiebungen. Statt einer unbeschränkten laufenden Summe führt `ewma()` einen gewichteten gleitenden Mittelwert, der aktuellen Beobachtungen mehr Gewicht gibt, aber ein Gedächtnis der Vergangenheit behält. Die Kontrollgrenzen weiten sich mit der Zeit, bis sie einen stationären Wert erreichen — die Karte ist früh am empfindlichsten, was eine anfängliche Verschiebung zuverlässig erfasst.

```kotlin theme={"system"}
// EWMA chart: target = 25, σ = 1, λ = 0.2, L = 3
val observations = doubleArrayOf(25.0, 24.5, 25.2, 26.1, 25.8, 27.0, 26.5, 28.0)
val result = ewma(
    observations,
    target = 25.0,
    sigma = 1.0,
    lambda = 0.2,
    controlLimitWidth = 3.0
)

result.smoothedValues[0] // 25.0 — Z₀ = λ·x + (1-λ)·target
result.smoothedValues[7] // 26.2549 — smoothed statistic at t = 7
result.ucl[0]            // 25.6 — narrow at first, widens with t
result.ucl[7]            // 25.9858 — approaching steady state
result.outOfControl      // [7] — Z₇ exceeds UCL₇
```

<Tip>
  $\lambda = 0.2$ mit $L \approx 2.7$–$3.0$ ist ein gebräuchlicher Standard. Kleineres $\lambda$ betont das Gedächtnis und erkennt kleinere Verschiebungen; $\lambda = 1$ reduziert EWMA auf eine Shewhart-Einzelwertkarte.
</Tip>

<Accordion title="Mathematische Details">
  $$
  Z_t = \lambda x_t + (1 - \lambda) Z_{t-1}, \qquad Z_0 = \mu_0
  $$

  $$
  \mathrm{UCL}_t/\mathrm{LCL}_t = \mu_0 \pm L \sigma \sqrt{\frac{\lambda}{2 - \lambda}\,\bigl(1 - (1 - \lambda)^{2t}\bigr)}
  $$
</Accordion>

## Western-Electric-Regeln

`westernElectricRules()` erweitert eine Shewhart-Karte über den einfachen ±3σ-Check hinaus mit vier Lauflängen-Heuristiken, die Trends, Cluster und anhaltende einseitige Läufe erkennen.

| Regel | Muster                                                           | Erkennt                                  |
| ----- | ---------------------------------------------------------------- | ---------------------------------------- |
| **1** | 1 Punkt jenseits $\pm 3\sigma$                                   | extreme Einzelausschläge                 |
| **2** | 2 der letzten 3 Punkte jenseits $\pm 2\sigma$, gleiche Seite     | starke Verschiebung                      |
| **3** | 4 der letzten 5 Punkte jenseits $\pm 1\sigma$, gleiche Seite     | mittlere Verschiebung                    |
| **4** | 8 aufeinanderfolgende Punkte auf derselben Seite der Mittellinie | anhaltende Verschiebung beliebiger Größe |

Das Array jeder Regel enthält die *Auslöseindizes* — jene Beobachtung, deren Eintreffen das betreffende Muster vervollständigt.

```kotlin theme={"system"}
// Process drifting upward in the last four observations
val observations = doubleArrayOf(
    0.1, 0.2, -0.3, 0.0, 1.4, 1.2, 2.4, 2.6, 3.5, 2.2
)
val violations = westernElectricRules(observations, center = 0.0, sigma = 1.0)

violations.rule1 // indices of points beyond ±3σ
violations.rule2 // indices where 2 of last 3 points are beyond ±2σ (same side)
violations.rule3 // indices where 4 of last 5 points are beyond ±1σ (same side)
violations.rule4 // indices where 8 consecutive points fall on the same side
```

<Tip>
  Kombinieren Sie eine Shewhart-Karte (große Verschiebungen) mit CUSUM oder EWMA (kleine Verschiebungen) und den Western-Electric-Regeln (Muster) — die drei Sichtweisen zusammen decken die breiteste Palette außer-Kontrolle-Zustände ab.
</Tip>

## Fehlerbehandlung

<Warning>
  Leere Arrays lösen eine `InsufficientDataException` aus. Funktionen, die mindestens zwei Beobachtungen erfordern (Varianz, Standardabweichung, Schiefe), lösen bei Eingaben mit nur einem Element eine `InsufficientDataException` aus. Funktionen, die positive Daten erfordern (`geometricMean`, `harmonicMean`), lösen bei nicht-positiven Werten eine `InvalidParameterException` aus.
</Warning>

## API-Referenz

<Card title="Vollständige API-Referenz" icon="book-open" href="https://oremif.github.io/kstats">
  Durchsuchen Sie alle öffentlichen Typen, Funktionen, Parameterüberladungen und Rückgabetypen in der Dokka-generierten Referenz.
</Card>
