std.stats — Statistik über Zahlenfelder

Zurück zur Unit-Übersicht

Auswertung von int64-Feldern: Summe, Minimum, Maximum, Mittelwert, Median, Varianz, Standardabweichung, Suchen und Zählen. Dazu ein paar Rechenhilfen und Aggregatfunktionen für Map-Werte.

Alle Feldfunktionen arbeiten auf einem rohen Speicherblock mit 8 Byte Schrittweite; n zählt Elemente, nicht Bytes.

Autor: Andreas Röne
Copyright: 2024–2025 Andreas Röne
Quelle: std/stats.lyx

 
Zwei früher hier verzeichnete Defekte sind behoben. Nachgemessen mit 1.0.21A:

* Die Map*-Aggregate rechnen wieder — für {1: 10, 2: 20, 3: 30} liefern MapCount/MapSum/MapMax die Werte 3, 60 und 30 (#1491).
* std.stats und std.math lassen sich gemeinsam importieren; die frühere Mehrdeutigkeit bei Clamp64 und InRange64 besteht nicht mehr (#1492).

Import

import std.stats;

Nicht zusammen mit std.math importierbar (#1492). Zieht std.sort mit — ArrayMedian und ArraySort bauen darauf auf.


Funktionen

Kennzahlen

Signatur Beschreibung
ArraySum(arr, n: int64): int64 Summe aller Werte
ArrayCount(arr, n: int64): int64 Gibt n zurück — der Vollständigkeit halber
ArrayMin(arr, n: int64): int64 Kleinster Wert; leeres Feld ergibt 0
ArrayMax(arr, n: int64): int64 Größter Wert; leeres Feld ergibt 0
ArrayAvg(arr, n: int64): int64 Mittelwert, ganzzahlig abgerundet
ArrayRange(arr, n: int64): int64 Spannweite: Maximum minus Minimum
ArrayProduct(arr, n: int64): int64 Produkt aller Werte; leeres Feld ergibt 0
ArraySumSquares(arr, n: int64): int64 Summe der Quadrate
ArrayVariance(arr, n: int64): int64 Varianz, durch n geteilt — Rückgabe ist int64
ArrayStdDev(arr, n: int64): int64 Standardabweichung, ganzzahlig abgerundet

<WRAP alert> Varianz und Standardabweichung sind hier ganzzahlig — das ist selten die gewünschte Antwort. Eine Varianz ist fast nie eine ganze Zahl; ArrayStdDev rundet ab und liefert für die Werte 2…6 den Wert 1 statt 1,58. Bei kleinen Streuungen wird daraus 0.

Für Statistik in f64 gibt es std.prob — mit dem Unterschied zwischen Grundgesamtheit (ProbVarianceP) und Stichprobe (ProbVarianceS), den diese Unit gar nicht führt. std.stats bleibt richtig, wenn tatsächlich über ganze Zahlen gezählt und aggregiert wird. </WRAP>

ArrayMedian(arr, n: int64): int64 Mittlerer Wert — sortiert das übergebene Feld

Ein leeres Feld (n == 0) liefert bei allen Funktionen 0, ohne zu lesen. Beim Produkt ist das eine bewusste Abweichung von der Mathematik, wo das leere Produkt 1 wäre.

Die Varianz teilt durch n, nicht durch n − 1 — sie beschreibt also die vorliegenden Werte selbst und ist keine Schätzung für eine größere Grundgesamtheit.

Suchen und Zählen

Signatur Beschreibung
ArrayFirst(arr, n, fallback: int64): int64 Erster Wert, sonst fallback
ArrayLast(arr, n, fallback: int64): int64 Letzter Wert, sonst fallback
ArrayIndexOf(arr, n, value: int64): int64 Erste Fundstelle, -1 wenn nicht enthalten
ArrayLastIndexOf(arr, n, value: int64): int64 Letzte Fundstelle
ArrayContains(arr, n, value: int64): bool Ob der Wert vorkommt
ArrayCountValue(arr, n, value: int64): int64 Wie oft der Wert vorkommt

Sortieren

Signatur Beschreibung
ArraySort(arr, n: int64): void Sortiert aufsteigend — reicht an SortInt64 aus std.sort weiter

Rechenhilfen

Signatur Beschreibung
Clamp64(value, minVal, maxVal: int64): int64 Wert auf einen Bereich begrenzen
InRange64(value, minVal, maxVal: int64): bool Ob der Wert im Bereich liegt, Grenzen eingeschlossen
Percentage64(value, total: int64): int64 Prozentanteil, ganzzahlig; total == 0 ergibt 0
AbsDiff64(a, b: int64): int64 Betrag der Differenz

Clamp64 und InRange64 gibt es namensgleich auch in std.math (#1492).

Map-Aggregate

Signatur Beschreibung Status
MapCount(m: int64): int64 Zahl der Einträge liefert 0 (#1491)
MapSum(m: int64): int64 Summe der Werte liefert 0 (#1491)
MapMin(m: int64): int64 · MapMax · MapAvg Kleinster/größter/mittlerer Wert liefert 0 (#1491)

Übergeben wird die Map als m as int64.


Beispiel

import std.stats;
import std.alloc;

fn main(): int64 {
    // Messreihe
    var a: int64 := alloc(6 * 8);
    poke64(a, 4); poke64(a+8, 8); poke64(a+16, 15); poke64(a+24, 16); poke64(a+32, 23); poke64(a+40, 42);

    PrintLn("Werte: 4 8 15 16 23 42");
    PrintLn("Summe        = ", IntToStr(ArraySum(a, 6)));
    PrintLn("Minimum      = ", IntToStr(ArrayMin(a, 6)));
    PrintLn("Maximum      = ", IntToStr(ArrayMax(a, 6)));
    PrintLn("Mittelwert   = ", IntToStr(ArrayAvg(a, 6)));
    PrintLn("Spannweite   = ", IntToStr(ArrayRange(a, 6)));
    PrintLn("Varianz      = ", IntToStr(ArrayVariance(a, 6)));
    PrintLn("Standardabw. = ", IntToStr(ArrayStdDev(a, 6)));

    PrintLn("-- Suchen:");
    PrintLn("IndexOf(15)   = ", IntToStr(ArrayIndexOf(a, 6, 15)));
    PrintLn("Contains(99)  = ", IntToStr(ArrayContains(a, 6, 99) as int64));
    PrintLn("CountValue(8) = ", IntToStr(ArrayCountValue(a, 6, 8)));

    PrintLn("-- Median sortiert die Eingabe:");
    var b: int64 := alloc(5 * 8);
    poke64(b, 50); poke64(b+8, 10); poke64(b+16, 40); poke64(b+24, 20); poke64(b+32, 30);
    Print("vorher:  ");
    var i: int64 := 0;
    while (i < 5) { Print(IntToStr(peek64(b + i*8)), " "); i := i + 1; }
    PrintLn("");
    PrintLn("Median = ", IntToStr(ArrayMedian(b, 5)));
    Print("nachher: ");
    i := 0;
    while (i < 5) { Print(IntToStr(peek64(b + i*8)), " "); i := i + 1; }
    PrintLn("");
    return 0;
}

Ausgabe:

Werte: 4 8 15 16 23 42
Summe        = 108
Minimum      = 4
Maximum      = 42
Mittelwert   = 18
Spannweite   = 38
Varianz      = 151
Standardabw. = 12
-- Suchen:
IndexOf(15)   = 2
Contains(99)  = 0
CountValue(8) = 1
-- Median sortiert die Eingabe:
vorher:  50 10 40 20 30
Median = 30
nachher: 10 20 30 40 50


Fallstricke

ArrayMedian verändert das Feld

Um den mittleren Wert zu finden, sortiert die Funktion die Eingabe — dauerhaft, wie die Ausgabe oben zeigt. Wer die ursprüngliche Reihenfolge braucht, arbeitet auf einer Kopie:

var kopie: int64 := alloc(n * 8);
var i: int64 := 0;
while (i < n) { poke64(kopie + i*8, peek64(arr + i*8)); i := i + 1; }
var med: int64 := ArrayMedian(kopie, n);
free(kopie, n * 8);

Dasselbe gilt für ArraySort, dort ist es allerdings der erklärte Zweck.

Median bei gerader Anzahl

Bei einer geraden Zahl von Werten gibt die Funktion das obere der beiden mittleren Elemente zurück, nicht deren Mittelwert:

Median(10, 20, 30, 40) = 30        statistisch üblich wären 25

Wer den üblichen Wert braucht, bildet ihn selbst aus den Positionen n/2 − 1 und n/2 des sortierten Feldes.

Ganzzahlige Ergebnisse

Mittelwert, Varianz, Standardabweichung und Prozentangaben werden abgerundet: aus 12,3 wird 12, aus 12,5 % werden 12 %. Bei kleinen Werten fällt das ins Gewicht — ein Mittelwert von 0 kann sowohl „alle Werte sind 0„ als auch „alle Werte liegen unter 1“ bedeuten. Wo es genauer sein muss, vorher mit 100 oder 1000 skalieren und am Ende zurückrechnen.

Überlauf bei Produkt und Quadratsummen

ArrayProduct multipliziert ungeprüft — sechs dreistellige Werte genügen bereits für einen Überlauf. Auch ArraySumSquares und die Varianz quadrieren, ohne den Bereich zu prüfen. Bei großen Messwerten vorher überschlagen oder die Werte herunterskalieren.


Verwandte Units

  • std.sortSortInt64, Grundlage von ArraySort und ArrayMedian
  • std.mathIntSqrt, Min64/Max64; nicht zusammen importierbar (#1492)
  • std.list — dynamische Zahlenlisten mit eigener Sortierung