{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "79eb69bf",
   "metadata": {},
   "source": [
    "# Lernwerkstatt 03 — Datenqualität: „Wer hat recht?“\n",
    "\n",
    "**Wirtschaftsinformatik · Brick-Flow AG · Wintersemester 2026/27**\n",
    "\n",
    "> **Leitfrage der Woche:** *Der Vertrieb meldet 1.616 Bestellungen, das Controlling 1.594. Wer hat recht?*\n",
    "\n",
    "Beide rechnen richtig — und meinen Verschiedenes. In dieser Lernwerkstatt klärt ihr die Differenz\n",
    "**Zeile für Zeile** auf und schreibt am Ende eine belastbare Definition auf.\n",
    "\n",
    "> Diese Lernwerkstatt bearbeitet ihr in der **Digitalkompetenzwoche im Selbststudium**. Plant etwa\n",
    "> **100 Minuten** ein. Mit dieser Woche schließt ihr **Meilenstein 1** (erste Programmschritte) und\n",
    "> **Meilenstein 2** (Daten prüfen) ab."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d4b5202",
   "metadata": {},
   "source": [
    "### So arbeitet ihr mit diesem Notebook\n",
    "\n",
    "- **Von oben nach unten.** Führt jede Code-Zelle aus (Shift + Enter) und lest danach den Text darunter.\n",
    "  Bei den Beispielen ist der Code fertig und Zeile für Zeile kommentiert. Bei den Aufgaben am Ende schreibt ihr selbst.\n",
    "- **Kontrollzahlen.** Unter den Zellen steht, was ihr sehen solltet. Weicht euer Ergebnis ab, führt die Zelle\n",
    "  noch einmal aus und lest den Kommentar im Code.\n",
    "- **Hinweise zum Aufklappen.** Bei den Aufgaben stehen gestufte Hinweise (Pfeil anklicken). Versucht es erst\n",
    "  ohne, dann Hinweis 1, dann Hinweis 2, und so weiter.\n",
    "- **Fehlermeldungen sind Hinweise.** Lest die **letzte Zeile**: Wo steht der Fehler, welche Art von Fehler ist es,\n",
    "  was habt ihr zuletzt geändert?\n",
    "- **Nichts kann kaputtgehen.** Wenn etwas durcheinandergerät: Kernel neu starten und alle Zellen von oben ausführen.\n",
    "- **Wenn ihr hängen bleibt:** Macht trotzdem weiter. Die nächste Übung startet mit einem eigenen, bereitgestellten Datenstand."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a6af17f",
   "metadata": {},
   "source": [
    "## Teil 0 — Daten laden (der *fehlerbehaftete* Rohdatensatz)\n",
    "\n",
    "Heute arbeiten wir mit `brickflow_bestellungen_roh.csv`: der **ungeputzten** Fassung direkt aus den Vorsystemen.\n",
    "Dazu brauchen wir den Kundenstamm `brickflow_kunden.csv`, den wir erst in Teil 3e benutzen.\n",
    "\n",
    "Legt **beide Dateien in denselben Ordner wie dieses Notebook** (auf dem **HS-Bochum-Jupyter-Cluster**\n",
    "`jupyter.hs-bochum.de`: aus dem Kursordner kopieren oder hochladen)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89bc3f09",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "df     = pd.read_csv(\"brickflow_bestellungen_roh.csv\")   # die Bestellungen, ungeputzt\n",
    "kunden = pd.read_csv(\"brickflow_kunden.csv\")             # der Kundenstamm (brauchen wir erst in Teil 3e)\n",
    "\n",
    "print(\"Bestellungen:\", len(df), \"Zeilen,\", len(df.columns), \"Spalten\")\n",
    "print(\"Kunden      :\", len(kunden), \"Zeilen\")\n",
    "df.head()                                                # die ersten 5 Zeilen ansehen"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "400f4038",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** 1616 Bestellungen mit 6 Spalten, 550 Kunden. Die Tabelle zeigt wie in Woche 2 eine Zeile je\n",
    "Bestellung (`bestellnr`, `bestelldatum`, `kundennr`, `anzahl_pakete`, `bestellwert`, `code`)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff669828",
   "metadata": {},
   "source": [
    "## Teil 1 — Zuerst von Hand: zählen mit einer Schleife\n",
    "\n",
    "So zählt man **grundsätzlich**: einen Zähler hochzählen, Wert für Wert. Das üben wir an einer kleinen Handvoll\n",
    "Bestellwerte aus dem Rohdatensatz. Zwei neue Bausteine kommen dabei vor:\n",
    "\n",
    "- Eine **Liste** fasst mehrere Werte zusammen: in eckigen Klammern, durch Kommas getrennt.\n",
    "- Eine **`for`-Schleife** wiederholt einen Block, einmal für jeden Wert der Liste. `for wert in bestellwerte:` heißt\n",
    "  „für jeden Wert in der Liste `bestellwerte`“. In jedem Durchlauf steht der aktuelle Wert unter dem Namen `wert`.\n",
    "  Alles, was **eingerückt** unter dem `for` steht, gehört zur Schleife (genau wie bei `if` und `def` in Woche 2)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "74142e8d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Eine Liste mit 20 Bestellwerten aus dem Rohdatensatz\n",
    "bestellwerte = [98.62, 137.41, 103.59, 98.92, 0.0, 152.30, 88.10,\n",
    "                -129.90, 210.00, 98.62, 47.55, 133.00, 0.0, 175.20,\n",
    "                92.40, 118.75, 63.20, 149.99, 201.10, 77.00]\n",
    "\n",
    "anzahl_alle = 0     # Zähler 1: zählt jeden Wert. Start bei 0\n",
    "anzahl_echt = 0     # Zähler 2: zählt nur echte Bestellungen (Wert größer als 0)\n",
    "anzahl_unecht = 0   # Zähler 3: zählt alle übrigen Werte (Null oder negativ)\n",
    "\n",
    "for wert in bestellwerte:                  # für jeden Wert in der Liste, einen nach dem anderen:\n",
    "    anzahl_alle = anzahl_alle + 1          # eingerückt = gehört zur Schleife: Zähler 1 um 1 erhöhen\n",
    "    if wert > 0:                           # Fallunterscheidung wie in Woche 2: ist der Wert größer als 0 ...\n",
    "        anzahl_echt = anzahl_echt + 1      # ... zählt Zähler 2 mit\n",
    "    else:                                  # ... und sonst (Null oder negativ) ...\n",
    "        anzahl_unecht = anzahl_unecht + 1  # ... zählt Zähler 3 mit\n",
    "\n",
    "print(\"alle Werte :\", anzahl_alle)\n",
    "print(\"echte Werte:\", anzahl_echt)\n",
    "print(\"unechte Werte:\", anzahl_unecht)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "610151ed",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** alle Werte **20**, echte Werte **17**, unechte Werte **3**.\n",
    "\n",
    "So läuft die Schleife ab (die ersten drei Durchläufe):\n",
    "\n",
    "| Durchlauf | `wert` | `anzahl_alle` danach | `anzahl_echt` danach |\n",
    "|---|---|---|---|\n",
    "| 1 | 98.62 | 1 | 1 |\n",
    "| 2 | 137.41 | 2 | 2 |\n",
    "| 3 | 103.59 | 3 | 3 |\n",
    "| … | … | … | … |\n",
    "| 5 | 0.0 | 5 | 4 (die Null zählt **nicht** mit) |\n",
    "\n",
    "Drei der 20 Werte zählen nicht als echte Bestellung (sie landen im `else`-Zweig): zwei **Nullen** (Testbestellungen) und ein **negativer Wert**\n",
    "(`-129.90`, eine Rücknahme). Je nachdem, welchen Zähler man nimmt, kommt **20 oder 17** heraus. Dieselben Daten, zwei\n",
    "Antworten: genau der Streit zwischen Vertrieb und Controlling, nur im Kleinen.\n",
    "\n",
    "Das funktioniert, aber stellt euch vor, ihr tippt das für **1.616** Zeilen. Genau da übernimmt `pandas`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd0c15b8",
   "metadata": {},
   "source": [
    "### Jetzt ihr: eine eigene Schleife\n",
    "\n",
    "**Mini-Aufgabe (ca. 5 Minuten).** Bisher habt ihr eine fertige Schleife gelesen. Schreibt jetzt selbst eine: Zählt mit der Liste\n",
    "`bestellwerte` von oben, wie viele Bestellungen **100 € oder mehr** wert sind (`anzahl_gross`) und wie viele **weniger als 100 €**\n",
    "(`anzahl_klein`). Ihr braucht zwei Zähler, eine `for`-Schleife und im Block ein `if` mit `else`. Gebt am Ende beide Zähler aus.\n",
    "Die Zelle oben ist eure Vorlage: Ihr könnt sie kopieren und ändern.\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 1</b></summary>\n",
    "\n",
    "Setzt beide Zähler **vor** der Schleife auf 0. In der Schleife prüft ihr bei jedem `wert`, ob er die Grenze erreicht. Im `if`-Zweig zählt\n",
    "ihr einen Zähler hoch, im `else`-Zweig den anderen.\n",
    "\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 2</b></summary>\n",
    "\n",
    "Gerüst (ersetzt die Punkte durch eure Zeilen):\n",
    "\n",
    "```python\n",
    "anzahl_gross = 0\n",
    "anzahl_klein = 0\n",
    "for wert in bestellwerte:\n",
    "    if wert >= 100:\n",
    "        ...\n",
    "    else:\n",
    "        ...\n",
    "print(\"100 € oder mehr:\", anzahl_gross)\n",
    "print(\"weniger als 100 €:\", anzahl_klein)\n",
    "```\n",
    "\n",
    "**Kontrollzahlen:** **9** Werte mit 100 € oder mehr und **11** mit weniger als 100 €. Zusammen sind es 20, wie oben.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "afc0112e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Mini-Aufgabe: eigene Schleife\n",
    "\n",
    ""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1bedd25",
   "metadata": {},
   "source": [
    "## Teil 2 — Dieselbe Frage in *einer* Zeile (der Werkzeugwechsel)\n",
    "\n",
    "`len(df)` liefert die **Anzahl der Zeilen**. `df[\"bestellnr\"].nunique()` liefert die Anzahl der\n",
    "**verschiedenen** Bestellnummern. Die erste Zahl zählt der Vertrieb, die zweite zählt, wie viele echte\n",
    "Bestellungen es nur *gibt*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0bff110a",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Zeilen gesamt      :\", len(df))                       # so zählt der Vertrieb: jede Zeile\n",
    "print(\"eindeutige Nummern :\", df[\"bestellnr\"].nunique())      # wie viele verschiedene Bestellnummern gibt es?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1fe84f1",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** **1616** Zeilen, aber nur **1608** *verschiedene* Bestellnummern. Also kommen **8** Nummern doppelt\n",
    "vor. Das ist der erste Teil der Differenz. Die Gesamtdifferenz ist 1616 − 1594 = **22** Bestellungen. Auf die Jagd nach\n",
    "den übrigen 14."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "91cf6c38",
   "metadata": {},
   "source": [
    "## Teil 3 — Die Differenz Zeile für Zeile lokalisieren\n",
    "\n",
    "Wir suchen die 22 Bestellungen mit vier Werkzeugen. Jedes Werkzeug findet eine andere Art von Fehler.\n",
    "\n",
    "### 3a — Doppelerfassungen (`duplicated`)\n",
    "\n",
    "`df.duplicated(subset=\"bestellnr\")` prüft für **jede Zeile**, ob ihre Bestellnummer weiter oben schon einmal vorkam, und\n",
    "liefert eine Spalte aus `True`/`False` (Wahrheitswerte, wie in Vorlesung 3). Mit `.sum()` zählt man die `True`. Mit\n",
    "`keep=False` markiert man **beide** Seiten eines Duplikats, das Original und die Kopie."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4c25aa9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# True = diese Bestellnummer kam weiter oben schon einmal vor\n",
    "anzahl_doppelt = df.duplicated(subset=\"bestellnr\").sum()   # True zählt als 1, False als 0\n",
    "print(\"doppelte Zeilen:\", anzahl_doppelt)\n",
    "\n",
    "# Original UND Kopie ansehen (keep=False), 16 Zeilen (die Paare stehen nicht untereinander):\n",
    "df[df.duplicated(subset=\"bestellnr\", keep=False)]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "479a7ace",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** **8** doppelte Zeilen. In der Tabelle stehen die Bestellungen `8050` und `8250` je zweimal (sucht die beiden Paare in den 16 Zeilen), mit\n",
    "gleicher Kundennummer, gleichem Wert, aber **einem Tag Unterschied im `bestelldatum`**. Dieselbe Bestellung wurde einen Tag\n",
    "später **erneut erfasst**. Das ist ein klassischer Fehler beim Wechsel zwischen Systemen.\n",
    "\n",
    "### 3b — Testbestellungen (`bestellwert == 0`)\n",
    "\n",
    "Eine Bestellung über **0 €** ist kein echter Verkauf, sondern ein Test. Mit `value_counts()` zählt man, wie oft jeder\n",
    "Wert einer Spalte vorkommt. Wir schauen, auf welchem Kundenkonto die Tests laufen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eca48c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "tests = df[df[\"bestellwert\"] == 0]        # Filter wie in Woche 2: nur Zeilen mit Bestellwert 0\n",
    "print(\"Testbestellungen:\", len(tests))\n",
    "\n",
    "tests[\"kundennr\"].value_counts()          # wie oft kommt jede Kundennummer unter den Tests vor?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "faaeebb5",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** **9** Testbestellungen, alle auf der Kundennummer **9999**. Das ist ein auffälliges **Testkonto**, das es\n",
    "im Kundenstamm gar nicht gibt.\n",
    "\n",
    "### 3c — Stornos (`describe` zeigt das Minimum)\n",
    "\n",
    "`describe()` fasst eine Zahlenspalte zusammen: Anzahl, Mittelwert, kleinster und größter Wert. Achtet auf `min`, den\n",
    "**kleinsten** Wert."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "43b9e44d",
   "metadata": {},
   "outputs": [],
   "source": [
    "df[\"bestellwert\"].describe()              # Zusammenfassung der Spalte bestellwert"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9f753c4",
   "metadata": {},
   "source": [
    "Das Minimum ist **negativ** (`-129.9`). Einen negativen Bestellwert gibt es bei einem echten Verkauf nicht, das ist eine\n",
    "**Rücknahme** (Storno). Welche Zeile ist das?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "688cbf29",
   "metadata": {},
   "outputs": [],
   "source": [
    "df[df[\"bestellwert\"] < 0]                 # alle Zeilen mit negativem Bestellwert"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9dcdde63",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** genau **1** Zeile, Bestellnummer `9604`, Bestellwert `-129.9`.\n",
    "\n",
    "### 3d — Fehlende Werte (`isna`) und eine Falle\n",
    "\n",
    "`df.isna()` prüft jedes Feld auf „leer“ und liefert wieder `True`/`False`. Mit `.sum()` zählt man die leeren Felder je Spalte."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4d751b8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "df.isna().sum()                           # leere Felder je Spalte zählen"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50d81ed0",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** Nur die Spalte `code` hat leere Felder (**1348**). Alle anderen Spalten sind vollständig.\n",
    "\n",
    "**Vorsicht, eine Falle:** Die Spalte `code` ist oft leer, das ist aber **kein Fehler**, sondern bedeutet *keine Kampagne*.\n",
    "Die für uns wichtigen Spalten (`bestellnr`, `kundennr`, `bestellwert`) sind vollständig. **Leer ist nicht dasselbe wie falsch.**\n",
    "\n",
    "### 3e — Verwaiste Kundennummern (Blick voraus auf Woche 4)\n",
    "\n",
    "Zu welchen Bestellungen gibt es gar keine Kundin im Kundenstamm? `df[\"kundennr\"].isin(kunden[\"kundennr\"])` prüft für jede\n",
    "Zeile, ob ihre Kundennummer im Kundenstamm **vorkommt** (`True`/`False`). Das Zeichen `~` **kehrt** das um: `~` heißt „nicht“.\n",
    "So bleiben die Zeilen übrig, deren Kundennummer **nicht** vorkommt, die „verwaisten“."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "76112b98",
   "metadata": {},
   "outputs": [],
   "source": [
    "# isin: steht die Kundennummer im Kundenstamm?  ~ kehrt um -> True = \"unbekannte\" Kundennummer\n",
    "verwaist = ~df[\"kundennr\"].isin(kunden[\"kundennr\"])\n",
    "print(\"Zeilen mit unbekannter Kundennummer:\", int(verwaist.sum()))\n",
    "\n",
    "df.loc[verwaist, \"kundennr\"].value_counts()   # welche Nummern sind das, und wie oft?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3877845",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** **13** Zeilen. Davon sind **9** das Testkonto `9999` (kennen wir schon aus 3b) und **4** weitere echte\n",
    "Waisen. Nach dem Entfernen der Tests bleiben also genau diese 4."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4614f57",
   "metadata": {},
   "source": [
    "## Teil 4 — Bereinigen und die Zahl festlegen\n",
    "\n",
    "Jetzt setzen wir die Schritte nacheinander zusammen. Jeder Schritt behält nur die Zeilen, die er **nicht** entfernen will.\n",
    "Zur Kontrolle steht nach jedem Schritt die Zeilenzahl."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "210faa69",
   "metadata": {},
   "outputs": [],
   "source": [
    "schritt0 = len(df)                                      # Rohzeilen: so zählt der Vertrieb\n",
    "df1 = df.drop_duplicates(subset=\"bestellnr\")            # 3a: nur die erste Zeile je Bestellnummer behalten\n",
    "df2 = df1[df1[\"bestellwert\"] > 0]                       # 3b + 3c: Tests (0) und Storno (< 0) entfernen\n",
    "df3 = df2[df2[\"kundennr\"].isin(kunden[\"kundennr\"])]     # 3e: Zeilen mit unbekannter Kundennummer entfernen\n",
    "\n",
    "print(\"Rohzeilen (Vertrieb)      :\", schritt0)\n",
    "print(\"nach Entdopplung          :\", len(df1))\n",
    "print(\"nach Wert > 0             :\", len(df2))\n",
    "print(\"nach gültiger Kundennummer:\", len(df3), \"  <- Controlling\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8a14f22",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** 1616 → 1608 → 1598 → **1594**. Damit ist die Differenz vollständig erklärt:\n",
    "\n",
    "| Schritt | entfernt | verbleibend |\n",
    "|---|---|---|\n",
    "| Rohzeilen | — | **1.616** |\n",
    "| Doppelerfassungen | 8 | 1.608 |\n",
    "| Testbestellungen + Storno | 10 | 1.598 |\n",
    "| verwaiste Kundennummern | 4 | **1.594** |\n",
    "\n",
    "**Beide hatten recht.** Der Vertrieb zählte *jede Zeile*, das Controlling nur *gültige, eindeutige* Bestellungen.\n",
    "\n",
    "Und hier steckt die Antwort der Woche: **Doppelte Zeilen und verwaiste Verweise sind Fehler**, die man immer entfernt. Ob\n",
    "**Testbestellungen** und **Stornos** mitzählen, ist dagegen eine **Definitionsfrage**, die jemand entscheiden muss.\n",
    "*Eine Zahl ohne ihre Definition ist wertlos.*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "854cda61",
   "metadata": {},
   "source": [
    "## Teil 5 — Lernwerkstattaufgaben (selbstständig)\n",
    "\n",
    "Drei Aufgaben. Nehmt euch Zeit und nutzt die Hinweise, wenn ihr nicht weiterkommt."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2e5ca61",
   "metadata": {},
   "source": [
    "**Aufgabe 1.** Formuliert die Definition **„Wir zählen als Bestellung: …“** in eigenen Worten und begründet in einem Satz,\n",
    "warum Testbestellungen nicht zählen. Schreibt eure Antwort als Kommentar (Zeilen mit `#`) in die Zelle darunter.\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 1</b></summary>\n",
    "\n",
    "Welche **vier Arten** von Zeilen haben wir in Teil 3 aus den 1.616 Zeilen entfernt? Zählt sie auf (Doppelerfassungen, …).\n",
    "\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 2</b></summary>\n",
    "\n",
    "Dreht die Liste um: Eine Zeile zählt als Bestellung, wenn ihre Bestellnummer **eindeutig** ist, ihr Bestellwert ... und ihre Kundennummer ...\n",
    "\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 3</b></summary>\n",
    "\n",
    "Der Satz fängt so an: *Wir zählen als Bestellung: jede Zeile mit eindeutiger Bestellnummer, ...* Zur Begründung: Was würde passieren, wenn Testbestellungen mitzählen? Denkt an Umsatz und Bestellzahl.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ea299a1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Aufgabe 1 (als Kommentar):\n",
    "# Wir zählen als Bestellung: \n",
    "\n",
    ""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fbfd3c26",
   "metadata": {},
   "source": [
    "**Aufgabe 2.** Angenommen, das Controlling würde **Stornos mitzählen** (es entfernt nur Doppelerfassungen, Testbestellungen\n",
    "und verwaiste Kundennummern). Wie viele Bestellungen wären es dann? Rechnet nach und seht, wie die *Definition* die Zahl verändert.\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 1</b></summary>\n",
    "\n",
    "Baut es wie in Teil 4 auf, mit **drei** Schritten. Nur ein Schritt ändert sich gegenüber Teil 4: Dort haben wir mit `> 0` Tests *und* Storno entfernt. Jetzt soll nur noch der Test (Wert **0**) raus, der Storno (Wert unter 0) bleibt.\n",
    "\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 2</b></summary>\n",
    "\n",
    "Statt `df1[df1[\"bestellwert\"] > 0]` braucht ihr eine Bedingung, die nur die Zeilen mit Wert **0** entfernt, also alle Zeilen behält, deren Bestellwert **ungleich 0** ist. Ungleich schreibt man in Python `!=`.\n",
    "\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 3</b></summary>\n",
    "\n",
    "Gerüst: `df1 = df.drop_duplicates(subset=\"bestellnr\")`, dann `df2b = df1[df1[\"bestellwert\"] != 0]`, dann `df3b = df2b[df2b[\"kundennr\"].isin(kunden[\"kundennr\"])]`. Gebt `len(df3b)` aus.\n",
    "\n",
    "**Kontrollzahl:** Es sind **1595**, also genau eine mehr als 1594: der eine Storno.\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d5ff8a48",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Aufgabe 2:\n",
    "\n",
    ""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6b39251c",
   "metadata": {},
   "source": [
    "**Aufgabe 3.** Brick-Flow will **Treuepunkte** vergeben: **2 Punkte** für jede Bestellung ab **100 €**, **1 Punkt** für jede kleinere.\n",
    "Schreibt eine **Funktion** `treuepunkte(werte)`, die über eine Liste von Bestellwerten läuft und die **Gesamtpunkte** zurückgibt.\n",
    "Prüft sie mit zwei Aufrufen: `treuepunkte([50, 120, 99.99, 100])` und `treuepunkte(bestellwerte)`.\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 1</b></summary>\n",
    "\n",
    "Setzt zusammen, was ihr kennt: Eine Funktion mit `def` und `return` kennt ihr aus Woche 2, die Schleife mit `if` und `else` aus der Mini-Aufgabe.\n",
    "Statt zweier Zähler braucht ihr hier **einen** Zähler `punkte`, der im `if`-Zweig um 2 und im `else`-Zweig um 1 wächst.\n",
    "\n",
    "</details>\n",
    "\n",
    "<details>\n",
    "<summary><b>Hinweis 2</b></summary>\n",
    "\n",
    "Gerüst (ersetzt die Punkte durch eure Zeilen):\n",
    "\n",
    "```python\n",
    "def treuepunkte(werte):\n",
    "    punkte = 0\n",
    "    for wert in werte:\n",
    "        if wert >= 100:\n",
    "            ...\n",
    "        else:\n",
    "            ...\n",
    "    return punkte\n",
    "```\n",
    "\n",
    "Das `return` steht **nach** der Schleife, also so weit links wie das `for`.\n",
    "\n",
    "**Kontrollzahlen:** `treuepunkte([50, 120, 99.99, 100])` ergibt **6** (1 + 2 + 1 + 2), `treuepunkte(bestellwerte)` ergibt **29**\n",
    "(9 Bestellungen ab 100 € zählen doppelt, 11 einfach).\n",
    "\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b5bf6f8f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Aufgabe 3:\n",
    "\n",
    ""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e7eee91",
   "metadata": {},
   "source": [
    "## Geschafft — Meilenstein 1 und 2 abgeschlossen 🎉\n",
    "\n",
    "Ihr habt heute\n",
    "\n",
    "- mit einer **Schleife** von Hand gezählt, auch mit einer Bedingung in der Schleife (**M1**),\n",
    "- selbst eine **Schleife** mit `if` und `else` und eine **Funktion mit Schleife** geschrieben (**M1**),\n",
    "- gesehen, warum man das für große Tabellen nicht tippt, und mit `len` und `nunique` den **Werkzeugwechsel zu `pandas`** vollzogen,\n",
    "- mit `duplicated`, `value_counts`, `describe` und `isna` einen echten, verschmutzten Datensatz **geprüft** (**M2**),\n",
    "- 1.616 auf 1.594 **bereinigt** und aufgeschrieben, **was als Bestellung zählt**.\n",
    "\n",
    "**Nächste Woche (Vorlesung am 2. November):** Eine Kundin heiratet und zieht um: Was ändert sich wo? Wir verbinden Tabellen\n",
    "(`merge` / JOIN). Legt dafür vorab `brickflow_kunden_dubletten.csv`, `brickflow_bestellungen.csv` und `brickflow_marketing.csv` aus dem\n",
    "Kursordner neben das Notebook der nächsten Woche."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2630cd8",
   "metadata": {},
   "source": [
    "## Vertiefung (optional): Tupel — mehrere Werte auf einmal zurückgeben\n",
    "\n",
    "Eine **Liste** (wie `bestellwerte` oben) bündelt beliebig viele *gleichartige* Werte. Manchmal soll eine Funktion aber genau\n",
    "**zwei oder drei zusammengehörige** Werte auf einmal zurückgeben. Dafür ist ein **Tupel** die übliche Form:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "330e7af3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def spanne(werte):\n",
    "    kleinster = min(werte)               # kleinster Wert der Liste\n",
    "    groesster = max(werte)               # größter Wert der Liste\n",
    "    ergebnis = (kleinster, groesster)    # zwei Werte in runden Klammern -> ein Tupel\n",
    "    return ergebnis\n",
    "\n",
    "ergebnis = spanne(bestellwerte)\n",
    "print(ergebnis)\n",
    "print(type(ergebnis))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4bf27f8",
   "metadata": {},
   "source": [
    "Ein Tupel bündelt beide Werte zu `(kleinster, groesster)`. Meist will man die beiden Teile aber gleich wieder einzeln\n",
    "haben, das nennt man **Tupel entpacken**:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f9048051",
   "metadata": {},
   "outputs": [],
   "source": [
    "kleinster, groesster = spanne(bestellwerte)   # Tupel entpacken: je ein Name pro Wert\n",
    "print(\"kleinster Bestellwert:\", kleinster)\n",
    "print(\"größter Bestellwert:  \", groesster)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9975b22",
   "metadata": {},
   "source": [
    "**Kontrollzahlen:** `(-129.9, 210.0)`, Typ `tuple`, dann kleinster `-129.9` und größter `210.0`.\n",
    "\n",
    "Der Unterschied zur Liste ist die **Bedeutung der Position**: An Stelle 1 steht immer das Minimum, an Stelle 2 immer das Maximum.\n",
    "Bei einer Liste wie `bestellwerte` hat dagegen jede Position dieselbe Bedeutung („ein Bestellwert“)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9fe22a93",
   "metadata": {},
   "source": [
    "## Anhang (freiwillig): Selbsttest und Wiederholung\n",
    "\n",
    "### Selbsttest Meilenstein 1 und 2\n",
    "\n",
    "Hakt ehrlich ab. Bei einem Punkt, den ihr nicht sicher könnt, lest den genannten Teil noch einmal.\n",
    "\n",
    "- [ ] Ich kann eine `for`-Schleife mit Zähler und `if`/`else` selbst schreiben (Teil 1, Mini-Aufgabe).\n",
    "- [ ] Ich kann eine Funktion schreiben, die über eine Liste läuft und ein Ergebnis zurückgibt (Teil 5, Aufgabe 3).\n",
    "- [ ] Ich kann mit `len` und `nunique` zählen und den Unterschied erklären (Teil 2).\n",
    "- [ ] Ich kann Duplikate mit `duplicated`, Tests mit einem Filter, Ausreißer mit `describe` und leere Felder mit `isna` finden (Teil 3).\n",
    "- [ ] Ich kann erklären, warum 1.616 und 1.594 beide „richtig“ sind (Teil 4).\n",
    "- [ ] Ich weiß, welche Fehler man immer entfernt und welche Entscheidungen eine Definitionsfrage sind (Teil 4).\n",
    "\n",
    "### Wiederholung aus Woche 1 und 2 (ca. 15 Minuten)\n",
    "\n",
    "**Wiederholung 1 — Fehler finden.** Die Regel: *Weiß und Schwarz sind Premiumfarben (+0,02 € je Stein), alle anderen Farben haben\n",
    "keinen Aufschlag.* Passt diese Funktion zur Regel?\n",
    "\n",
    "```python\n",
    "def farbaufschlag(farbe):\n",
    "    if farbe == \"weiss\":\n",
    "        aufschlag = 0.02\n",
    "    else:\n",
    "        aufschlag = 0.0\n",
    "    return aufschlag\n",
    "```\n",
    "\n",
    "Für welche Farbe liefert die Funktion einen falschen Aufschlag, und wie muss die Zeile mit `if` richtig heißen?\n",
    "\n",
    "<details>\n",
    "<summary><b>Lösung</b></summary>\n",
    "\n",
    "Für **schwarz**: Die Funktion liefert `0.0`, richtig wären `0.02`. Es gibt keine Fehlermeldung, die Funktion läuft für jede Farbe\n",
    "durch, nur das Ergebnis für „schwarz“ ist falsch. Richtig wäre z. B. `if farbe == \"weiss\" or farbe == \"schwarz\":`.\n",
    "\n",
    "</details>\n",
    "\n",
    "**Wiederholung 2 — Fehler vorhersagen.** Läuft dieser Code durch, oder gibt es einen Fehler? Wenn Fehler: welche Art, und warum?\n",
    "\n",
    "```python\n",
    "anzahl_pakete = \"12\"\n",
    "zusatzpakete = 3\n",
    "gesamt = anzahl_pakete + zusatzpakete\n",
    "print(gesamt)\n",
    "```\n",
    "\n",
    "<details>\n",
    "<summary><b>Lösung</b></summary>\n",
    "\n",
    "Es gibt einen **`TypeError`**: `can only concatenate str (not \"int\") to str`. `\"12\"` steht in Anführungszeichen, ist also **Text**,\n",
    "und Text und Zahl lassen sich nicht mit `+` verbinden. Behebung: `int(anzahl_pakete) + zusatzpakete` ergibt 15.\n",
    "\n",
    "</details>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
