Daten des Feinstaubsensors im The Things Network auswerten

Kategorien: internet of things

Wir haben bisher eine Applikation und einen Sensor im The Things Network angelegt, den Sensor mit der Arduino-IDE programmiert und die Daten in einen Data Storage geschoben. Im letzten Artikel haben wir uns auch schon angesehen, wie man prinzipiell wieder an seine Daten herankommt. Jetzt wollen wir noch ein hübsches Diagramm mit Python erzeugen.

Daten abholen und auswerten

Die Daten kann man in Python leicht abholen und als JSON-formatierten Text ausgeben.

import urllib

# insert here the URL from the Swagger page
url = 'https://mein-test.data.thethingsnetwork.org/api/v2/query'

# insert here the duration you want to retrieve data for
args = '?last=2d'

# insert here the access key
access_key = 'ttn-account-v2.eK8RestGekuerzt'

headers = {
  'Accept': 'application/json',
  'Authorization': 'key ' + access_key
}

req = urllib.request.Request(url + args, headers=headers)

with urllib.request.urlopen(req) as f:
    print(f.read().decode('utf-8'))

Mit pandas kann man mit Python gut große Mengen an Daten analysieren und anzeigen. Auch wenn pandas ziemlich komplex ist, kommt man doch schnell zu den ersten Ergebnissen. In den Beispielen liest pandas oft einfach eine CSV-Datei ein. Auch Excel-Dateien kann es einlesen. Wie sieht es mit JSON-Dateien ein?

Ich habe keine Ahnung. Ein Beispiel habe ich nicht gleich gefunden, aber ganz schnell eine andere Lösung. Die Grundstruktur in pandas ist ein DataFrame. Den kann man im Konstruktor mit den Daten aus einem Dictionary füllen. Also kann man die JSON-Daten mit der json-Bibliothek laden und das Dictionary an den DataFrame übergeben. Wir ersetzen in unserem Beispiel von oben die print()-Funktion mit dem folgenden Kode.

    import json

    data = json.load(f)

Halt! Nicht so schnell. Wie groß wird denn das Dictionary. Reicht unser Speicherplatz im RAM? Wir zeichnen alle 15 Minuten einen Datensatz aus acht Byte auf, der als JSON allerdings noch etwas aufgeblasen wird. Der Data Storage speichert unsere Daten für sieben Tage. Jede Stunde hat viermal 15 Minuten. Jeder Tag hat 24 Stunden. Also haben wir maximal n = 7 * 24 * 4 = 672 Datensätze. Selbst wenn jeder Datensatz 1 KiBiByte hätte, was er bei weitem nicht hat, würde er bei einem aktuellen Rechner in den Hauptspeicher passen. Glück gehabt.

Jetzt haben wir in data unser Dictionary mit den Daten aus dem Data Storage. Nach dem Import der pandas-Bibliothek kann man jetzt aus dem Dictionary einen DataFrame erzeugen. Upps, bei manchen schlägt der Import fehl. Die Bibliothek muss eventuell erst noch mit conda oder pip installiert werden, aber dann…

    import pandas as pd

    df = pd.DataFrame(data)

Gibt man df mit print(df) aus, so bekommt man eine ordentliche Tabelle mit unseren Daten. Allerdings werden nur die ersten und letzten 30 Datensätze angezeigt. Man kann auch schon die Daten mit df.plot() in einem Diagramm anzeigen lassen. Damit pandas die Zeit als Ordinate (x-Achse) verwendet, muss es die Zeit, die wir in der Spalte time haben, im eigenen Format haben. Das kann man leicht mit der Funktion to_datetime() erreichen.

    df['time'] = pd.to_datetime(df['time'])

In meinem Fall sind im Data Storage auch die Daten, die der Sensor während der Entwicklung auf meinem Schreibtisch gesammelt hat. Diese Daten will ich nicht in meinem Diagramm haben, also nehmen wir nur die Datensätze nach dem Zeitpunkt, an dem ich den Sensor draußen eingeschaltet habe.

    df = df.loc[lambda x: x.time > datetime(2018, 12, 31, 15, 58, 00)]

Jetzt können wir noch festlegen, dass wir eine Zeitreihe ts nach der Zeit indexiert haben wollen und können das Ganze als Diagramm ausgeben. Mit secondary_y legen wir fest, dass die Skalierung für die Feuchtigkeit und Temperatur auf der rechten Seite des Diagramms angezeigt wird.

    ts = df.set_index('time')
    ts.plot(secondary_y=('humidity', 'temperature'), figsize=(16,9))

Ergebnis

Hier noch einmal das Programm als Ganzes schön sortiert.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan  2 16:32:20 2019

@author: derSuessmann
"""

import urllib
import json
import pandas as pd
from datetime import datetime

# insert here the URL from the Swagger page
url = 'https://mein-test.data.thethingsnetwork.org/api/v2/query'

# insert here the duration you want to retrieve data for
args = '?last=7d'

# insert here the access key
access_key = 'ttn-account-v2.eK8RestGekuerzt'

headers = {
        'Accept': 'application/json',
        'Authorization': 'key ' + access_key
        }

req = urllib.request.Request(url + args, headers=headers)

with urllib.request.urlopen(req) as f:
    data = json.load(f)
    
    df = pd.DataFrame(data)
    df['time'] = pd.to_datetime(df['time'])
    df = df.loc[lambda x: x.time > datetime(2018, 12, 31, 15, 58, 00)]
    ts = df.set_index('time')
    ts.plot(secondary_y=('humidity', 'temperature'), figsize=(16,9))

Es liefert das folgende Diagramm.

Feinstaub an Sylvester

Fazit und Ausblick

Das Diagramm war recht einfach zu erzeugen, aber mich stört, dass die Messwerte mit Linien verbunden werden. Mir ist nicht klar, warum das Diagramm als Standard die Messwerte mit Linien verbindet. Das ist fast immer falsch! Eine Linie bedeutet, dass alle Zwischenwerte auf dieser Linie liegen. Es fehlen auch noch die Einheiten an den Achsen. Im Beispiel muss man wissen, dass die Feinstaubwerte als Mikrogramm pro Kubikmeter, die Feuchtigkeit als Prozent und die Temperatur in Grad Celsius angegeben wurde. Diese Mängel lassen sich bestimmt beseitigen. Wer traut sich da ran?

Das könnte Sie auch interessieren