MongoDB

Einen gleitenden Durchschnitt mit MongoDB berechnen

Bei der Arbeit mit einer Vielzahl an Messwerten kommt man oft in die Situation, dass man in der großen Datenmenge die wesentlichen Entwicklungen nicht mehr erkennen kann - man sieht praktisch den Wald vor lauter Bäumen nicht mehr. Hierbei hilft es, wenn man sich nicht zu sehr auf die Einzelwerte fokussiert, sondern stattdessen den gleitenden Durchschnitt (moving average) über einen Zeitraum betrachtet. Dies glättet die Kurve, so dass sprunghafte Änderungen weniger relevant werden und der allgemeine Trend in den Vordergrund rückt:

Einzelwerte vs. gleitender Durchschnitt über 3 Tage

Beispieldaten

In unserem Beispiel stützen wir uns auf einfache Daten, die folgende Struktur haben:

{
  "_id": ObjectId("63ca9e13f0a91511db9cc25c"),
  "ts": ISODate("2022-12-01T00:00:00Z"),
  "value": 113
}

Wesentlich ist in diesem Fall vor allem der Timestamp und der Wert zu dieser Zeit. Insgesamt haben wir folgende Daten in unserer Collection (der gleitende Durchschnitt ist das berechnete Ergebnis):

Datum Wert Gleitender Durchschnitt
01.12.2022 113,00 113,00
02.12.2022 129,00 121,00
03.12.2022 102,00 114,67
04.12.2022 191,00 140,67
05.12.2022 111,00 134,67
06.12.2022 155,00 152,33
07.12.2022 196,00 154,00
08.12.2022 115,00 155,33
09.12.2022 155,00 155,33
10.12.2022 149,00 139,67
11.12.2022 164,00 156,00
12.12.2022 165,00 159,33
13.12.2022 163,00 164,00
14.12.2022 144,00 157,33

Einen gleitenden Durchschnitt mit $setWindowFields berechnen

Zum Berechnen des gleitenden Durchschnitts erstellen wir eine Aggregation-Pipeline, deren zentrale Stage $setWindowFields darstellt, die seit MongoBD 5.0 verfügbar ist. Diese Stage führt Berechnungen auf Datenbereichen in der Collection aus und fügt das Ergebnis als neue Felder an das Dokument an:

[{
 $setWindowFields: {
  partitionBy: null,
  sortBy: {
   ts: 1
  },
  output: {
   avg: {
    $avg: '$value',
    window: {
     range: [
      -2,
      0
     ],
     unit: 'day'
    }
   }
  }
 }
}]

Folgendes ist bei obigem Beispiel zu beachten:

  • Die Angabe von partitionBy ermöglicht es, die Datenbereiche in Partitionen aufzugliedern. So kann beispielsweise für jeden Sensor der gleitende Durchschnitt gesondert berechnet werden. In unseren Beispieldaten ist dies jedoch nicht relevant, so dass wir über null signalisieren, dass keine Partitionen gebildet werden müssen.
  • Das Window wird anhand des Datumsbereichs gebildet, da wir range verwendet haben. Dies wird als time range window bezeichnet. Über die Unit day geben wir an, dass die Grenzen für den Bereich anhand der Tage gebildet werden. Der Range [-2, 0] gibt also an, dass wir die Daten der letzten beiden und des aktuellen Tages im Fenster einschließen wollen. Die Grenzen sind hier also inklusive.
  • Durch die Verwendung der window function $avg bilden wir den Durchschnitt über die Werte und legen ihn im Feld avg ab.
  • Bei der Verwendung eines time range windows ist es notwendig, ein sortBy anzugeben, das ausschließlich aus Datumswerten besteht. Deshalb wird in unserem Fall nach dem ts-Feld sortiert.

Dies führt zu folgender Dokumentenstruktur nach der Stage:

{
  "_id": ObjectId("63ca9e13f0a91511db9cc269"),
  "ts": ISODate("1670976000000"),
  "value": 144,
  "avg": 157.33333333333334
}

Anschließend können die Dokumente weiter angepasst werden; außerdem können in der $setWindowFields-Stage auch mehrere Felder mit unterschiedlichen Fenstern berechnet werden. Durch eine abschließende $merge-Stage können die Daten in einer Collection abgelegt werden, so dass sie zukünftig ohne Berechnung verfügbar sind.

Fazit

Ein gleitender Durchschnitt hilft dabei, Trends und Entwicklungen in Daten erkennen zu können. Die mächtige Stage $setWindowFields erlaubt die Berechnung von Felder für einen bestimmten Datenausschnitt, so dass es mit MongoDB auf einfache Weise möglich ist, einen gleitenden Durchschnitt zu berechnen.


MongoDB Analyse Time Series Collections NoSql
Markus Wildgruber
Markus Wildgruber

Geschäftsführer

  • CloudArchitect
  • DeveloperCoach
  • DotNet
  • MongoDB
  • Angular