Umgang mit NumPy

NumPy ist eine Python-Bibliothek zur Handhabung großer Datenmengen in Arrays und stellt die technologische Basis für arraybasierte NumPy-Traceschrittvorlagen dar. Das offizielle Handbuch von NumPy ist über folgenden Link erreichbar: https://docs.scipy.org/doc/numpy/index.html

Als einführende Literatur sei insbesondere das Quickstart-Tutorial empfohlen: https://docs.scipy.org/doc/numpy/user/quickstart.html

Ergänzend dazu werden nachfolgend einige Aspekte beleuchtet, die für die Traceanalyse besonders relevant sind.

Boolsche Operatoren

Ob ein Numpy-Array Elemente enthält oder einzelne Elemente des Arrays wahr sind, kann nicht implizit über if arr: print('hallo') oder direkt über den Wahrheitswert bool(arr) geprüft werden. Stattdessen müssen entsprechende Mechanismen von NumPy genutzt werden, die im nachfolgenden Beispiel erläutert werden:

import numpy as np

arr = np.array([3, 1, -2, 0], dtype=np.float64)

# Ist arr gültig?
if isinstance(arr, np.ndarray):
    print('arr ist ein gültiges NumPy-Array')

# Ist arr leer?
if len(arr) > 0:
    print('arr ist nicht leer')

# Umgang mit Wahrheitswerten
positive = arr >= 0                    # Neues Array, das Ergebnis der elementweisen Prüfung auf >= 0 enthält: [True, True, False, True]
print(np.all(positive))                # False, da nicht alle Elemente von positive True sind
print(np.any(positive))                # True, da positive mindestens einmal (im konkreten Fall dreimal) True enthält

Vorsicht

Der Wahrheitswert von np.nan („not a number“) ist True! Der Ausdruck np.all(np.array([0.1, 3.0, np.nan])) ergibt dementsprechend True.

Maskierung

Für den Zugriff auf Einzelwerte oder Teil-Arrays kann auch für NumPy-Arrays der Index-/Slicing-Mechanismus von Python verwendet werden:

import numpy as np

arr = np.arange(10)                    # [0 1 2 3 4 5 6 7 8 9]
print(arr[3:5])                        # [3, 4]
print(arr[:4])                         # [0, 1, 2, 3]
print(arr[8:])                         # [8, 9]

NumPy bietet allerdings noch deutlich leistungsfähigere Möglichkeiten zur Auswahl einer Teilmenge von Werten. Zum einen können durch Angabe einer boolschen Maske bestimmte Werte selektiert werden, zum anderen können gewünschte Elemente auch in einem Array von Indizes angegeben werden.

Boolsche Maskierung: Alle Werte >= threshold

import numpy as np

arr = np.array([0, 3, 7, 13, 10, 11, 12.5, 15])
threshold = 12

mask = arr >= threshold                # Bool-Maske: [False, False, False, True, False, False, True, True]
print(arr[mask])                       # [13, 12.5, 15]

Boolsche Maskierung: Alle Signalwerte innerhalb zeitlicher Grenzen

import numpy as np

values = np.array([100, 200, 300, 400, 500])
timestamps = np.arange(5.0)            # [0.0, 1.0, 2.0, 3.0, 4.0]

# Alle Werte im geschlossenen Zeitintervall von 1.0 bis 2.5
mask = np.logical_and(1.0 <= timestamps, timestamps <= 2.5)
print(values[mask])                     # [200, 300]

Index-Array: Signalwechsel von 20 auf 30

import numpy as np

values = np.array([0, 10, 20, 30, 40])
timestamps = np.arange(5.0)            # [0.0, 1.0, 2.0, 3.0, 4.0]

# Bool-Maske erstellen, die True enthält, wo eine 20 gefolgt von einer 30 gefunden wird
boolMask = np.logical_and(values[:-1] == 20, values[1:] == 30)  # [False, False, True, False]
# Entsprechendes Index-Array erstellen, Obacht: boolMask enthält ein Element weniger als values
indexArr = np.arange(values.shape[0] - 1)[boolMask]  # [2]
# indexArr enthält bisher Position der 20, es soll jedoch der Wechsel 20 -> 30 gefunden werden
indexArr = indexArr + 1                # [3]

# Alternativ mit numpy.where()
indexArr = np.where(boolMask)[0] + 1   # [3]

print(timestamps[indexArr])            # [3.0]
print(values[indexArr])                # [30]