stdlib: JavaScript für Statistik

Echtzeit-Statistik in JavaScript mit stdlib erstellen
Keine Kommentare

Statstiken mit JavaScript erstellen: Nicht nur tensorflow.js bietet Optionen zur Verarbeitung großer Datenmengen in JavaScript. Auch stdlib bietet ganz neue Möglichkeiten für den Browser.

Heutzutage werden mehr Daten gesammelt als jemals zuvor. Nicht nur, dass Daten in einem noch nie dagewesenen Umfang erhoben werden (daher der Begriff „Big Data“), die Daten unterscheiden sich auch qualitativ von denen, die früher verfügbar waren. Neben der sich ändernden Natur der Daten (von Zahlen über Textdateien bis hin zu Bildern und Videostreams), werden diese nun auch häufig in Echtzeit gesammelt, und zwar sowohl in großem Umfang als auch mit hoher Geschwindigkeit. Statistiken und andere Data-Mining-Ansätze helfen uns, diesen Schatz an Informationen sinnvoll zu nutzen. Da sich die Datenanforderungen ständig weiterentwickeln, sollten wir erwarten, dass unsere Analyseinstrumente Schritt halten.

Jüngste Initiativen wie tensorflow.js zeigen, dass die Zeit reif dafür ist, Statistik und Machine Learning zu JavaScript und damit auf die Web-Plattform zu bringen. Was fehlt, sind die einzelnen Bausteine, um erweiterte Funktionen für Statistik und Data Science im Allgemeineren zu entwickeln. Das ist einer der Hauptgründe, warum wir stdlib, eine Open-Source-Standardbibliothek für JavaScript und Node.js mit einem Schwerpunkt auf numerischen und wissenschaftlichen Rechenmethoden, entwickeln. Zum jetzigen Zeitpunkt bietet die Bibliothek mehr als 2.000 Funktionen für Mathematik, Statistik, Datenverarbeitung, Streams und andere Bereiche und enthält viele der Werkzeuge, die Sie von einer Standardbibliothek erwarten würden.

Ganze Ökosysteme von statistischen Werkzeugen und maschinellen Lernframeworks sind für verschiedene Programmiersprachen wie Python, R und seit kurzem auch Julia entstanden. Warum also, könnte man fragen, wäre der Zugriff auf vergleichbare Funktionen in JavaScript nützlich, wenn wir bereits Zugriff auf diese anderen Umgebungen haben? Erstens hat serverseitiges JavaScript (über Node.js) die Anwendungsfälle für JavaScript erweitert, sodass sie sich stärker auf Daten zu konzentrieren (z. B. was die Verwendung in der Datenbankverwaltung und Datenverarbeitung angeht). Gerade für kleinere Teams, die nicht über die Ressourcen verfügen, um einen polyglotten Stack zu unterstützen, bedeutet die Möglichkeit, Analysen durchzuführen und Visualisierungen mit JavaScript zu erstellen, wenn alle anderen Daten bereits in JavaScript verarbeitet werden, eine schnellere und effizientere Entwicklung.

Zweitens ermöglicht ein starker wissenschaftlicher JavaScript-Stack neuartige Anwendungen, die in der Regel unter dem Oberbegriff Edge Computing zusammengefasst werden. Hier geht es darum, Berechnungen aus der Cloud an die Ränder des Netzwerks, d. h. an die Endbenutzer, zurückzubringen. Beispielsweise können wir serverseitige Berechnungen an den Webbrowser eines Benutzers auslagern. Eine solche Änderung würde nicht nur Vorteile in Form von kürzeren Latenzzeiten und niedrigeren Serverkosten mit sich bringen, sondern ermöglicht es uns auch, an Anwendungen zu denken, die sonst nicht realisierbar wären. Zum Beispiel könnten wir ein Tool entwickeln, das aus dem Verhaltens eines Benutzers auf einer Website vorhersagt, welche Webseite-Ressourcen als nächstes geladen werden sollen. Das Scrollverhalten eines Benutzers, Suchanfragen und andere UI-Interaktionen können dazu herangezogen werden, herauszufinden welche Assets als nächstes von einem Host-Server angefordert werden sollen.

Drittens, JavaScript ist überall: Telefone, Tabletts, Desktops, eingebettete Geräte und mehr. Insbesondere bei Webanwendungen können Sie, sofern ein Gerät über einen Webbrowser verfügt, JavaScript ausführen. Es ist keine komplizierte Installation und Einrichtung erforderlich, um eine Webanwendung auszuführen, die Modelle trainiert, statistische Analysen durchführt oder maschinelle Lernalgorithmen ausführt. Ein Benutzer muss nur eine URL öffnen und das Herunterladen und Ausführen einer maschinell lernfähigen Webanwendung erfolgt automatisch. Das steht in krassem Gegensatz zu traditionellen wissenschaftlichen Computerumgebungen, die oft lange und komplizierte Installations- und Setup-Prozeduren haben. Kurz gesagt, JavaScript ist ein attraktives Umfeld für wissenschaftliches Rechnen und maschinelles Lernen aufgrund seiner Allgegenwart, seiner Zugänglichkeit, seiner großen Entwicklergemeinde und der erstaunlichen neuen Arten von Anwendungen, die es aufgrund seiner Eigenschaft, die lingua franca des Web zu sein, bietet.

In diesem Artikel untersuchen wir, wie man stdlib für die Berechnung von Echtzeitstatistiken verwendet. Eine Statistik ist eine Größe, die aus einer größeren Stichprobe von Beobachtungen berechnet wird (z. B. die Summe einer Sammlung von Zahlen). In der Vergangenheit wurden eine oder mehrere Statistiken aus statischen Datensätzen berechnet. In der heutigen Welt werden Datensätze jedoch immer dynamischer, kommen in Batches oder werden in Echtzeit gestreamt. Wenn Daten in Batches oder in Echtzeit ankommen, ist die wiederholte Neuberechnung von Statistiken über einen gesamten Datensatz nicht recheneffizient. Stattdessen würde man es vorziehen, Statistiken inkrementell zu berechnen (d. h. Wert für Wert in sequentieller Reihenfolge). Im Falle von Berechnung der Summe ist die Lösung einfach: Wir halten eine laufende Summe aller bis zum aktuellen Zeitpunkt beobachteten Datenpunkte fest und addieren neue Werte zur kumulierten Summe, sobald sie ankommen. Für Statistiken höherer Ordnung mögen die Berechnungen komplexer werden, aber das Prinzip bleibt das gleiche – die Algorithmen für die Aufrechterhaltung laufender Akkumulatoren – egal, welche Statistiken berechnet werden sollen oder welches statistische Modell aktualisiert werden soll. Zu diesem Zweck enthält stdlib eine Vielzahl von Funktionen zur inkrementellen Berechnung von Statistiken.

Wenn Sie in die Webplattform investiert sind, haben Sie sich wahrscheinlich die Nutzungsmetriken für die Websites angesehen, an denen Sie gearbeitet haben. Um die Dinge zu konkretisieren, gehen wir durch die Schritte der Berechnung der Statistik für die Anzahl der Seitenaufrufe, die eine Website jede Sekunde erhält. Anstatt reale Daten zu verwenden, verwenden wir eine sogenannte Monte-Carlo-Simulation (benannt nach dem Monte-Carlo-Casino in Monaco), d. h. wir generieren hypothetische Seitenaufrufdaten mit Hilfe eines Wahrscheinlichkeitsmodells. Ein Hauptvorteil von Simulationen ist, dass wir sehen können, ob (und wie schnell) wir die wahren Parameter unseres Modells wiederherstellen.

Die Poisson-Verteilung wird häufig verwendet, um Zählungen zu modellieren, sei es der Umsatz eines Unternehmens pro Monat, die Seitenaufrufe auf einer Website pro Stunde oder die Anzahl der Geburten in einem Krankenhaus pro Tag. Um die Arbeit mit der Poisson-Verteilung zu erleichtern, enthält stdlib Funktionen zur Berechnung verschiedener Verteilungseigenschaften und einen constructor zur Erstellung von Poisson-Verteilungsobjekten. Letzteres verwenden wir, um eine Poisson-Distribution mit einer Rate von λ = 30 (also 30 Seitenaufrufe pro Sekunde) zu erstellen.

 
import Poisson from '@stdlib/stats/base/dists/poisson/ctor';

const pois = new Poisson( 30 );

Die Rate λ ist gleich der durchschnittlichen Anzahl an Seitenaufrufen in einem Intervall, wie wir schnell bestätigen können:

 
let mean = pois.mean
// returns 30

Die kumulative Verteilungsfunktion (CDF) einer Verteilung gibt die Wahrscheinlichkeit an, dass wir eine Zählung kleiner oder gleich einem gelieferten Wert beobachten würden:

 
let p = pois.cdf( 30 );
// ~0.548 

p = pois.cdf( 80 );
// returns ~0.999

Wie beim zweiten Aufruf zu beobachten ist, ist es für uns fast unmöglich (weniger als 0,1% Chance), eine Zählung von mehr als 80 in einem bestimmten Intervall zu beobachten, wenn Daten aus einer Poisson-Verteilung mit der Rate λ = 30 generiert werden. Lassen Sie uns diese Beobachtung nun empirisch überprüfen, indem wir die folgenden Statistiken inkrementell berechnen:

  • die maximal beobachtete Anzahl
  • die durchschnittlich beobachtete Anzahl

Zuerst müssen wir unsere Daten generieren. Dazu nutzen wir einen der vielen Zufallszahlgeneratoren (genauer: pseudorandom number generators, kurz PRNGs), die von stdlib bereitgestellt werden. Um eine Poisson verteilte Pseudozufallszahl jede Sekunde für eine Minute zu erzeugen, können wir stdlibs Poisson PRNG wie folgt verwenden:

 
import rpois from '@stdlib/random/base/poisson';

async function main() {
    for ( let i = 0; i < 60; i++ ) {
        await sleep( 1000 );
        console.log( 'Count: %d', rpois( 30 ) );
    }
}

main();

wobei sleep eine promisified Variante von setTimeout is, die wie folgt definiert werden kann:

 
function sleep( duration ) {
    return new Promise( ( resolve ) => {
        setTimeout( () => resolve(), duration );
    });
}

Nun, da wir die Anzahl der Seitenaufrufe simulieren können, lassen Sie uns einige Statistiken berechnen! Um inkrementell ein laufendes Maximum und einen Mittelwert zu berechnen, stellt stdlib maximum bzw. mean Akkumulatoren zur Verfügung, die wie folgt verwendet werden können:

 
import incrmax from '@stdlib/stats/incr/max';
import incrmean from '@stdlib/stats/incr/mean';

// Initialize accumulators:
const stats = {
    'max': incrmax(),
    'mean': incrmean()
};

// Provide the accumulators values:
for ( let i = 0; i < 100; i++ ) {
    stats.max( i );
    stats.mean( i );
}

// Print the current accumulator state:
console.log( 'max: %d, mean: %d', stats.max(), stats.mean() );

Und nun, da wir uns mit der Verwendung der inkrementellen Akkumulatoren von stdlib vertraut gemacht haben, kombinieren wir das vorherige Snippet mit unserer Simulation der Anzahl der Seitenaufrufe.

 
import poisson from '@stdlib/random/base/poisson';
import incrmax from '@stdlib/stats/incr/max';
import incrmean from '@stdlib/stats/incr/mean';

const stats = {
    'max': incrmax(),
    'mean': incrmean()
};

// Create a "seeded" PRNG:
const rpois = poisson.factory({
    'seed': 1303
});

async function main() {
    for ( let i = 0; i < 60; i++ ) {
        await sleep( 1000 );
        let count = rpois( 30 );
        let max = stats.max( count );  
        let mean = stats.mean( count );
        console.log( 'count: %d; max: %d, mean: %d', count, max, mean );
    }
}

main();

Sie werden feststellen, dass wir unsere Simulation modifiziert haben, indem wir ein „seeded“ PRNG erstellt haben. Die Verwendung eines seeded PRNG erlaubt es uns, eine reproduzierbare Folge von Zufallszahlen zu erzeugen, was nicht nur im wissenschaftlichen Kontext, sondern auch beim Testen und Debuggen von Funktionen wichtig ist. Die Möglichkeit, seeded PRNGs zu erstellen, ist ein Grund, warum Sie die PRNGs von stdlib (randu, mt19937, und andere) der eingebauten JavaScript-Funktion Math.random vorziehen mögen.

Das Ausführen des obigen Code-Snippets sollte die folgende Ausgabe erzeugen:

count: 26; max: 26, mean: 26
count: 28; max: 28, mean: 27
count: 31; max: 31, mean: 28.333333333333332
count: 30; max: 31, mean: 28.75
...
count: 24; max: 41, mean: 28.94642857142857
count: 25; max: 41, mean: 28.877192982456137
count: 30; max: 41, mean: 28.89655172413793
count: 25; max: 41, mean: 28.83050847457627

In Wirklichkeit ist es unwahrscheinlich, dass man auf Daten trifft, die perfekt als Poisson-Daten modelliert werden können. Eine übliche Prüfung zur Beurteilung, ob Count-Daten durch einen Poisson-Prozess modelliert werden können, besteht darin, den Mittelwert mit der Varianz zu vergleichen, wobei die Varianz ein Maß für die Streuung ist, berechnet als die durchschnittliche Summe der quadrierten Differenzen vom Mittelwert. Die Begründung für diese Prüfung ist, dass der Mittelwert eines Poisson-Prozesses gleich seiner Varianz sein sollte.

Kostenlos: Das iJS React Cheat Sheet

Sie wollen mit React durchstarten?
Unser Cheatsheet zeigt Ihnen alle wichtigen Snippets auf einen Blick.
Jetzt kostenlos herunterladen!

Download for free

 

API Summit 2018

From Bad to Good – OpenID Connect/OAuth

mit Daniel Wagner (VERBUND) und Anton Kalcik (business.software.engineering)

Um zu beurteilen, ob beobachtete Daten als Poisson-Prozess modelliert werden können, verwenden wir das Varianz-Mittelwert-Verhältnis (VMR), die, wie der Name schon sagt, als Varianz dividiert durch den Mittelwert definiert ist. Wenn der VMR kleiner als eins ist, sagen wir, dass die beobachteten Daten „underdispersed“ sind. Wenn der VMR größer als eins ist, sagen wir, dass die beobachteten Daten „overdispersed“ sind. Wenn der VMR gleich eins ist, sind Varianz und Mittelwert gleich, was auf einen Poisson-Prozess hindeutet. Sowohl in den Fall von underdispersion als auch overdispersion kann eine andere Verteilung als die Poisson-Verteilung besser passen (z. B. wird die negative binomiale Verteilung häufig zur Modellierung von overdispersed Daten verwendet).

Lassen Sie uns unseren vorherigen Code-Schnipsel ändern, um das Verhältnis zwischen Varianz und Mittelwert zu berechnen.

 
import poisson from '@stdlib/random/base/poisson';
import incrvmr from '@stdlib/stats/incr/vmr';

const stats = {
    'vmr': incrvmr()
};

// Create a "seeded" PRNG:
const rpois = poisson.factory({
    'seed': 1303
});

async function main() {
    for ( let i = 0; i < 60; i++ ) {
        await sleep( 1000 );
        let count = rpois( 30 );
        let vmr = stats.vmr( count );
        console.log( 'count: %d, vmr: %d', count, vmr );
    }
}

main();

Das Ausführen dieses Snippets sollte die folgende Ausgabe ergeben:

count: 26, vmr: 0
count: 28, vmr: ~0.074
count: 30, vmr: ~0.224
count: 31, vmr: ~0.171
...
count: 24, vmr: ~1.024
count: 25, vmr: ~1.018
count: 30, vmr: ~1.0
count: 25, vmr: ~0.994

Da unsere Daten durch eine Poisson-Verteilung generiert werden, ist zu erwarten, dass sich der VMR umso näher an Eins annähert. Dies ist der Fall, wie wir sehen können.

Was können wir mit unserem Wahrscheinlichkeitsmodell tun? Eine Möglichkeit besteht darin, ein Anomalie-Erkennungssystem zu konstruieren, so dass wir benachrichtigt werden, wenn wir einen Anstieg der Anzahl der Seitenaufrufe beobachten. Eine solche Warnung könnte uns dazu veranlassen, mehr Server bereitzustellen, um den erhöhten Datenverkehr zu bewältigen oder das anomale Nutzerverhalten zu untersuchen, um sicherzustellen, dass wir nicht Opfer eines Denial-of-Service-Angriffs werden.

Als Anomalie-Erkennungssystem für den ersten Durchgang können wir unser Modell verwenden, um ein Vorhersageintervall zu konstruieren, das als ein Intervall definiert ist, für das wir erwarten, dass X% der beobachteten Zählungen dazu gehören werden. In unserem Fall möchten wir zum Beispiel die obere Grenze für beobachtete Zählungen kennen, in der wir zu 99% sicher sein können, dass eine beobachtete Zählung erwartet wird und nicht anomal ist.

In der Praxis ist diese Aufgabe etwas aufwändiger, da die Daten zur Schätzung des Ratenparameters λ verwendet wurden. In unserem Beispiel kennen wir jedoch die wahre Rate (λ = 30) und können so leicht ein Intervall berechnen, das den nächsten Wert mit einer bestimmten Wahrscheinlichkeit enthält. Für eine gegebene Wahrscheinlichkeit p zwischen 0 und 1 gibt die Quantilfunktion einer Wahrscheinlichkeitsverteilung den Wert zurück, den eine Zufallsvariable nur in 100(1-p)% der Fälle überschreiten wird. Wenn wir zum Beispiel die obere Grenze der Zählung wissen wollten, die weniger als 1% der Zeit überschritten wird, würden wir die Quantilfunktion für p = 0.99 auswerten.

 
let v = pois.quantile( 0.99 );
// returns 43

Für λ = 30 können wir also erwarten, dass wir Seitenaufrufe, die größer als 43 sind, nur 1% der Zeit beobachten. Wenn wir einen Schwellenwert von 99% für angemessen hielten, könnten wir unser Anomalie-Erkennungssystem so konfigurieren, dass es uns benachrichtigt, wenn die Anzahl der Seitenaufrufe 43 übersteigt.

 
import poisson from '@stdlib/random/base/poisson';

// Create a "seeded" PRNG:
const rpois = poisson.factory({
    'seed': 1303
});

// Compute our anomaly detection threshold:
const threshold = pois.quantile( 0.99 );

async function main() {
    for ( let i = 0; i < 60; i++ ) { await sleep( 1000 ); let count = rpois( 30 ); if ( count > threshold ) {
            console.log( 'ALERT! Possible anomaly detected. Count: %d.', count );
        }
    }
}

main();

Während dies ein einfaches Beispiel ist, bilden probabilistische Modelle den Kern vieler moderner Vorhersagemodelle. Einige davon sind bereits in stdlib enthalten (auch für die Analyse großer Datenmengen). Wenn Sie sich dafür interessieren, wie Sie mit stdlib ein robusteres Anomalie-Erkennungssystem entwickeln können, sollten Sie einige andere Aspekte beachten:

  • In Wirklichkeit werden sich die zugrundeliegenden Kräfte, die die Seitenaufrufe bestimmen, im Laufe der Zeit ändern. Beispielsweise können die Seitenaufrufe je nach Jahreszeit oder, je nach Tageszeit, auf einer feinkörnigeren Skala variieren. Ein Ansatz, um mit dieser „Nicht-Stationarität“ umzugehen, ist die Berechnung relevanter Statistiken über ein bestimmtes Zeitintervall, wobei ältere Beobachtungen verworfen werden und nur die neuesten Beobachtungen berücksichtigt werden.
  • In den obigen Code-Snippets haben wir Akkumulatoren verwendet, die Statistiken über alle bereitgestellten Werte berechnen, aber wir könnten stattdessen Varianten, die auf einen Zeitrahmen abzielen, verwenden und die auch als Teil von stdlib verfügbar sind. Diese moving window Versionen können durch ein m vor dem Namen der jeweiligen Statistik identifiziert werden (z.B. mmax, mmean, mvmr). Dementsprechend ist eine mögliche Erweiterung unseres Anomalie-Erkennungssystems die Verwendung der Fenstervarianten, um schwankenden λ Parametern Rechnung zu tragen.
  • Um zu beurteilen, ob wir beobachtete Daten als Poisson-Prozess modellieren können, haben wir das Verhältnis von Varianz zu Mittelwert berechnet. Ein statistisch strengerer Ansatz wäre die Verwendung eines chi-square goodness-of-fit Tests. Der chi-square goodness-of-fit Test ist für diskrete Daten und Verteilungen geeignet. Für kontinuierliche Daten können wir stattdessen den Kolmogorov-Smirnov Test
  • Eine interessante Erweiterung unseres Anomalie-Erkennungssystems wäre die Verwendung eines chi-square goodness-of-fit Tests zur Überwachung eingehender Daten auf Änderungen im generativen Modell. Wenn ein chi-square goodness-of-fit Test konsequent scheitert, sollten wir die Angemessenheit der oben diskutierten Schwellenwertmethode neu bewerten und uns vielleicht für ein anderes Modell entscheiden.

Zusammengefasst haben wir in diesem Artikel untersucht, wie wir mit stdlib Monte-Carlo-Simulationen durchführen können, um synthetische Daten zu generieren, Statistiken über Echtzeitdaten inkrementell zu berechnen, die Modellanpassung zu bewerten und verschiedene Eigenschaften von Wahrscheinlichkeitsverteilungen zu berechnen. Aus unserem gesammelten Wissen haben wir gezeigt, wie wir Seitenaufrufe auf einer Website überwachen und einen einfachen Anomalie-Erkennungsalgorithmus erstellen können. Hoffentlich haben Sie ein oder zwei Dinge darüber gelernt, wie Sie JavaScript und stdlib zur Datenanalyse in Echtzeit nutzen können und sind genauso begeistert wie wir für die Zukunft von JavaScript zur Datenanalyse und zum maschinellen Lernen.

Happy coding!

Wenn Sie daran interessiert sind, die obigen Code-Snippets selbst auszuführen, sehen Sie sich das folgende GitHub-Repository an: https://github.com/Planeshifter/stdlib-realtime-statistics-code

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -