# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# ## 0. Importe

# + cell_id="00002-c2aabb42-89b1-4a7a-bf70-652f9b2c64c6" deepnote_cell_type="code" deepnote_to_be_reexecuted=false execution_millis=0 execution_start=1611833332447 source_hash="aa3ac375" tags=[]
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# TODO: Importiere Decision Tree aus Scikit-Learn
# TODO: Importiere Random Forest aus Scikit-Learn
# TODO: Importiere Gradient Boosting aus Scikit-Learn
# TODO: Importiere Evaluationsmetriken
# TODO: Visualisierungstools

# TODO: Importieren von `train_test_split` aus Scikit-Learn

import utils_viz

# + [markdown] cell_id="00001-a5b98850-8886-487f-8840-383b29627dab" deepnote_cell_type="markdown" tags=[]
# ## 1. Decision Trees
#
# Decision Trees sind überwachte Lernmodelle, die sich sowohl für Klassifikationsprobleme (die Zielvariable ist ein diskreter/kategorieller Wert) als auch für Regressionsprobleme (die Zielvariable ist ein metrischer Wert) eignen.
#
# Wir benutzen für dieses Tutorial die in Scikit-Learn implementierten Klassen für Decision Trees: `DecisionTreeClassifier` und `DecisionTreeRegressor`. Achten Sie in jedem Fall darauf, die für das Problem passende Klasse zu verwenden.
#
# Nach dem Import der Klassen reicht ein Blick in den Docstring der Klassen, das heißt durch
#
# ```python
# >>> DecisionTreeClassifier?
# ```
#
# um zu erkennen, dass bei der Initialisierung des Decision Trees viele Argumente übergeben werden können. Wir beschränken uns hier nur auf das wichtigste davon, nämlich `max_depth`.
#
# Weitere relevante Funktionen und Attribute sind
# - `fit`
# - `predict`
#
# sowie fortgeschrittene Attribute:
# - `predict_proba`
# - `feature_importances_` (erst nach dem Fit verfügbar)

# + [markdown] cell_id="00002-d10ef959-11d0-4d88-9674-5a5f91219927" deepnote_cell_type="markdown" tags=[]
# ### 1.1 Decision Trees für Klassifikation
#
# Zunächst beschäftigen wir uns mit dem Einlesen und Vorbereiten eines Toy-Datensatzes. Der Datensatz soll den Zusammenhang zwischen zwei Features, die die Genaktivität zweier Gene einer nicht genannten Spezies (Beispiel: Mäuse) und dem Phänotyp dieser Spezies darstellen. Es gibt 4 verschiedene Phänotypen (A, B, C, D) - es handelt sich also um ein Klassifikationsproblem. Decision Trees können auf natürliche Weise solche Multi-Class-Klassifikationen handhaben (tatsächlich können das alle Modelle in Scikit-Learn, aber nur weil intern dazu eine gewisse zusätzliche Logik - genannt One-vs-All - verbaut ist. Decision Trees handhaben Multi-Class ganz selbstständig.)
#
# Wir müssen
#
# - die Daten einlesen
# - die Phänotypen A, B, C, D in numerische Werte übersetzen
# - die Daten in Trainings- und Testdaten aufspalten
# - die Daten in einem Scatterplot visualisieren

# + cell_id="00003-121ee766-fc8f-4981-b4b0-05f1dc7e464b" deepnote_cell_type="code" deepnote_to_be_reexecuted=false execution_millis=0 execution_start=1611833669943 source_hash="50005f79" tags=[]
# TODO: Einlesen der Daten `toy_gene_data.csv`
# TODO: Aufspalten der Daten in Trainings- und Testdaten
# TODO: Daten verarbeiten
# TODO: Visualisieren der Daten
# -

# Nun fitten wir das Modell auf die übliche Weise und evaluieren. Wir nutzen die **Genauigkeit** bzw. *Korrekte-Klassifikations-Rate* als Metrik zum Evaluieren, da wir es mit einem Klassifikationsproblem zu tun haben. Wir können dafür eine Funktion schreiben oder die Funktion `accuracy_score` aus Scikit-Learn importieren.
#
# Dabei sollten wir auch das Argument `max_depth` variieren und verstehen, welchen Einfluss dieses auf den Trainingsfehler bzw. Testfehler hat (Overfitting versus Underfitting?).
#
# Beim Training des Decision Trees ist folgendes relevant:
# - der Decision Tree versucht beim Aufspalten der Daten in jedem Knoten den **Gini-Koeffizienten** (alternativ die **Entropie**) in den nachfolgenden Knoten zu minimieren.
# - die Vorhersage in den Blättern ist die häufigste Klasse unter den Trainingsdatenpunkten, die in diesem Blatt gelandet sind.
# - das Aufspalten in jedem Knoten ist *greedy*, das heißt es wird so gespalten, wie es zu jedem Zeitpunkt am sinnvollsten erscheint, ohne auf den weiteren Verlauf des Baums zu achten.
# - die Größe des Baums ist vor allem von `max_depth` abhängig. Daneben gibt es andere Abbruchkriterien für das Wachsen des Baums, die durch weitere Argumente bei der Initialisierung eingestellt werden können. In jedem endet das Wachstum, wenn in einem Blatt alle Trainingsdatenpunkte derselben Klasse angehören.
#
# Weiterhin gibt es die Möglichkeit, die Entscheidungen des trainierten Baums zu visualisieren. Dazu existiert eine Funktion `plot_tree` in Scikit-Learn. Als zusätzliche Visualisierung werden wir auch die Entscheidungsgrenzen des Modells veranschaulichen, was wir dank der Benutzung von nur zwei Features problemlos können.

# + cell_id="00003-121ee766-fc8f-4981-b4b0-05f1dc7e464b" deepnote_cell_type="code" deepnote_to_be_reexecuted=false execution_millis=0 execution_start=1611833669943 source_hash="50005f79" tags=[]
# TODO: Modell trainieren
# TODO: Modell evaluieren
# TODO: Modell visualisieren

# + [markdown] cell_id="00004-821fcffe-7ba0-42c1-89fb-041f868d3ad8" deepnote_cell_type="markdown" tags=[]
# ### 1.2 Decision Trees für Regressionen
#
# Durch leichte Änderungen das Algorithmus können Decision Trees auch für Regressionsprobleme verwendet werden.
# Wir untersuchen ein Regressionsproblem, das nur auf einem Feature beruht, damit für wie gehabt den Fit in einem Graphen visualisieren können. Wir entscheiden uns für den Toy-Automobile Datensatz.
#
# Folgende Unterschiede sind bei einem Decision Tree für Regressionen im Vergleich zu einem für Klassifikationen zu beachten:
# - der Regressions-Decision Tree versucht nicht den **Gini-Koeffizienten** (oder alternativ die **Entropie**) der Knoten zu minimieren, sondern minimiert den **Mean-Squared-Error** der Knoten.
# - die Vorhersage an einem Blatt ist nicht die häufigste Klasse, sondern der durchschnittliche Werte der Zielvariablen aller Trainingsdatenpunkte in diesem Blatt
# - das Abbruchkriterium für das Wachsen des Baums ist wieder von `max_depth` sowie von weiteren Argumenten abhängig. Außerdem endet das Wachsum in jedem Fall, wenn der **Mean-Squared-Error** in einem Knoten auf Null sinkt, wenn also alle dortigen Trainingsdatenpunkte denselben Wert der Zielvariablen haben.

# + cell_id="00012-3f83ba20-a482-4199-9fbd-ca21a34b476e" deepnote_cell_type="code" tags=[]
# TODO: Einlesen der Daten `toy_auto_data_A.csv` und `toy_auto_data_test.csv`
# TODO: Visualisieren der Daten
# TODO: Modell trainieren
# TODO: Modell evaluieren
# TODO: Modell visualisieren

# + [markdown] cell_id="00006-bfdb05a2-4c15-4729-8f6e-dd823d991d5e" deepnote_cell_type="markdown" tags=[]
# ## 2. Random Forests
#
# Random Forests lösen das wesentliche Problem von Decision Trees: die Tendenz, extremes Overfitting zu betreiben. Dazu besteht der Random Forest aus einer Menge - einem *Ensemble* - von Decision Trees, die sich alle leicht voneinander unterscheiden. Der Vorgang wird auch **Bagging** genannt. Die Decision Trees im *Ensemble* unterscheiden sich dadurch, dass sie jeweils einen leicht anderen Teil der Trainingsdaten kennengelernt haben.
#
# Diese Randomisierung der Trainingsdaten kann auf verschiedene Weise erfolgen:
# - zufällige Auswahl von 50-80% der Trainingsdaten für jeden Baum. Überschneidungen zwischen den einzelnen Randomisierungen sind natürlich möglich.
# - zufälliges Ziehen-mit-Zurücklegen der Trainingdatenpunkte. Dies wird auch **Bootstrapping** genannt. Es wird einfach wiederholt ein Trainingsdatenpunkt aus den gesamten Trainingsdaten gezogen, dann aber wieder "zurückgelegt", sodass ein Trainingsdatenpunkt in dem neuen, randomisierten Trainingsdatensatz auch mehrmals vorkommen kann.
# - Kombinationen aus den beiden vorher genannten Strategien
#
# Zusätzlich kann man für jeden randomisierten Trainingsdatensatz auch eine zufällige Auswahl der Features vornehmen.
#
# Die Argumente bei der Initialisierung des Random Forest erlauben es, alle diese Randomisierungen selbstständig einzustellen. Im einfachsten Fall sollte man es allerdings bei den Default-Werten belassen.
#
# Die wichtigsten Argumente für uns sind deshalb
# - `max_depth`
# - `n_estimators`
#
# Typische Größen für `n_estimators` sind 100, 200, 500 oder maximal 1000. Danach haben zusätzliche Bäume meist keinen Effekt mehr. `max_depth` muss als Hyperparameter manchmal getunt werden.
#
# Zusätzlich brauchen wir
# - `fit`
# - `predict`
#
# sowie eventuell fortgeschrittene Attribute:
# - `predict_proba`
# - `feature_importances_` (erst nach dem Fit verfügbar)
#
# Wichtig ist, dass wir von dem Training der einzelnen Decision Trees nichts mitbekommen, da dies im Aufruf der `fit` und `predict` Funktionen des Random Forest intern geschieht.

# + [markdown] cell_id="00007-05e51525-0c32-471e-b441-445d3447c43d" deepnote_cell_type="markdown" tags=[]
# ### 2.1 Random Forests für Klassifikationen
#
# Wir vergleichen das Ergebnis des Fits zu dem eines einzelnen Decision Trees und versuchen zu verstehen, wie der Random Forest gegen Overfitting arbeitet.

# + cell_id="00015-04e0261e-8fb8-4529-800d-6f9747569640" deepnote_cell_type="code" deepnote_to_be_reexecuted=false execution_millis=169 execution_start=1611832455113 source_hash="8bb024a1" tags=[]
# TODO: Einlesen der Daten `toy_gene_data.csv`
# TODO: Aufspalten der Daten in Trainings- und Testdaten
# TODO: Daten verarbeiten
# TODO: Visualisieren der Daten
# TODO: Modell trainieren
# TODO: Modell evaluieren
# TODO: Modell visualisieren

# + [markdown] cell_id="00008-a8469e89-4ecd-489a-9923-8eeeb7aee0cd" deepnote_cell_type="markdown" tags=[]
# ### 2.2. Random für Regressionen
#
# Auch für Regressionsprobleme eignet sich ein Random Forest. Wir vergleichen den Fit auch hier mit dem eines einzelnen Decision Trees.

# +
# TODO: Einlesen der Daten `toy_auto_data_A.csv` und `toy_auto_data_test.csv`
# TODO: Visualisieren der Daten
# TODO: Modell trainieren
# TODO: Modell evaluieren
# TODO: Modell visualisieren
# -

# ## 3. Gradient Boosting
#
# Gradient Boosting ist die zweite der Erweiterungen des Decision Trees. Sie beruht auf der Idee des **Boostings**: die Kombination vieler einfacher Modelle - auch genannt *weak learners* - zu einem starken Modell. Dies geschieht über die sequentielle Fehlerkorrektur. Decision Trees eignen sich sehr gut als *weak learners* weil sie schnell zu trainieren sind. Typischerweise hat jeder Decision Tree im **Boosting** eine sehr kleine Tiefe von 3-5. Manchmal sogar nur die Tiefe 1.
#
# Das Gradient Boosting hat ingesamt drei sehr wichtige und sensitive Hyperparameter:
# - `n_estimators`
# - `max_depth`
# - `learning_rate`
#
# Eventuell müssen alle diese Hyperparamter getunt werden.
#
# Den Effekt der Hyperparameter kann mit in folgender interaktiver Graphik verstehen lernen:

# +
# %matplotlib widget
import utils_boosting

X_train, y_train = utils_boosting.generate_data(n_samples=50, random_state=2)
X_test, y_test = utils_boosting.generate_data(n_samples=200, random_state=5)

interactive_plot, ui = utils_boosting.get_interactive_boosting(
    X_train, y_train, X_test, y_test, max_depth=3)
display(interactive_plot, ui)
# -

# ### 3.1. Gradient Boosting für Regressionen



# ### 3.2. Gradient Boosting für Klassifikationen



StarcoderdataJupyterScriptsDedupFiltered