Übungsaufgaben zu Lektion 6

Vorab

Eine Anleitung für die Bearbeitung und Abgabe der Übunsgaufgaben in Python findet Ihr im Abgabebereich des Moodlekurses ( Link ).

Kurzform: Eine Aufgabe besteht immer aus einer "Funktion", die für einen definierten Input (Argumente) einen definierten Output zurückgeben soll (engl.: return). Überprüft die Korrektheit dieses Outputs und damit Eurer Lösung mit testme(funktionsname). Wenn Ihr fertig seid, erzeugt unter im Abschnitt Gesamtauswertung Euer Zertifikat und gebt es dann in Moodle zusammen mit diesem Notebook ab.

Hier ein Beispiel für eine Funktion: Die Funktion heißt beispiel, hat die Argumente (den Input) x und y und gibt die Variable ergebnis zurück. Das Ergebnis ist in diesem Fall die Summe der Argumente x und y.

In [ ]:
def beispiel(x, y):
    ergebnis = x + y
    return(ergebnis)
In [ ]:
beispiel(1,1)
In [ ]:
beispiel(2,-3)

Testzentrum importieren!

In [ ]:
from testzentrum import *

Weitere benötigte Pakete importieren

In [ ]:
import numpy as np
import datetime as dt
import geopandas as gpd

Noch auf ein Wort

Dies ist unsere letzte Übungsrunde. Wir wollen in dieser Runde einen Rundumschlag über das bereits Gelernte geben. Es können also Inhalte aus allen Lektionen drankommen - juchuu!

Kleine Vorbereitung auf git

Das Arbeiten mit der Versionsverwaltungssoftware git lernen wir eigentlich erst in der nächsten Lektion. Hier aber schonmal ein kleiner Vorgeschmack. Keine Sorge, es geht in dieser Aufgabe nur um Stringverkettung und Ihr braucht eigentlich gar keine git Kenntnisse...

Trotzdem kurz der Hintergrund: Beim Arbeiten mit git liegt Euer Code (Python, R oder was auch immer) in einem lokalen "Repository". Ihr könnt in dieses Repository Eure eigenen Änderungen am Code einpflegen. Ihr könnt aber auch Änderungen anderer Personen aus sogenannten "remote repositories" (also z.B. auf Git.UP unser Kursrepository) in Euer lokales Repository "reinziehen" (pull). Der Befehl dazu lautet: git pull {REMOTENAME} {BRANCHNAME}. Ein Branch ist sowas wie ein "Entwicklungszweig". Wir benutzen hier als {BRANCHNAME} immer master.

Eure Funktion git_pruefung erhält nun als Argument remote den Namen des Remote Repository, also {REMOTENAME}. Die Funktion soll nun den vollständigen Befehl zurückgeben, mit dem Ihr Änderungen aus dem Remote Repository {REMOTENAME} in Euer lokales Repository zieht.

Alles sehr verwirrend? Am Beispiel aber sehr einfach:

>>> git_pruefung("erich")
'git pull erich master'
>>> git_pruefung("Leonid")
'git pull Leonid master'

Hinweis: Trennt jedes Element des gesamten Befehls mit EINEM Leerzeichen.

In [ ]:
def git_pruefung(remote):
    pass
In [ ]:
testme(git_pruefung)

Dicke Luft

Das 2-D Array x enthalte Werte der $NO_x$-Konzentration an städtischen Hotspot-Messstellen. Die Zeilen stellen die Zeitschritte dar, die Spalten die unterschiedlichen Messstellen.

Die Funktion alarm gebe für jeden Zeitschritt die Zahl der Stationen zurück, an denen der Schwellenwert schwelle überschritten wird.

Beispiel:

>>> schwelle = 30
>>> array([[22.5, 57. , 43.9],
           [35.9,  9.4,  9.4],
           [ 3.5, 52. , 36.1],
           [42.5,  1.2, 58.2],
           [49.9, 12.7, 10.9]])
Ergebnis:
   [2, 1, 2, 2, 1]

Achtung: Das Ergebnis soll als Liste zurückgeben werden (.tolist()).

In [ ]:
def alarm(x, schwelle):
    pass
In [ ]:
testme(alarm)

Daten ohne Fehl und Tadel

Im 2-D Array x sind Fehlwerte durch eine Zahl flag markiert. Die Funktion ohne_fehl soll für jede Zeile das Minimum zurückgeben, Fehlwerte sind hierbei aber zu ignorieren! Das Ergebnis soll als Liste zurückgegeben werden! (mit .tolist()).

Beispiel:

>>> flag = -99
>>> x = array([[ 14,  15, -99,  18, -99],
               [  7, -99,   2,   4,   8],
               [  0,  14, -99,   2,   2],
               [-99,  19, -99, -99, -99]])
Ergebnis
--------
   [14, 2, 0, 19]

Hinweise:

  • Es gibt viele Möglichkeit, die Aufgabe zu lösen.
  • Ein eleganter Weg ist es, zunächst alle Werte, die flag entsprechen, in np.nan umzuwandeln
  • Anschließend könnt Ihr die Funktion np.nanmin nutzen...
In [ ]:
def ohne_fehl(x, flag):
    pass
In [ ]:
testme(ohne_fehl)

Bello, hol' das CRS!

Die Funktion get_crs gebe das CRS (Coordinate Reference System) eines Vektordatensatzes zurück, der unter pfad liegt. Die Rückgabe soll als EPSG-Code erfolgen (Datentyp int). Der Datensatz liegt in einer Datei, die mit geopandas gelesen werden kann.

Beispiel:

>>> pfad = data/landuse1.shp
Ergebnis: 4326
In [ ]:
def get_crs(pfad):
    pass
In [ ]:
testme(get_crs)

Reaktionszeit

Als Reaktionszeit (engl. response time) eines Einzugsgebiets bezeichnet man die Verzögerung zwischen dem zeitlichen Schwerpunkt des Niederschlags und dem darauf folgenden Abflusspeak. dtprec sei der Zeitpunkt des Niederschlagsschwerpunkts und dtrunoff der Zeitpunkt des Abflusspeaks. Beide sind als Strings der Art "2021-12-31T20:22:59" gegeben (sog. Isoformat). Die Funktion responsetime gebe die Reaktionszeit des Einzugsgebiets in Sekunden zurück.

Vorgehensweise:

  • Strings in datetime-Objekte umwandeln
  • Differenz berechnen
  • Differenz als Sekunden zurückgeben (findet selbst raus, wie das geht!)

Beispiel:

>>> dtprec = "2021-12-31T20:22:59"
>>> dtrunoff = "2022-01-01T12:22:00"
Ergebnis: 57541.0
In [ ]:
def responsetime(dtprec, dtrunoff):
    pass
In [ ]:
testme(responsetime)

Eine endgegnermäßige Aufgabe zur Wasserbilanz

Eine Datei enthalte zeilenweise Daten eines wägbaren Lysimeters, und zwar für die folgenden Variablen:

  • start: Beginn des Messzeitraums (im Format %Y-%m-%dT%H:%M:%S)
  • end: Ende des Messzeitraums (im Format %Y-%m-%dT%H:%M:%S)
  • deltaS: Massenänderung des Lysimeters im Messzeitraum (in kg)
  • Q: Masse des Sickerwassers (in kg), welches im Bezugszeitraum aus dem Lysimeter gesickert ist
  • P: Niederschlagsmenge (in mm).

Die Oberfläche des Lysimeters sei 1 m².

Du sollst nun die mittlere Verdunstungsrate $\frac{ET}{\Delta(t)}$ im Messzeitraum in der Einheit $mm/h$ zurückgeben: also das verdunstete Volumen (in Litern) pro Fläche (in m²) und Zeit (in $h$). Nimm als Dichte für Wasser 1 kg/l an. Hier nochmal zur Erinnerung die Wasserbilanzgleichung:

$(P - ET - Q - deltaS) \frac{1}{\Delta(t)} = 0$

Soweit so gut. Leider ist die Datei sehr merkwürdig formatiert! Hier mal ein Beispiel für zwei Zeilen:

":start=2020-06-01T00:00:00;Hallo Mutti!end=2020-06-15T00:00:00;deltaS=30;Q=10;P=100:"
":start=2020-06-15T00:00:00;Hi Thorsten!P=90;deltaS=40;Q=10;end=2020-07-01T00:00:00:"

Bei so einem Datenschrott geht selbst pandas in die Knie.

  • Am Anfang und am Ende stehen immer völlig überflüssige Doppelpunkte (:...:)
  • Jemand hat die Datei missbraucht, um in jeder Zeile morningshowmäßig jemanden mit gruss zu grüßen (hier: Hallo Mutti! bzw. Hi Thorsten!)
  • Und jetzt kommt die größte Gemeinheit: Die Reihenfolge der eigentlichen Variablen ist jedes Mal unterschiedlich. Aber zum Glück sind ja die Variablen in jeder Zeile mit Variablenname=... beschriftet.
  • Freundlicherweise hat der oder die Wahnsinnige immer das gleiche Trennzeichen (;) verwendet.

Wirst Du es schaffen, dem Wahnsinn die Stirn zu bieten?

Deine Funktion lysimeter erhält immer nur eine Zeile des Datensatzes (Argument zeile) sowie das Argument gruss, welches ein String mit dem überflüssigen Gruß ist. Zurückgeben soll Deine Funktion die mittlere Verdunstungsrate über den gesamten Zeitraum in der Einheit $mm/h$, gerundet auf eine Nachkommastelle.

Tipps zur Vorghensweise:

  • Erst zeile bereinigen: Die einschließenden : mit strip, den störenden gruss mit replace.
  • Dann Einträge mit split trennen (welches Trennzeichen?). Das Ergebnis ist eine Liste, die Du z.B. items nennen kannst
  • Lege nun einen leeren Dictionary namens data an mit data = {}
  • Über die Liste items legen wir nun eine for-Schleife an:
    • Jeder Eintrag von items wird nochmal mit split behandelt (welches Trennzeichen?), das Ergebnis ist eine Liste mit zwei Einträgen, nennen wir diese Liste mal paar.
    • Der erste Eintrag von paar ist der Name der gemessenen Variable (z.B. "deltaS"), der zweite Eintrag ist ein String mit dem Wert (z.B. "10.1". Lege nun mit jedem Durchlauf der Schleife im Dictionary data einen neuen Eintrag an, also data[paar[0]] = paar[1]
  • Damit hast Du Deine for-Schleife durch und alle Einträge hübsch im Dictionary. Diese Einträge sind aber noch Zeichenketten. Du musst nun die Einträge für "start" und "end" jeweils in ein datetime-Objekt umwandeln (mit dt.datetime.strptime); die Einträge "P", "Q" und "deltaS" in einen float.
  • Nun kannst Du mit dem Dictionary losrechnen:
    • aus den Messwerten das Verdunstungsvolumen berechnen
    • Aus dem Start- und Endzeitpunkt die Zeitdifferenz berechnen, diese in Stunden umrechnen
    • Aus Verdunstungsvolumen und Zeitdifferenz die Verdunstungsrate berechnen.
    • Ergebnis auf eine Nachkommastelle runden.
  • Voila.

Beispiel:

>>> gruss = "Hallo Heidi!"
>>> zeile = ":start=2020-06-01T00:00:00;end=2020-06-15T00:00:00;Hallo Heidi!deltaS=30;Q=10;P=100:"

Zwischenergebnisse:
Bereinigte Zeile:
   "start=2020-06-01T00:00:00;end=2020-06-15T00:00:00;deltaS=30;Q=10;P=100"
Dictionary data vor der for-Schleife:
   data = {}
Dictionary data nach der for-Schleife:
    data = {'start': '2020-06-01T00:00:00',
            'end': '2020-06-15T00:00:00',
            'deltaS': '30',
            'Q': '10',
            'P': '100'}
Dictionary data nach Umwwandlung in korrekte Datentypen:          
    data = {'start': datetime.datetime(2020, 6, 1, 0, 0),
            'end': datetime.datetime(2020, 6, 15, 0, 0),
            'deltaS': 30.0,
            'Q': 10.0,
            'P': 100.0}
Verdunstetes Volumen über den Gesamtzeitraum:
    60 Liter (100-10-30)
Dauer des Messzeitraums:
    336 Stunden

ENDERGEBNIS
60 Liter / (336 h * 1 m²) = 0.17857142857142858 mm/h
also: 0.2

Versucht die Lösung Stück für Stück zur entwickeln, bis Ihr sie letztlich zur FUnktion zusammensetzt.

In [ ]:
def lysimeter(zeile, gruss):
    # Erst die Zeile bereinigen
    # Nun splitten
    # Leeren Dictionary anlegen
    # For-Schleife über items
    # Type conversion
    # Wasserbilanz aufstellen
    pass
In [ ]:
testme(lysimeter)

Das Ei ist hart

Nach der umweltwissenschaftlichen Monsteraufgabe zum Lysimeter noch was zum Runterkommen - etwas aus der Zeit gefallen und doch ein brandaktueller Beitrag zur Genderdebatte: Loriots "Frühstücksei".

er und sie unterhalten sich strikt abwechselnd, er beginnt. Die untenstehenden Listen enthalten in korrekter Reihenfolge die Redebeiträge der beiden.

Die Funktion das_ei soll Dir Deine Lieblingsstellen im Dialog als String zurückgeben.

Das Argument wer ist ein String und gibt an, wer als erstes spricht: "er" oder "sie". Das Argument i gibt den Index in der zugehörigen Liste er bzw. sie. An die entsprechende Dialogzeile er[i] bzw. sie[i] soll dann noch die passende Replik des Partners angehängt werden, getrennt durch den String " - ".

Ist verwirrend? Am Beispiel hoffentlich klarer:

Beispiel:

>>> wer = "er"
>>> i = 2
Ergebnis: "Das Ei ist hart!!! - Ich habe es gehört ..."
>>> wer = "sie"
>>> i = 3
Ergebnis = "Zu viele Eier sind gar nicht gesund! - Ich meine, wie lange dieses Ei gekocht hat ...?"

Du musst in dieser Aufgabe eine if-Abfrage nutzen, Listen korrekt indizieren und Strings verketten. Beachte: Die Antwort auf er[i] ist sie[i], aber die Antwort auf sie[i] ist er[i+1].

In [ ]:
er = ["Berta!",
      "Das Ei ist hart!",
      "Das Ei ist hart!!!",
      "Wie lange hat das Ei denn gekocht?",
      "Ich meine, wie lange dieses Ei gekocht hat ...?",
      "Das weiß ich ..."
      "Ich meine, wie lange dieses Ei gekocht hat ...?",
      "Weil dieses Ei nicht viereinhalb Minuten gekocht haben kann!",
      "Wieso ist es dann mal zu hart und mal zu weich?",
      "Ach! ... Und woher weißt du, wann das Ei gut ist?",
      "Nach der Uhr oder wie?",
      "Im Gefühl? Was hast du im Gefühl?",
      "Aber es ist hart ... vielleicht stimmt da mit deinem Gefühl was nicht ...",
      "Jaja ... jaja ... jaja ... wenn ein Ei nach Gefühl kocht, kocht es eben nur zufällig genau viereinhalb Minuten.",
      "Ich hätte nur gern ein weiches Ei und nicht ein zufällig weiches Ei! Es ist mir egal, wie lange es kocht!",
      "Nein - nein ...", 
      "Das habe ich doch gesagt ...",
      "Ich hätte nur gern ein weiches Ei ...",
      "Ich bringe sie um ... morgen bringe ich sie um!"
     ]

sie = ["Ja ...",
       "(schweigt)",
       "Ich habe es gehört ...", 
       "Zu viele Eier sind gar nicht gesund!",
       "Du willst es doch immer viereinhalb Minuten haben ...",
       "Was fragst du denn dann?"
       "Ich koche es aber jeden Morgen viereinhalb Minuten.",
       "Ich weiß es nicht ... ich bin kein Huhn!",
       "Ich nehme es nach viereinhalb Minuten heraus, mein Gott!",
       "Nach Gefühl ... eine Hausfrau hat das im Gefühl ...",
       "Ich habe es im Gefühl, wann das Ei weich ist ...",
       "Mit meinem Gefühl stimmt was nicht? Ich stehe den ganzen Tag in der Küche, mache die Wäsche, bring deine Sachen in Ordnung, mache die Wohnung gemütlich, ärgere mich mit den Kindern rum und du sagst, mit meinem Gefühl stimmt was nicht?",
       "Es kann dir doch ganz egal sein, ob das Ei zufällig viereinhalb Minuten kocht ... Hauptsache, es kocht viereinhalb Minuten!",
       "Aha! Das ist dir egal ... es ist dir also egal, ob ich viereinhalb Minuten in der Küche schufte!",
       "Aber es ist nicht egal ... das Ei muss nämlich viereinhalb Minuten kochen ...",
       "Aber eben hast du doch gesagt, es ist dir egal!",
       "Gott, was sind Männer primitiv!"
      ]
In [ ]:
def das_ei(er, sie, wer, i):
    pass       
In [ ]:
testme(das_ei)

Gesamtauswertung

In [ ]:
get_certificate(testmysolutions=[git_pruefung, alarm, ohne_fehl, 
                                  get_crs, responsetime, lysimeter, das_ei],
                                 lesson=6)
In [ ]: