Skip to content

Latest commit

 

History

History
473 lines (342 loc) · 19.6 KB

File metadata and controls

473 lines (342 loc) · 19.6 KB
title Deskriptive Statistik
description Zusammenfassende Statistiken, Lagemaße, Streuungsmaße, Quantile, Formkennzahlen, Häufigkeitsanalyse und Streaming-Berechnungen in kstats-core.

{/---IMPORT org.oremif.kstats.samples.DocsSamples--/} {/---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.

{/---FUN coreSummarySnapshot--/}

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

{/---END--/}

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

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.

{/---FUN coreCentralTendency--/}

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

{/---END--/}

  • 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.
$$ \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.

Streuungsmaße

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

{/---FUN coreDispersion--/}

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

{/---END--/}

`semiVariance` misst die Variabilität auf einer Seite eines Schwellenwerts. `DOWNSIDE` erfasst das Risiko unterhalb des Schwellenwerts; `UPSIDE` erfasst die Variabilität oberhalb davon. $$ 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 &lt; t}(x_i - t)^2 $$

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.

{/---FUN coreQuantiles--/}

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

{/---END--/}

`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. 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$.

Form und Momente

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

{/---FUN coreShape--/}

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)

{/---END--/}

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.

`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. $$ 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.

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.

{/---FUN coreFrequency--/}

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}

{/---END--/}

{/---FUN coreFrequencyTable--/}

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

{/---END--/}

`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.

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.

{/---FUN coreStreaming--/}

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

{/---END--/}

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

`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.

Prozessfähigkeit

Prozessfähigkeitsindizes quantifizieren, wie gut ein Prozess innerhalb seiner Spezifikationsgrenzen 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.

{/---FUN coreProcessCapability--/}

// 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

{/---END--/}

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.

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.

$$ \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.

Shewhart-Kontrollkarten

Shewhart-Kontrollkarten 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.

{/---FUN coreXBarRChart--/}

// 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)

{/---END--/}

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.

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}] $$

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.

{/---FUN coreCusum--/}

// 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

{/---END--/}

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. $$ 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 &gt; H$ bzw. $C^-_i &gt; H$.

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.

{/---FUN coreEwma--/}

// 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₇

{/---END--/}

$\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. $$ 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)} $$

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.

{/---FUN coreWesternElectricRules--/}

// 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

{/---END--/}

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.

Fehlerbehandlung

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.

API-Referenz

Durchsuchen Sie alle öffentlichen Typen, Funktionen, Parameterüberladungen und Rückgabetypen in der Dokka-generierten Referenz.