Blog der Heimetli Software AG

Regenmenge visualsieren mit Pandas und Matplotlib

Der Regensensor" arbeitet nach dem Tipping-Bucket Prinzip und zählt wie oft die Wippe kippt. Die Rohdaten zeigen also eine stetig ansteigende Kurve:

Rohdaten des Regensensors

Diese Darstellung macht zwar auch Sinn, denn sie zeigt wieviel Regen insgesamt gefallen ist. Da der Regen aber versickert wollte ich eine Darstellung die zeigt wann genau es geregnet hat.

Niederschlag in 10 Minuten

Das Auswertescript loggt den Zählerstand, also brauchen wir auf jeder Zeile die Differenz zum vorangehenden Stand. Pandas berechnet das mühelos:

df["diff"] = df[2].diff()

Bei dieser Berechnung entsteht aber ein NaN-Value am Anfang, weil die erste Zeile keinen Vorgänger hat. Das muss gefiltert werden damit es später keine Exceptions gibt. Nach etwas Feinbearbeitung kriegen wir dann die gewünschte Darstellung:

Rohdaten des Regensensors

Das Python-Script

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from matplotlib.dates  import DateFormatter

plt.style.use( "ggplot" )

# Read the logfile
df = pd.read_csv( "rainyday.csv", header=None )

# Convert the datetime column
df[0] = pd.to_datetime(df[0])

# Determine difference between rows
df["diff"] = df[2].diff()

# Drop the first difference which is NaN
df = df.dropna()

# Group the differences and sum the groups
sums = df.groupby( pd.Grouper(key=0,freq="10min",axis=0) ).sum().reset_index()

# One tip means 0.45 liters
sums["rain"] = sums["diff"] * 0.45

# Create a new column with formatted time
sums["time"] = sums[0].dt.strftime( "%H:%M" )

# Create the plot
ax = sums.plot( x="time", y="rain", kind="bar", width=1, legend=None )

# Reduce the ticks to keep them readable
ax.xaxis.set_major_locator( MaxNLocator(40) )

# Set title and labels
plt.title( "Regenmenge pro 10min, August 2024" )
plt.xlabel( None )
plt.ylabel( "Regen [Liter]" )

# Search the midnight rows
midnights = sums[sums["time"] == "00:00"].index

# Add separators and date strings
left = 0
day  = sums.iloc[0,0].day
for m in midnights:
    plt.axvline( m, color="blue" )
    plt.text( (left + m) / 2, 3.9, f"{day}. August", color="blue", horizontalalignment="center", size="x-large" )
    left = m
    day += 1

# Save the plot as SVG
plt.savefig( "rain.svg", bbox_inches="tight" )

Nachbearbeitung

Damit das SVG schön skalierbar wird müssen noch die Attribute **width** und **height** aus dem svg-Element geputzt werden. Dafür habe ich bisher keinen Weg gefunden, deshalb habe ich sie einfach mit dem Editor entfernt...

Ja. ich weiss das man das mit XSLT hinkriegt, aber das war mir zu mühsam. Mit dem Editor ging es einfach schneller.