KQL - Rekonstruktion fehlender Reihen-Elemente mit 'make-series' und 'series-fill-linear'

Von Lukas Hillesheim, 14. Juli 2024
Blog Article & Copyright by Lukas Hillesheim
Reihen wie z.B. Zeitreihen sind nützlich, wenn es darum geht, Vorhersagen zu treffen oder Muster zu erkennen. KQL bietet eine Fülle von Funktionen für die Arbeit mit Reihen an. Der folgende Artikel zeigt, wie die beiden Funktionen make-series und series-fill-linear verwendet werden kann, um fehlende Werte aus den vorhandenen Werten mittels Interpolation zu konstruieren.

Inhalt:

1. Szenario

Dem Artikel liegt folgendes Szenario zu Grunde:
  • Schritt 1. Es wird eine Reihe konstruiert, die eine Sinus-Kurve repräsentiert, und der Variablen sinusoid zugewiesen
  • Schritt 2. Aus der Reihe sinusoid wird der Bereich 0.5 bis 1 entfernt. Die unvollständige Reihe wird der Variablen missing_range zugewiesen
  • Schritt 3. Auf Basis der Reihe missing_range wird eine neue Reihe abgeleitet und der Variablen recomp_xitems zugewiesen. Die Reihe recomp_xitems enthält neue, künstlich berechnete Elemente, die den fehlenden Bereich 0.5 bis 1 ersetzen. Die Elemente stellen die X-Achsen-Elemente der Sinuskurve dar. Die Y-Werte werden für alle neuen Elemente auf -1 gesetzt
  • Schritt 4. Auf Basis der Reihe recomp_xitems wird eine neue Reihe abgeleitet und der Variablen recomp_yitems zugewiesen. In dieser Reihe werden für alle X-Elemente mit dem Wert -1 neue - passende - Werte berechnet.

2. Vollständiger KQL Code

Nachfolgend der vollständige Code. Die einzelnen Abschnitte werden später erklärt:
let sinusoid = 
    print x = range(0.01, 5, 0.03) 
    | mv-expand x
    | serialize 
    | extend  
        row_ix = row_number(1), 
        x = round(todouble(x), 2), 
        y = sin(todouble(x))
    | project-reorder 
        row_ix, 
        x, 
        y
    | sort by row_ix asc; 
let missing_range = 
    sinusoid 
    | where x < 0.5 or x > 1; 
let recomp_xitems = 
    missing_range
    | make-series 
        y = avg (todouble(y))
        default = -1 // Set -1 for new items  
        on x
        from 0.1 to 5
        step 0.03; 
let recomp_yitems = 
    recomp_xitems 
    | extend  
        y = series_fill_linear(y, -1, false); 
recomp_yitems  
    | mv-expand x, y 
    | serialize 
    | extend 
        row_ix = row_number(1), 
        x = round(todouble(x), 2), 
        y = todouble(y)
    | render linechart with (xcolumn=x, ycolumns=y)

3. Vollständige Reihe und Visualisierung

Mit dem nachfolgenden KQL-Code wird eine Sinuskurve konstruiert und dargestellt:
  • Die Reihe basiert auf einem Decimal-Array im Bereich 0.01 bis 5, das mit der range-Funktion erzeugt wird. Die Schrittweite 0.03 soll dafür sorgen, dass bei der Darstellung keine Artefakte auftreten
  • Eine Spalte row_ix mit einer AutoId wird hinzugefügt, um jede Zeile identifizieren und die Daten ggf. sortieren zu können
  • Die Daten der Reihe werden der Variablen sinusoid zugewiesen und mit der Funktion render visualisiert
KQL-Code:
let sinusoid = 
    print x = range(0.01, 5, 0.03) 
    | mv-expand x
    | serialize 
    | extend  
        row_ix = row_number(1), 
        x = round(todouble(x), 2), 
        y = sin(todouble(x))
    | project-reorder 
        row_ix, 
        x, 
        y
    | sort by row_ix asc; 
sinusoid
| render linechart with (xcolumn=x,ycolumns=y)
Output:
Sinuskurve
Abb.1: Sinuskurve

4. Reihen mit fehlenden Elementen

Mit dem KQL-Code des Abschnitts 4. wird simuliert, dass es bei der Erhebung von Daten auch zu fehlenden Werten kommen kann. Die in der Variablen sinusoid enthaltene Reihe wird modifiziert, indem die Elemente im Bereich 0.5 bis 1 entfernt werden. Das Ergebnis wird der Variablen missing_range zugewiesen:
KQL-Code:
let missing_range = 
    sinusoid 
    | where x < 0.5 or x > 1; 
missing_range
Output:
Fehlender Bereich 0.5 bis 1
Abb.2: Fehlender Bereich 0.5 bis 1

5. Rekonstruktion fehlender Elemente

Ausgehend von einer Reihe mit fehlenden Elementen kann nun die KQL-Funktion make-series diese Elemente rekonstruieren. Im Beispiel erhalten alle neuen Elemente den Wert -1.
KQL-Code:
... 
let recomp_xitems = 
    missing_range
    | make-series 
        y = avg (todouble(y))
        default = -1 // Set -1 for new items  
        on x
        from 0.1 to 5
        step 0.03; 
recomp_xitems
Output:
Rekonstruktion fehlender Elemente (1)
Abb.3: Rekonstruktion fehlender Elemente (1)
Der Screenshot zeigt, dass die Tupels für X- und Y-Achse in zwei voneinander getrennten Arrays untergebracht sind. Aus den beiden Arrays könnten die Tupels (x,y) gebildet werden, indem beide Arrays gleichzeitig durchlaufen werden. In Python bewerkstelligt dies eine Funktion namens map, in KQL die Funktion mv-expand. Um den Inhalt der Arrays für das Szenario darzustellen und zu zeigen, dass make-series die fehlenden X-Achsen-Elemente tatsächlich künstlich hinzugefügt hat, wird nachfolgend der Inhalt der Variablen recomp_xitems mit Hilfe von mv-expand angezeigt. Die Extraktion ist aber nur aus Gründen der Nachvollziehbarkeit notwendig. Im vollständigen Code-Beispiel von oben fehlt der Zwischenschritt, da die nächste - im Szenario angewandte Funktion - series-fill-linear ist und series-fill-linear die Daten in einem Array-Format erwartet. Es wäre also ein überflüssiger Schritt, die Daten aus den beiden Arrays erst zu entpacken und dann wieder für series-fill-linear zu packen. 
Die Ausgabe ist auf X-Werte grösser oder gleich 0.46 eingeschränkt.
KQL-Code:
... 
let recomp_xitems = 
    missing_range
    | make-series 
        y = avg (todouble(y))
        default = -1 // Set -1 for new items  
        on x
        from 0.1 to 5
        step 0.03; 
recomp_xitems
    | mv-expand x, y 
    | extend 
        x = round(todouble(x), 2) 
    | project-reorder 
        x, 
        y
    | where x >= 0.46 
    | sort by x asc 
Output:
Rekonstruktion fehlender Elemente (2)
Abb.4: Rekonstruktion fehlender Elemente (2)

6. Rekonstruktion fehlender Werte

Im letzten Schritt wird die Funktion series-fill-linear verwendet, um die Dummy-Werte -1 durch solche neuen Werte zu ersetzen, die zu den vorhandenen Reihen-Werten passen.
KQL-Code:
... 
let recomp_yitems = 
    recomp_xitems 
    | extend  
        y = series_fill_linear(y, -1, false); 
recomp_yitems
Output:
Rekonstruktion fehlender Werte (1)
Abb.5: Rekonstruktion fehlender Werte (1)
Wie in 5. Rekonstruktion fehlender Elemente auch, wird der Inhalt der Arrays entpackt, um die neu zugewiesenen Werte anzuzeigen:
KQL-Code:
... 
let recomp_yitems = 
    recomp_xitems 
    | extend  
        y = series_fill_linear(y, -1, false); 
recomp_yitems
    | mv-expand x, y 
    | extend 
        x = round(todouble(x), 2) 
    | project-reorder 
        x, 
        y
    | where x >= 0.46 
    | sort by x asc 
Output:
Rekonstruktion fehlender Werte (2)
Abb.6: Rekonstruktion fehlender Werte (2)

7. Darstellung der rekonstruierten Reihe als Linechart

Rekonstruierte Reihe als Linechart dargestellt.
KQL-Code:
... 
recomp_yitems  
    | mv-expand x, y 
    | serialize 
    | extend 
        row_ix = row_number(1), 
        x = round(todouble(x), 2), 
        y = todouble(y)
    | render linechart with (xcolumn=x, ycolumns=y)
Output:
Rekonstruierte Reihe als Linechart
Abb.7: Rekonstruierte Reihe als Linechart
Der rekonstruierte Bereich 0.5 bis 1 ist nicht so glatt wie der restliche Bereich. Durch Anwendung weiterer Verfahren könnte dies wegoptimiert werden.