intial commit (forked from private repo)
BIN
Aufgabenstellung.pdf
Normal file
2366
Explainable_AI_Adult_Census_Income.ipynb
Normal file
260
README.md
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
# Explainable AI Projektaufgabe - Adult Census Income Datensatz
|
||||||
|
|
||||||
|
Dieses Repository enthält eine Projektaufgabe zum Thema Explainable AI (XAI), die anhand des Adult Census Income Datensatzes demonstriert wird. Das Projekt verwendet einen Random Forest Classifier, um Einkommensvorhersagen zu treffen, und verschiedene Methoden zur Erklärung der Modellentscheidungen, darunter Feature Importance, LIME (Local Interpretable Model-agnostic Explanations) und regelbasierte Erklärungen mit dem RIPPER-Algorithmus.
|
||||||
|
|
||||||
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
|
1. [Projektübersicht](#projektübersicht)
|
||||||
|
2. [Datensatz](#datensatz)
|
||||||
|
3. [Notebook-Struktur](#notebook-struktur)
|
||||||
|
4. [Datenvorverarbeitung](#datenvorverarbeitung)
|
||||||
|
5. [Modelltraining](#modelltraining)
|
||||||
|
6. [Hyperparameter-Tuning](#hyperparameter-tuning)
|
||||||
|
7. [Modellbewertung](#modellbewertung)
|
||||||
|
8. [Explainable AI Methoden](#explainable-ai-methoden)
|
||||||
|
- [Feature Importance](#feature-importance)
|
||||||
|
- [LIME](#lime)
|
||||||
|
- [Regelbasierte Erklärungen (RIPPER)](#regelbasierte-erklärungen-ripper)
|
||||||
|
- [Vergleich der Methoden](#vergleich-der-methoden)
|
||||||
|
9. [Anwendung auf einzelne Beispiele](#anwendung-auf-einzelne-beispiele)
|
||||||
|
10. [Voraussetzungen und Installation](#voraussetzungen-und-installation)
|
||||||
|
11. [Fazit](#fazit)
|
||||||
|
|
||||||
|
## Projektübersicht
|
||||||
|
|
||||||
|
Dieses Projekt demonstriert die Anwendung von Explainable AI-Techniken auf ein klassisches Machine Learning-Problem: die Vorhersage, ob eine Person ein Einkommen von mehr als 50.000 Dollar pro Jahr erzielt. Während die Vorhersagegenauigkeit wichtig ist, liegt der Schwerpunkt dieses Projekts auf der Erklärbarkeit der Modellentscheidungen. Wir verwenden verschiedene Methoden (Feature Importance, LIME und regelbasierte Erklärungen), um zu verstehen, welche Faktoren das Einkommensniveau am stärksten beeinflussen.
|
||||||
|
|
||||||
|
## Datensatz
|
||||||
|
|
||||||
|
Der Adult Census Income Datensatz (auch bekannt als "Census Income" oder "Adult" Datensatz) enthält demografische Informationen aus der US-Volkszählung und wird verwendet, um vorherzusagen, ob eine Person ein Einkommen von mehr als 50.000 Dollar pro Jahr erzielt. Der Datensatz enthält folgende Attribute:
|
||||||
|
|
||||||
|
- **age**: Alter der Person
|
||||||
|
- **c**: Arbeitsklasse (Private, Self-emp-not-inc, Self-emp-inc, Federal-gov, Local-gov, State-gov, Without-pay, Never-worked)
|
||||||
|
- **fnlwgt**: Gewichtungsfaktor (repräsentiert die Anzahl der Personen mit ähnlichen demografischen Merkmalen)
|
||||||
|
- **education**: Höchster Bildungsabschluss (Bachelors, Some-college, 11th, HS-grad, Prof-school, Assoc-acdm, Assoc-voc, 9th, 7th-8th, 12th, Masters, 1st-4th, 10th, Doctorate, 5th-6th, Preschool)
|
||||||
|
- **education.num**: Numerische Darstellung des Bildungsabschlusses
|
||||||
|
- **marital.status**: Familienstand (Married-civ-spouse, Divorced, Never-married, Separated, Widowed, Married-spouse-absent, Married-AF-spouse)
|
||||||
|
- **occupation**: Beruf
|
||||||
|
- **relationship**: Beziehungsstatus (Wife, Own-child, Husband, Not-in-family, Other-relative, Unmarried)
|
||||||
|
- **race**: Ethnische Zugehörigkeit (White, Asian-Pac-Islander, Amer-Indian-Eskimo, Other, Black)
|
||||||
|
- **sex**: Geschlecht (Female, Male)
|
||||||
|
- **c**: Kapitalgewinn
|
||||||
|
- **capital.loss**: Kapitalverlust
|
||||||
|
- **hours.per.week**: Arbeitsstunden pro Woche
|
||||||
|
- **native.country**: Herkunftsland
|
||||||
|
- **income**: Zielvariable, gibt an, ob das Einkommen über 50.000 Dollar liegt (>50K) oder nicht (<=50K)
|
||||||
|
|
||||||
|
## Notebook-Struktur
|
||||||
|
|
||||||
|
Das Jupyter Notebook `Explainable_AI_Adult_Census_Income.ipynb` ist in folgende Abschnitte unterteilt:
|
||||||
|
|
||||||
|
1. **Abhängigkeiten installieren**: Installation der benötigten Python-Bibliotheken (scikit-learn, matplotlib, seaborn, pandas, numpy, lime, wittgenstein)
|
||||||
|
2. **Daten einlesen und erkunden**: Laden und erste Analyse der Daten
|
||||||
|
3. **Datenvorverarbeitung**: Behandlung fehlender Werte und Umwandlung kategorischer Variablen
|
||||||
|
4. **Daten aufteilen und Modell trainieren**: Aufteilung der Daten in Trainings- und Testsets und Training des Random Forest Modells
|
||||||
|
5. **Feature Importance**: Analyse der wichtigsten Features für die Vorhersage
|
||||||
|
6. **Hyperparameter-Tuning**: Optimierung der Modellparameter mit GridSearchCV
|
||||||
|
7. **LIME für Erklärbarkeit**: Verwendung von LIME für lokale Erklärungen
|
||||||
|
8. **Regelbasierte Erklärungen**: Verwendung des RIPPER-Algorithmus für interpretierbare Regeln
|
||||||
|
9. **Anwendung der Regeln auf einzelne Beispiele**: Demonstration, wie die Regeln auf einzelne Datenpunkte angewendet werden
|
||||||
|
10. **Vergleich zwischen LIME und regelbasierten Erklärungen**: Vergleich der verschiedenen Erklärungsmethoden
|
||||||
|
11. **Diskussion und Fazit**: Zusammenfassung der Ergebnisse und Schlussfolgerungen
|
||||||
|
|
||||||
|
## Datenvorverarbeitung
|
||||||
|
|
||||||
|
Die Datenvorverarbeitung umfasst mehrere Schritte:
|
||||||
|
|
||||||
|
1. **Behandlung fehlender Werte**:
|
||||||
|
|
||||||
|
- Identifizierung von Einträgen mit '?' als fehlende Werte
|
||||||
|
- Ersetzung fehlender Werte durch den häufigsten Wert in der jeweiligen Spalte
|
||||||
|
|
||||||
|
2. **Umwandlung kategorischer Variablen**:
|
||||||
|
- Label Encoding für die Zielvariable 'income'
|
||||||
|
- One-Hot-Encoding für alle anderen kategorischen Variablen
|
||||||
|
|
||||||
|
Diese Schritte sind wichtig, um die Daten in ein Format zu bringen, das von Machine Learning-Algorithmen verarbeitet werden kann.
|
||||||
|
|
||||||
|
## Modelltraining
|
||||||
|
|
||||||
|
Für das Modelltraining verwenden wir einen Random Forest Classifier, der folgende Vorteile bietet:
|
||||||
|
|
||||||
|
- Gute Leistung bei verschiedenen Arten von Daten
|
||||||
|
- Robustheit gegenüber Overfitting
|
||||||
|
- Fähigkeit, mit fehlenden Werten und kategorischen Variablen umzugehen
|
||||||
|
- Eingebaute Feature Importance
|
||||||
|
|
||||||
|
Der Trainingsprozess umfasst:
|
||||||
|
|
||||||
|
1. Initialisierung des Random Forest Classifiers mit 100 Entscheidungsbäumen
|
||||||
|
2. Training des Modells mit den Trainingsdaten
|
||||||
|
3. Vorhersage für die Testdaten
|
||||||
|
4. Berechnung von Metriken wie Accuracy, Precision, Recall und F1-Score
|
||||||
|
|
||||||
|
## Hyperparameter-Tuning
|
||||||
|
|
||||||
|
Um die Leistung des Modells zu optimieren, wird Hyperparameter-Tuning mit GridSearchCV durchgeführt. Folgende Parameter werden optimiert:
|
||||||
|
|
||||||
|
- **n_estimators**: Anzahl der Bäume im Random Forest (50, 100)
|
||||||
|
- **max_depth**: Maximale Tiefe der Bäume (None, 10, 20)
|
||||||
|
- **min_samples_split**: Minimale Anzahl von Samples, um einen Knoten zu teilen (2, 5)
|
||||||
|
- **min_samples_leaf**: Minimale Anzahl von Samples in einem Blatt (1, 2)
|
||||||
|
|
||||||
|
GridSearchCV testet alle möglichen Kombinationen dieser Parameter mit 3-facher Kreuzvalidierung und wählt die beste Kombination aus. Das optimierte Modell wird dann für die endgültigen Vorhersagen verwendet.
|
||||||
|
|
||||||
|
## Modellbewertung
|
||||||
|
|
||||||
|
Die Modellbewertung erfolgt anhand mehrerer Metriken:
|
||||||
|
|
||||||
|
- **Accuracy**: Anteil der korrekt klassifizierten Instanzen
|
||||||
|
- **Precision**: Anteil der als positiv klassifizierten Instanzen, die tatsächlich positiv sind
|
||||||
|
- **Recall**: Anteil der tatsächlich positiven Instanzen, die als positiv klassifiziert wurden
|
||||||
|
- **F1-Score**: Harmonisches Mittel aus Precision und Recall
|
||||||
|
|
||||||
|
Zusätzlich werden visuelle Darstellungen verwendet:
|
||||||
|
|
||||||
|
- **Confusion Matrix**: Zeigt die Anzahl der True Positives, False Positives, True Negatives und False Negatives
|
||||||
|
- **Klassifikationsbericht**: Detaillierte Metriken für jede Klasse
|
||||||
|
|
||||||
|
Diese Metriken helfen zu verstehen, wie gut das Modell funktioniert und ob es bestimmte Klassen besser vorhersagt als andere.
|
||||||
|
|
||||||
|
## Explainable AI Methoden
|
||||||
|
|
||||||
|
### Feature Importance
|
||||||
|
|
||||||
|
Die einfachste Methode zur Erklärung des Modells ist die eingebaute Feature Importance des Random Forest Classifiers. Diese zeigt, wie wichtig jedes Feature für die Vorhersage ist, basierend auf der durchschnittlichen Verringerung der Unreinheit (Impurity) über alle Bäume.
|
||||||
|
|
||||||
|
Vorteile:
|
||||||
|
|
||||||
|
- Einfach zu berechnen und zu verstehen
|
||||||
|
- Gibt einen globalen Überblick über die Wichtigkeit der Features
|
||||||
|
|
||||||
|
Nachteile:
|
||||||
|
|
||||||
|
- Zeigt nur die globale Wichtigkeit, nicht die Auswirkung auf einzelne Vorhersagen
|
||||||
|
- Berücksichtigt keine Feature-Interaktionen
|
||||||
|
|
||||||
|
### LIME
|
||||||
|
|
||||||
|
LIME (Local Interpretable Model-agnostic Explanations) ist eine Methode zur Erklärung einzelner Vorhersagen, indem ein lokales lineares Modell um die Vorhersage herum erstellt wird.
|
||||||
|
|
||||||
|
In unserem Projekt verwenden wir LIME für:
|
||||||
|
|
||||||
|
1. **Lokale Erklärungen**: Erklärung einzelner Vorhersagen durch Approximation mit einem linearen Modell
|
||||||
|
2. **Feature-Wichtigkeit**: Identifikation der wichtigsten Features für eine einzelne Vorhersage
|
||||||
|
|
||||||
|
Vorteile:
|
||||||
|
|
||||||
|
- Modellunabhängig (kann mit jedem Modell verwendet werden)
|
||||||
|
- Einfach zu verstehen (basiert auf linearen Modellen)
|
||||||
|
- Fokussiert auf lokale Erklärungen
|
||||||
|
|
||||||
|
Nachteile:
|
||||||
|
|
||||||
|
- Ergebnisse können instabil sein (abhängig von der Stichprobe)
|
||||||
|
- Keine globalen Erklärungen
|
||||||
|
|
||||||
|
### Regelbasierte Erklärungen (RIPPER)
|
||||||
|
|
||||||
|
RIPPER (Repeated Incremental Pruning to Produce Error Reduction) ist ein Algorithmus zur Extraktion von Regeln aus Daten. Er erstellt eine Reihe von Wenn-Dann-Regeln, die leicht zu interpretieren sind.
|
||||||
|
|
||||||
|
In unserem Projekt verwenden wir den RIPPER-Algorithmus (implementiert in der Wittgenstein-Bibliothek) für:
|
||||||
|
|
||||||
|
1. **Globale Erklärungen**: Extraktion von interpretierbaren Regeln aus den Daten
|
||||||
|
2. **Regelbasierte Vorhersagen**: Verwendung der Regeln für Vorhersagen
|
||||||
|
|
||||||
|
Vorteile:
|
||||||
|
|
||||||
|
- Sehr interpretierbar (Regeln sind in natürlicher Sprache)
|
||||||
|
- Bietet globale Erklärungen für das gesamte Modell
|
||||||
|
- Kann direkt für Vorhersagen verwendet werden
|
||||||
|
|
||||||
|
Nachteile:
|
||||||
|
|
||||||
|
- Möglicherweise geringere Genauigkeit als komplexere Modelle
|
||||||
|
- Kann bei großen Datensätzen rechenintensiv sein
|
||||||
|
|
||||||
|
### Vergleich der Methoden
|
||||||
|
|
||||||
|
In unserem Projekt vergleichen wir die Erklärungen von LIME und RIPPER, um ein umfassenderes Verständnis der Modellentscheidungen zu gewinnen. Dieser Vergleich zeigt:
|
||||||
|
|
||||||
|
- Welche Features von beiden Methoden als wichtig identifiziert werden
|
||||||
|
- Wo die Methoden unterschiedliche Erklärungen liefern
|
||||||
|
- Die Vor- und Nachteile jeder Methode in Bezug auf Interpretierbarkeit und Genauigkeit
|
||||||
|
|
||||||
|
## Anwendung auf einzelne Beispiele
|
||||||
|
|
||||||
|
Ein wichtiger Aspekt von Explainable AI ist die Fähigkeit, einzelne Vorhersagen zu erklären. Im Notebook wird ein zufälliges Beispiel aus den Testdaten ausgewählt und detailliert erklärt:
|
||||||
|
|
||||||
|
1. Anzeige des tatsächlichen und vorhergesagten Labels
|
||||||
|
2. Erklärung mit LIME, die ein lokales lineares Modell um die Vorhersage herum erstellt
|
||||||
|
3. Anwendung der RIPPER-Regeln auf das Beispiel
|
||||||
|
4. Vergleich der Vorhersagen des Random Forest Modells und der RIPPER-Regeln
|
||||||
|
|
||||||
|
Diese Erklärung hilft zu verstehen, warum das Modell eine bestimmte Vorhersage für eine bestimmte Person getroffen hat, was in realen Anwendungen sehr wertvoll sein kann.
|
||||||
|
|
||||||
|
## Voraussetzungen und Installation
|
||||||
|
|
||||||
|
Um dieses Projekt auszuführen, benötigen Sie:
|
||||||
|
|
||||||
|
1. Python 3.6 oder höher
|
||||||
|
2. Jupyter Notebook oder JupyterLab
|
||||||
|
3. Folgende Python-Bibliotheken:
|
||||||
|
- scikit-learn
|
||||||
|
- pandas
|
||||||
|
- numpy
|
||||||
|
- matplotlib
|
||||||
|
- seaborn
|
||||||
|
- lime
|
||||||
|
- wittgenstein
|
||||||
|
|
||||||
|
Sie können die benötigten Bibliotheken mit folgendem Befehl installieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install scikit-learn pandas numpy matplotlib seaborn lime wittgenstein
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fazit
|
||||||
|
|
||||||
|
Dieses Projekt demonstriert, wie Machine Learning-Modelle nicht nur für Vorhersagen, sondern auch für das Verständnis der zugrunde liegenden Daten und Zusammenhänge verwendet werden können. Durch die Verwendung verschiedener Explainable AI-Techniken wie Feature Importance, LIME und regelbasierte Erklärungen können wir:
|
||||||
|
|
||||||
|
1. Die wichtigsten Faktoren identifizieren, die das Einkommensniveau beeinflussen
|
||||||
|
2. Verstehen, wie diese Faktoren zusammenwirken
|
||||||
|
3. Einzelne Vorhersagen detailliert erklären
|
||||||
|
4. Verschiedene Erklärungsmethoden vergleichen und ihre Stärken und Schwächen verstehen
|
||||||
|
|
||||||
|
Der Vergleich zwischen LIME und regelbasierten Erklärungen zeigt, dass beide Methoden wertvolle Einblicke bieten können:
|
||||||
|
|
||||||
|
- LIME bietet detaillierte lokale Erklärungen für einzelne Vorhersagen
|
||||||
|
- Regelbasierte Erklärungen bietet globale, leicht verständliche Regeln, die das Gesamtverhalten des Modells beschreiben
|
||||||
|
|
||||||
|
Die Kombination verschiedener XAI-Methoden ermöglicht ein umfassenderes Verständnis der Modellentscheidungen und kann dazu beitragen, das Vertrauen in Machine Learning-Modelle zu erhöhen.
|
||||||
|
|
||||||
|
# Modell und XAI-Methoden im Überblick
|
||||||
|
|
||||||
|
## Random Forest
|
||||||
|
|
||||||
|
Random Forest ist ein Ensemble-Lernalgorithmus, der mehrere Entscheidungsbäume kombiniert, um präzisere Vorhersagen zu treffen. Er funktioniert, indem er:
|
||||||
|
|
||||||
|
Viele verschiedene Entscheidungsbäume auf zufällig ausgewählten Teilmengen der Trainingsdaten aufbaut
|
||||||
|
Bei jedem Split nur eine zufällige Teilmenge der Features berücksichtigt
|
||||||
|
Die Ergebnisse aller Bäume mittelt (bei Regression) oder durch Mehrheitsentscheidung kombiniert (bei Klassifikation)
|
||||||
|
Random Forests sind beliebt wegen ihrer hohen Genauigkeit, Robustheit gegen Overfitting und geringen Parameteroptimierungsbedarf.
|
||||||
|
|
||||||
|
## LIME (Local Interpretable Model-agnostic Explanations)
|
||||||
|
|
||||||
|
LIME ist eine Methode zur Erklärung der Vorhersagen beliebiger Machine Learning Modelle. LIME funktioniert durch:
|
||||||
|
|
||||||
|
Erzeugung von Stichproben in der Nähe der zu erklärenden Instanz
|
||||||
|
Training eines lokalen, interpretierbaren Modells (z.B. linearer Regression) auf diesen Stichproben
|
||||||
|
Nutzung der Gewichte des lokalen Modells, um zu zeigen, welche Features die Vorhersage am stärksten beeinflussen
|
||||||
|
LIME ermöglicht es, komplexe Modelle wie neuronale Netze oder Random Forests lokal zu interpretieren, ohne deren innere Struktur zu kennen.
|
||||||
|
|
||||||
|
## Rule-Based Explanations
|
||||||
|
|
||||||
|
Rule-Based Explanations verwenden Wenn-Dann-Regeln, um Modellvorhersagen zu erklären. Sie:
|
||||||
|
|
||||||
|
Extrahieren verständliche Entscheidungsregeln aus komplexen Modellen
|
||||||
|
Präsentieren Bedingungen, die zu bestimmten Vorhersagen führen
|
||||||
|
Können aus regelbasierten Modellen direkt abgeleitet oder aus komplexeren Modellen approximiert werden
|
||||||
|
Methoden wie RIPPER, CART oder die Extraktion von Regeln aus Random Forests fallen in diese Kategorie. Diese Erklärungen sind besonders wertvoll für Domänenexperten ohne ML-Hintergrund, da sie in natürlicher Sprache formuliert werden können.
|
||||||
22
extract_cells.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def extractToFiles(filePath: str, folder: str = "extracted_cells"):
|
||||||
|
with open(filePath, "r") as file:
|
||||||
|
fileContent = file.read()
|
||||||
|
content = json.loads(fileContent)
|
||||||
|
i=0
|
||||||
|
for cell in content["cells"]:
|
||||||
|
if cell["cell_type"] == "code":
|
||||||
|
codeStr = ""
|
||||||
|
for line in cell["source"]:
|
||||||
|
codeStr += line
|
||||||
|
Path(folder).mkdir(parents=True, exist_ok=True)
|
||||||
|
location = os.path.join(folder, f"cell{i}.py")
|
||||||
|
with open(location, "w+") as file:
|
||||||
|
file.write(codeStr)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
|
||||||
|
extractToFiles("Explainable_AI_Adult_Census_Income.ipynb")
|
||||||
1
extracted_cells/cell0.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
%pip install scikit-learn matplotlib seaborn pandas numpy lime
|
||||||
15
extracted_cells/cell1.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import seaborn as sns
|
||||||
|
from sklearn.ensemble import RandomForestClassifier
|
||||||
|
from sklearn.model_selection import train_test_split, GridSearchCV
|
||||||
|
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix, classification_report
|
||||||
|
from sklearn.preprocessing import LabelEncoder
|
||||||
|
from sklearn.tree import plot_tree
|
||||||
|
|
||||||
|
# Daten einlesen
|
||||||
|
df = pd.read_csv("./sample_data/adult_census_income/adult.csv")
|
||||||
|
|
||||||
|
# Anzeigen der ersten Zeilen des Datensatzes
|
||||||
|
df.head(10)
|
||||||
11
extracted_cells/cell10.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Features und Zielwerte definieren
|
||||||
|
X = df_encoded.drop(['income', 'income_encoded'], axis=1)
|
||||||
|
y = df_encoded['income_encoded']
|
||||||
|
|
||||||
|
|
||||||
|
# Daten in Trainings- und Testdaten aufteilen
|
||||||
|
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
|
||||||
|
|
||||||
|
print(f"Trainingsdaten: {X_train.shape[0]} Beispiele")
|
||||||
|
print(f"Testdaten: {X_test.shape[0]} Beispiele")
|
||||||
|
print(f"Features: {X_train.shape[1]} Merkmale")
|
||||||
17
extracted_cells/cell11.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Random Forest Modell trainieren
|
||||||
|
rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
|
||||||
|
rf_model.fit(X_train, y_train)
|
||||||
|
|
||||||
|
# Vorhersagen für die Testdaten
|
||||||
|
y_pred = rf_model.predict(X_test)
|
||||||
|
|
||||||
|
# Modellleistung evaluieren
|
||||||
|
accuracy = accuracy_score(y_test, y_pred)
|
||||||
|
precision = precision_score(y_test, y_pred)
|
||||||
|
recall = recall_score(y_test, y_pred)
|
||||||
|
f1 = f1_score(y_test, y_pred)
|
||||||
|
|
||||||
|
print(f"Accuracy: {accuracy:.4f}")
|
||||||
|
print(f"Precision: {precision:.4f}")
|
||||||
|
print(f"Recall: {recall:.4f}")
|
||||||
|
print(f"F1 Score: {f1:.4f}")
|
||||||
16
extracted_cells/cell12.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
# Confusion Matrix
|
||||||
|
cm = confusion_matrix(y_test, y_pred)
|
||||||
|
plt.figure(figsize=(8, 6))
|
||||||
|
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', cbar=False,
|
||||||
|
xticklabels=['<=50K', '>50K'],
|
||||||
|
yticklabels=['<=50K', '>50K'])
|
||||||
|
plt.xlabel('Vorhergesagt')
|
||||||
|
plt.ylabel('Tatsächlich')
|
||||||
|
plt.title('Confusion Matrix')
|
||||||
|
plt.savefig('output/Confusions_Matrix.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# Klassifikationsbericht
|
||||||
|
print("\nKlassifikationsbericht:")
|
||||||
|
print(classification_report(y_test, y_pred, target_names=['<=50K', '>50K']))
|
||||||
12
extracted_cells/cell13.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Visualisierung eines einzelnen Entscheidungsbaums aus dem Random Forest
|
||||||
|
plt.figure(figsize=(25, 12))
|
||||||
|
tree_to_plot = rf_model.estimators_[0] # Ersten Baum aus dem Forest auswählen
|
||||||
|
plot_tree(tree_to_plot,
|
||||||
|
feature_names=X_train.columns,
|
||||||
|
class_names=['<=50K', '>50K'],
|
||||||
|
filled=True,
|
||||||
|
rounded=True,
|
||||||
|
fontsize=10,
|
||||||
|
max_depth=3)
|
||||||
|
plt.savefig('output/Random_Forest_Tree_Example.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
12
extracted_cells/cell14.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Feature Importance
|
||||||
|
feature_importance = pd.DataFrame({
|
||||||
|
'Feature': X_train.columns,
|
||||||
|
'Importance': rf_model.feature_importances_
|
||||||
|
}).sort_values('Importance', ascending=False)
|
||||||
|
|
||||||
|
plt.figure(figsize=(12, 8))
|
||||||
|
sns.barplot(x='Importance', y='Feature', data=feature_importance.head(15))
|
||||||
|
plt.title('Feature Importance')
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('output/Feature_Importance.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
33
extracted_cells/cell15.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Hyperparameter-Grid definieren
|
||||||
|
param_grid = {
|
||||||
|
'n_estimators': [50, 100],
|
||||||
|
'max_depth': [None, 10, 20],
|
||||||
|
'min_samples_split': [2, 5],
|
||||||
|
'min_samples_leaf': [1, 2]
|
||||||
|
}
|
||||||
|
|
||||||
|
# GridSearchCV
|
||||||
|
grid_search = GridSearchCV(RandomForestClassifier(random_state=42), param_grid, cv=3, scoring='accuracy')
|
||||||
|
grid_search.fit(X_train, y_train)
|
||||||
|
|
||||||
|
# Beste Parameter
|
||||||
|
print("Beste Parameter:")
|
||||||
|
print(grid_search.best_params_)
|
||||||
|
|
||||||
|
# Bestes Modell
|
||||||
|
best_rf_model = grid_search.best_estimator_
|
||||||
|
|
||||||
|
# Vorhersagen mit dem besten Modell
|
||||||
|
y_pred_best = best_rf_model.predict(X_test)
|
||||||
|
|
||||||
|
# Modellleistung evaluieren
|
||||||
|
accuracy_best = accuracy_score(y_test, y_pred_best)
|
||||||
|
precision_best = precision_score(y_test, y_pred_best)
|
||||||
|
recall_best = recall_score(y_test, y_pred_best)
|
||||||
|
f1_best = f1_score(y_test, y_pred_best)
|
||||||
|
|
||||||
|
print(f"\nBestes Modell:")
|
||||||
|
print(f"Accuracy: {accuracy_best:.4f}")
|
||||||
|
print(f"Precision: {precision_best:.4f}")
|
||||||
|
print(f"Recall: {recall_best:.4f}")
|
||||||
|
print(f"F1 Score: {f1_best:.4f}")
|
||||||
17
extracted_cells/cell16.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# LIME für Erklärbarkeit
|
||||||
|
from lime import lime_tabular
|
||||||
|
import random
|
||||||
|
|
||||||
|
# Erstelle einen LIME-Erklärer
|
||||||
|
lime_explainer = lime_tabular.LimeTabularExplainer(
|
||||||
|
X_train.values,
|
||||||
|
feature_names=X_train.columns,
|
||||||
|
class_names=['<=50K', '>50K'],
|
||||||
|
mode='classification',
|
||||||
|
random_state=42
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wähle ein zufälliges Beispiel aus den Testdaten
|
||||||
|
random_idx = random.randint(0, len(X_test) - 1)
|
||||||
|
instance_df = X_test.iloc[random_idx:random_idx+1]
|
||||||
|
instance = instance_df.values[0] # Für LIME benötigen wir das Array
|
||||||
65
extracted_cells/cell17.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Erkläre die Vorhersage mit LIME
|
||||||
|
|
||||||
|
def analyze_lime_feature_importance(instance, rf_model, lime_explainer, num_features=5):
|
||||||
|
"""
|
||||||
|
Analysiert die Feature-Wichtigkeiten der LIME-Erklärung.
|
||||||
|
"""
|
||||||
|
# LIME-Erklärung generieren
|
||||||
|
exp = lime_explainer.explain_instance(
|
||||||
|
instance,
|
||||||
|
rf_model.predict_proba,
|
||||||
|
num_features=num_features
|
||||||
|
)
|
||||||
|
|
||||||
|
# Random Forest Vorhersage (nur zur Information)
|
||||||
|
feature_names = rf_model.feature_names_in_
|
||||||
|
instance_df = pd.DataFrame([instance], columns=feature_names)
|
||||||
|
rf_prediction = rf_model.predict_proba(instance_df)[0, 1]
|
||||||
|
|
||||||
|
# Feature-Wichtigkeiten aus LIME extrahieren
|
||||||
|
feature_importance = exp.as_list()
|
||||||
|
|
||||||
|
# Visualisierung der Feature-Wichtigkeiten
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
|
||||||
|
# Features und ihre Wichtigkeiten trennen
|
||||||
|
features, importances = zip(*feature_importance)
|
||||||
|
|
||||||
|
# Balkendiagramm erstellen
|
||||||
|
colors = ['red' if imp < 0 else 'green' for imp in importances]
|
||||||
|
y_pos = np.arange(len(features))
|
||||||
|
|
||||||
|
plt.barh(y_pos, importances, color=colors)
|
||||||
|
plt.yticks(y_pos, features)
|
||||||
|
|
||||||
|
plt.xlabel('Feature-Einfluss')
|
||||||
|
plt.title('LIME Feature-Wichtigkeiten')
|
||||||
|
|
||||||
|
# Vertikale Linie bei 0 für bessere Lesbarkeit
|
||||||
|
plt.axvline(x=0, color='black', linestyle='-', alpha=0.3)
|
||||||
|
|
||||||
|
plt.grid(alpha=0.3)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('output/lime_feature_importance.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# Ausgabe der Ergebnisse
|
||||||
|
print("\nLIME Feature-Wichtigkeiten Analyse:")
|
||||||
|
print("-" * 50)
|
||||||
|
print(f"Random Forest Vorhersage für diese Instanz: {rf_prediction:.4f}")
|
||||||
|
print("\nFeature-Einflüsse:")
|
||||||
|
for feature, importance in feature_importance:
|
||||||
|
print(f"{feature}: {importance:+.4f}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'rf_prediction': rf_prediction,
|
||||||
|
'feature_importance': dict(feature_importance)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Analysiere wie gut LIME die RF-Vorhersage erklärt
|
||||||
|
importance_analysis = analyze_lime_feature_importance(
|
||||||
|
instance=instance,
|
||||||
|
rf_model=best_rf_model,
|
||||||
|
lime_explainer=lime_explainer,
|
||||||
|
num_features=20
|
||||||
|
)
|
||||||
49
extracted_cells/cell18.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Wählen wir zwei Features zur Variation:
|
||||||
|
less_important_feature_education = "education.num"
|
||||||
|
less_important_feature_fnlwgt = "fnlwgt"
|
||||||
|
|
||||||
|
# Festlegen derRange für die Variation der zwei Features
|
||||||
|
education_range = np.linspace(instance_df[less_important_feature_education].values[0] - 10, instance_df[less_important_feature_education].values[0] + 10, 50)
|
||||||
|
fnlwgt_range = np.linspace(instance_df[less_important_feature_fnlwgt].values[0] - 100000, instance_df[less_important_feature_fnlwgt].values[0] + 100000, 50)
|
||||||
|
|
||||||
|
# Erstellen von Instanzen für LIME
|
||||||
|
instances_education = pd.DataFrame([instance] * len(education_range), columns=X_train.columns)
|
||||||
|
instances_fnlwgt = pd.DataFrame([instance] * len(fnlwgt_range), columns=X_train.columns)
|
||||||
|
|
||||||
|
# Ändern der Feature-Werte in den Instanzen
|
||||||
|
instances_education[less_important_feature_education] = education_range
|
||||||
|
instances_fnlwgt[less_important_feature_fnlwgt] = fnlwgt_range
|
||||||
|
|
||||||
|
# Vorhersagen mit dem Modell (Wahrscheinlichkeiten)
|
||||||
|
instances_education["prediction"] = best_rf_model.predict_proba(instances_education)[:, 1]
|
||||||
|
instances_fnlwgt["prediction"] = best_rf_model.predict_proba(instances_fnlwgt)[:, 1]
|
||||||
|
|
||||||
|
# Bestimmen der y-Achsen-Grenzen (min/max für alle Vorhersagen)
|
||||||
|
y_min = min(instances_education["prediction"].min(), instances_fnlwgt["prediction"].min())
|
||||||
|
y_max = max(instances_education["prediction"].max(), instances_fnlwgt["prediction"].max())
|
||||||
|
|
||||||
|
# Visualisierung der Variation von 'education-num' (moderater Einfluss)
|
||||||
|
plt.figure(figsize=(8,5))
|
||||||
|
plt.plot(education_range, instances_education["prediction"], label="Moderater Einfluss auf die Vorhersage", color='green')
|
||||||
|
plt.axvline(instance_df[less_important_feature_education].values[0], color="red", linestyle="dashed", label="Originalwert")
|
||||||
|
plt.xlabel("Bildungsniveau (education-num)")
|
||||||
|
plt.ylabel("Vorhersage (0 = <=50K, 1 = >50K)")
|
||||||
|
plt.title(f"Einfluss von {less_important_feature_education} auf die Vorhersage")
|
||||||
|
plt.ylim([y_min, y_max]) # Einheitliche y-Achse
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# Visualisierung der Variation von 'fnlwgt' (wenig Einfluss)
|
||||||
|
plt.figure(figsize=(8,5))
|
||||||
|
plt.plot(fnlwgt_range, instances_fnlwgt["prediction"], label="Wenig Einfluss auf die Vorhersage", color='orange')
|
||||||
|
plt.axvline(instance_df[less_important_feature_fnlwgt].values[0], color="red", linestyle="dashed", label="Originalwert")
|
||||||
|
plt.xlabel("Finales Gewicht (fnlwgt)")
|
||||||
|
plt.ylabel("Vorhersage (0 = <=50K, 1 = >50K)")
|
||||||
|
plt.title(f"Einfluss von {less_important_feature_fnlwgt} auf die Vorhersage")
|
||||||
|
plt.ylim([y_min, y_max]) # Einheitliche y-Achse
|
||||||
|
plt.legend()
|
||||||
|
plt.show()
|
||||||
40
extracted_cells/cell19.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pandas as pd
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from sklearn.linear_model import LinearRegression
|
||||||
|
from sklearn.metrics.pairwise import euclidean_distances
|
||||||
|
|
||||||
|
# Anzahl der zu erzeugenden Perturbationen
|
||||||
|
num_samples = 500
|
||||||
|
|
||||||
|
# Die Originalinstanz, die wir erklären wollen
|
||||||
|
original_instance = instance_df.iloc[0].copy()
|
||||||
|
|
||||||
|
# Erstelle perturbierte Instanzen durch zufällige Variation aller Features
|
||||||
|
perturbed_instances = pd.DataFrame(
|
||||||
|
np.random.normal(loc=original_instance, scale=1.0, size=(num_samples, len(original_instance))),
|
||||||
|
columns=original_instance.index
|
||||||
|
)
|
||||||
|
|
||||||
|
# Vorhersagen für die perturbierten Instanzen mit dem Random-Forest-Modell
|
||||||
|
perturbed_instances["prediction"] = best_rf_model.predict_proba(perturbed_instances)[:, 1]
|
||||||
|
|
||||||
|
# Berechnung der Gewichte nach Distanz zur Originalinstanz
|
||||||
|
distances = euclidean_distances(perturbed_instances.drop(columns=["prediction"]), [original_instance])
|
||||||
|
kernel_width = np.sqrt(len(original_instance)) # Kernel-Bandbreite
|
||||||
|
weights = np.exp(- (distances ** 2) / (2 * (kernel_width ** 2)))
|
||||||
|
|
||||||
|
# Gewichtete lokale lineare Regression zum Erklären der Vorhersage
|
||||||
|
lin_reg = LinearRegression()
|
||||||
|
lin_reg.fit(perturbed_instances.drop(columns=["prediction"]), perturbed_instances["prediction"], sample_weight=weights.flatten())
|
||||||
|
|
||||||
|
# Anzeige der Feature-Wichtigkeiten
|
||||||
|
feature_importances = pd.Series(lin_reg.coef_, index=original_instance.index).sort_values(key=abs, ascending=False)
|
||||||
|
|
||||||
|
# Visualisierung der wichtigsten Features
|
||||||
|
plt.figure(figsize=(8, 5))
|
||||||
|
feature_importances[:10].plot(kind="barh", color="skyblue")
|
||||||
|
plt.xlabel("Einfluss auf die Vorhersage")
|
||||||
|
plt.title("Erklärungsmodell (Nachbildung von LIME)")
|
||||||
|
plt.gca().invert_yaxis()
|
||||||
|
plt.show()
|
||||||
2
extracted_cells/cell2.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Informationen über den Datensatz
|
||||||
|
print("Datensatzgröße:", df.shape)
|
||||||
25
extracted_cells/cell20.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
def predict_fn(x):
|
||||||
|
# Konvertiere das NumPy-Array zurück in ein DataFrame mit den richtigen Feature-Namen
|
||||||
|
df = pd.DataFrame(x, columns=X_train.columns)
|
||||||
|
return best_rf_model.predict_proba(df)
|
||||||
|
|
||||||
|
# Speichere mehrere Erklärungen
|
||||||
|
explanations = []
|
||||||
|
for i in range(100):
|
||||||
|
exp = lime_explainer.explain_instance(
|
||||||
|
instance,
|
||||||
|
predict_fn,
|
||||||
|
num_features=20
|
||||||
|
)
|
||||||
|
explanations.append(exp.as_list())
|
||||||
|
|
||||||
|
# Berechne die Varianz der Feature-Wichtigkeiten
|
||||||
|
feature_variances = {}
|
||||||
|
for i in range(len(explanations[0])):
|
||||||
|
feature = explanations[0][i][0]
|
||||||
|
values = [exp[i][1] for exp in explanations if len(exp) > i and exp[i][0] == feature]
|
||||||
|
feature_variances[feature] = np.var(values)
|
||||||
|
|
||||||
|
print("Varianz der Feature-Wichtigkeiten:")
|
||||||
|
for feature, variance in feature_variances.items():
|
||||||
|
print(f"{feature}: {variance:.6f}")
|
||||||
53
extracted_cells/cell21.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Erstelle nur das farbcodierte Stabilitätsdiagramm für die Top-5 Features
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
from matplotlib.patches import Patch
|
||||||
|
|
||||||
|
# Anzahl der anzuzeigenden Features (Top-N mit höchster Varianz)
|
||||||
|
num_features_to_show = 5 # Top-5 Features
|
||||||
|
|
||||||
|
# Sortiere die Features nach Varianz (absteigend)
|
||||||
|
sorted_features = sorted(feature_variances.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
# Beschränke auf die Top-N Features
|
||||||
|
sorted_features = sorted_features[:num_features_to_show]
|
||||||
|
features = [item[0] for item in sorted_features]
|
||||||
|
variances = [item[1] for item in sorted_features]
|
||||||
|
|
||||||
|
# Definiere Schwellenwerte für die Farbkodierung
|
||||||
|
low_threshold = 0.0001
|
||||||
|
medium_threshold = 0.001
|
||||||
|
|
||||||
|
# Farbkodierung basierend auf Varianzwerten
|
||||||
|
colors = []
|
||||||
|
for v in variances:
|
||||||
|
if v < low_threshold:
|
||||||
|
colors.append('green') # Niedrige Varianz - sehr stabil
|
||||||
|
elif v < medium_threshold:
|
||||||
|
colors.append('orange') # Mittlere Varianz - mäßig stabil
|
||||||
|
else:
|
||||||
|
colors.append('red') # Hohe Varianz - instabil
|
||||||
|
|
||||||
|
# Erstelle das Balkendiagramm mit Farbkodierung
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
bars = plt.barh(features, variances, color=colors)
|
||||||
|
|
||||||
|
# Füge Werte am Ende der Balken hinzu
|
||||||
|
for i, v in enumerate(variances):
|
||||||
|
plt.text(v + 0.00001, i, f"{v:.6f}", va='center')
|
||||||
|
|
||||||
|
# Füge eine Legende hinzu
|
||||||
|
legend_elements = [
|
||||||
|
Patch(facecolor='green', label='Sehr stabil (< 0.0001)'),
|
||||||
|
Patch(facecolor='orange', label='Mäßig stabil (< 0.001)'),
|
||||||
|
Patch(facecolor='red', label='Instabil (≥ 0.001)')
|
||||||
|
]
|
||||||
|
plt.legend(handles=legend_elements, loc='lower right')
|
||||||
|
|
||||||
|
plt.xlabel('Varianz')
|
||||||
|
plt.title('Stabilität der Top-5 LIME-Features')
|
||||||
|
plt.grid(axis='x', linestyle='--', alpha=0.7)
|
||||||
|
plt.tight_layout()
|
||||||
|
|
||||||
|
# Speichere die Abbildung
|
||||||
|
plt.savefig('output/Lime_Varianz_Features.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
14
extracted_cells/cell22.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from sklearn.tree import DecisionTreeClassifier, export_text
|
||||||
|
|
||||||
|
# Random Forest-Vorhersagen verwenden als Ziel
|
||||||
|
rf_predictions = rf_model.predict(X_train)
|
||||||
|
|
||||||
|
# Einfachen Entscheidungsbaum auf die Vorhersagen des Random Forests trainieren
|
||||||
|
surrogate_tree = DecisionTreeClassifier(max_depth=5)
|
||||||
|
surrogate_tree.fit(X_train, rf_predictions)
|
||||||
|
|
||||||
|
# Evaluieren, wie gut der Baum den Random Forest approximiert
|
||||||
|
surrogate_predictions = surrogate_tree.predict(X_test)
|
||||||
|
rf_test_predictions = rf_model.predict(X_test)
|
||||||
|
surrogate_accuracy = np.mean(surrogate_predictions == rf_test_predictions)
|
||||||
|
print(f"Genauigkeit des Surrogate-Modells: {surrogate_accuracy:.4f}")
|
||||||
13
extracted_cells/cell23.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Formatierte Regeln anzeigen
|
||||||
|
def format_rules(tree_rules):
|
||||||
|
"""Formatiert die Baumregeln mit menschenlesbaren Klassennamen"""
|
||||||
|
# Ersetze 'class: 0' durch 'Einkommen ≤ 50K'
|
||||||
|
formatted_rules = tree_rules.replace('class: 0', 'Einkommen ≤ 50K')
|
||||||
|
# Ersetze 'class: 1' durch 'Einkommen > 50K'
|
||||||
|
formatted_rules = formatted_rules.replace('class: 1', 'Einkommen > 50K')
|
||||||
|
return formatted_rules
|
||||||
|
|
||||||
|
# Regeln aus dem Surrogate-Baum extrahieren
|
||||||
|
tree_rules = export_text(surrogate_tree, feature_names=X_train.columns.tolist())
|
||||||
|
#print(tree_rules)
|
||||||
|
print(format_rules(tree_rules))
|
||||||
69
extracted_cells/cell24.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
from sklearn.tree import _tree
|
||||||
|
|
||||||
|
def extract_single_rule(tree, feature_names, class_to_extract=1):
|
||||||
|
"""
|
||||||
|
Extrahiert eine einzelne Regel aus einem Decision Tree für eine bestimmte Klasse.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
tree : DecisionTreeClassifier
|
||||||
|
Der trainierte Entscheidungsbaum
|
||||||
|
feature_names : list
|
||||||
|
Liste der Feature-Namen
|
||||||
|
class_to_extract : int, default=1
|
||||||
|
Die Klasse, für die eine Regel extrahiert werden soll (0=≤50K, 1=>50K)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
--------
|
||||||
|
rule : str
|
||||||
|
Eine lesbare Regel als String
|
||||||
|
"""
|
||||||
|
tree_ = tree.tree_
|
||||||
|
|
||||||
|
# Funktion zum rekursiven Extrahieren einer Regel
|
||||||
|
def tree_to_rule(node, depth, conditions):
|
||||||
|
# Wenn wir einen Blattknoten erreicht haben
|
||||||
|
if tree_.children_left[node] == _tree.TREE_LEAF:
|
||||||
|
# Prüfe, ob dieser Blattknoten die gewünschte Klasse vorhersagt
|
||||||
|
if np.argmax(tree_.value[node][0]) == class_to_extract:
|
||||||
|
# Formatiere die Bedingungen als Regel
|
||||||
|
if conditions:
|
||||||
|
rule = " UND ".join(conditions)
|
||||||
|
return rule
|
||||||
|
else:
|
||||||
|
return "Keine Bedingungen (Wurzelklasse)"
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Feature und Schwellenwert am aktuellen Knoten
|
||||||
|
feature = feature_names[tree_.feature[node]]
|
||||||
|
threshold = tree_.threshold[node]
|
||||||
|
|
||||||
|
# Linkspfad (≤)
|
||||||
|
left_conditions = conditions + [f"{feature} ≤ {threshold:.2f}"]
|
||||||
|
left_rule = tree_to_rule(tree_.children_left[node], depth + 1, left_conditions)
|
||||||
|
if left_rule is not None:
|
||||||
|
return left_rule
|
||||||
|
|
||||||
|
# Rechtspfad (>)
|
||||||
|
right_conditions = conditions + [f"{feature} > {threshold:.2f}"]
|
||||||
|
right_rule = tree_to_rule(tree_.children_right[node], depth + 1, right_conditions)
|
||||||
|
if right_rule is not None:
|
||||||
|
return right_rule
|
||||||
|
|
||||||
|
# Keine passende Regel gefunden
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Starte die Suche vom Wurzelknoten
|
||||||
|
rule = tree_to_rule(0, 1, [])
|
||||||
|
|
||||||
|
# Formatiere die Ausgabe
|
||||||
|
class_name = "Einkommen > 50K" if class_to_extract == 1 else "Einkommen ≤ 50K"
|
||||||
|
if rule:
|
||||||
|
return f"WENN {rule} DANN {class_name}"
|
||||||
|
else:
|
||||||
|
return f"Keine Regel für {class_name} gefunden."
|
||||||
|
|
||||||
|
# Anwendung für die Extraktion einer Regel für hohes Einkommen (Klasse 1)
|
||||||
|
single_rule = extract_single_rule(surrogate_tree, X_train.columns.tolist(), class_to_extract=1)
|
||||||
|
print("Einzelne Regel aus dem Surrogate-Modell:")
|
||||||
|
print(single_rule)
|
||||||
67
extracted_cells/cell25.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def plot_surrogate_accuracy_vs_depth_test_only(rf_model, X_train, X_test, max_depths=range(1, 16)):
|
||||||
|
"""
|
||||||
|
Visualisiert die Genauigkeit des Surrogate-Modells für verschiedene Baumtiefen,
|
||||||
|
fokussiert nur auf die Testdaten.
|
||||||
|
"""
|
||||||
|
# Random Forest-Vorhersagen (nur einmal berechnen)
|
||||||
|
rf_train_predictions = rf_model.predict(X_train)
|
||||||
|
rf_test_predictions = rf_model.predict(X_test)
|
||||||
|
|
||||||
|
# Ergebnisse für verschiedene Baumtiefen
|
||||||
|
test_accuracies = []
|
||||||
|
|
||||||
|
for depth in max_depths:
|
||||||
|
# Surrogate-Baum mit aktueller Tiefe trainieren
|
||||||
|
surrogate_tree = DecisionTreeClassifier(max_depth=depth, random_state=42)
|
||||||
|
surrogate_tree.fit(X_train, rf_train_predictions)
|
||||||
|
|
||||||
|
# Vorhersagen
|
||||||
|
surrogate_test_pred = surrogate_tree.predict(X_test)
|
||||||
|
|
||||||
|
# Genauigkeit berechnen
|
||||||
|
test_acc = np.mean(surrogate_test_pred == rf_test_predictions)
|
||||||
|
test_accuracies.append(test_acc)
|
||||||
|
|
||||||
|
# Visualisierung
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
plt.plot(max_depths, test_accuracies, 'o-', color='#ED7D31', linewidth=2)
|
||||||
|
|
||||||
|
# Finde die beste Tiefe
|
||||||
|
best_depth = max_depths[np.argmax(test_accuracies)]
|
||||||
|
best_acc = max(test_accuracies)
|
||||||
|
|
||||||
|
# Markiere den besten Punkt
|
||||||
|
plt.scatter([best_depth], [best_acc], s=100, c='red', zorder=5)
|
||||||
|
plt.annotate(f'Optimale Tiefe: {best_depth}\nGenauigkeit: {best_acc:.4f}',
|
||||||
|
xy=(best_depth, best_acc), xytext=(best_depth+1, best_acc-0.05),
|
||||||
|
arrowprops=dict(facecolor='black', shrink=0.05, width=1.5))
|
||||||
|
|
||||||
|
# Beschriftungen und Layout
|
||||||
|
plt.grid(alpha=0.3)
|
||||||
|
plt.title('Surrogate-Modell-Genauigkeit bei verschiedenen Baumtiefen', fontsize=14)
|
||||||
|
plt.xlabel('Maximale Baumtiefe', fontsize=12)
|
||||||
|
plt.ylabel('Genauigkeit auf Testdaten', fontsize=12)
|
||||||
|
|
||||||
|
# Füge Werte über den Punkten hinzu
|
||||||
|
for i, acc in enumerate(test_accuracies):
|
||||||
|
plt.text(max_depths[i], acc + 0.01, f'{acc:.3f}', ha='center')
|
||||||
|
|
||||||
|
# Y-Achse anpassen (je nach Daten)
|
||||||
|
y_min = max(0, min(test_accuracies) - 0.05)
|
||||||
|
plt.ylim(y_min, 1.05)
|
||||||
|
|
||||||
|
# Verbesserte visuelle Elemente
|
||||||
|
plt.fill_between(max_depths, test_accuracies, y_min, alpha=0.1, color='#ED7D31')
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('output/surrogate_accuracy.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
return best_depth, best_acc
|
||||||
|
|
||||||
|
# Aufruf der Funktion
|
||||||
|
best_depth, best_accuracy = plot_surrogate_accuracy_vs_depth_test_only(rf_model, X_train, X_test)
|
||||||
|
print(f"Optimale Baumtiefe: {best_depth} mit einer Genauigkeit von {best_accuracy:.4f}")
|
||||||
3
extracted_cells/cell26.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Für Quarto Präsentationen
|
||||||
|
import extract_cells
|
||||||
|
extract_cells.extractToFiles("Explainable_AI_Adult_Census_Income.ipynb")
|
||||||
3
extracted_cells/cell3.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
print("\nDatentypen:")
|
||||||
|
#print(df.dtypes)
|
||||||
|
df.info()
|
||||||
2
extracted_cells/cell4.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
print("\nFehlende Werte:")
|
||||||
|
df.isnull().sum()
|
||||||
2
extracted_cells/cell5.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
print("\nStatistische Zusammenfassung:")
|
||||||
|
df.describe()
|
||||||
13
extracted_cells/cell6.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Überprüfen der Verteilung der Zielklasse
|
||||||
|
plt.figure(figsize=(8, 6))
|
||||||
|
sns.countplot(x='income', data=df)
|
||||||
|
plt.title('Verteilung der Einkommensklassen')
|
||||||
|
plt.xlabel('Einkommen')
|
||||||
|
plt.ylabel('Anzahl')
|
||||||
|
plt.savefig('output/Verteilung_Einkommensklassen.png', dpi=300)
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
# Prozentuale Verteilung berechnen
|
||||||
|
income_counts = df['income'].value_counts(normalize=True) * 100
|
||||||
|
print("Prozentuale Verteilung der Einkommensklassen:")
|
||||||
|
print(income_counts)
|
||||||
5
extracted_cells/cell7.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Überprüfen auf fehlende Werte oder '?'
|
||||||
|
for col in df.columns:
|
||||||
|
missing_count = df[df[col] == '?'].shape[0]
|
||||||
|
if missing_count > 0:
|
||||||
|
print(f"Spalte '{col}' hat {missing_count} Einträge mit '?'")
|
||||||
13
extracted_cells/cell8.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Ersetzen von '?' durch NaN und dann durch den häufigsten Wert
|
||||||
|
df_clean = df.copy()
|
||||||
|
|
||||||
|
for col in df_clean.columns:
|
||||||
|
if df_clean[col].dtype == 'object':
|
||||||
|
# Ersetze '?' durch NaN
|
||||||
|
df_clean[col] = df_clean[col].replace('?', np.nan)
|
||||||
|
|
||||||
|
# Ersetze NaN durch den häufigsten Wert
|
||||||
|
most_frequent = df_clean[col].mode()[0]
|
||||||
|
df_clean[col] = df_clean[col].fillna(most_frequent)
|
||||||
|
|
||||||
|
df_clean.head(10)
|
||||||
20
extracted_cells/cell9.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Kategorische Variablen in numerische umwandeln
|
||||||
|
categorical_cols = df_clean.select_dtypes(include=['object']).columns
|
||||||
|
print("Kategorische Spalten:", categorical_cols.tolist())
|
||||||
|
|
||||||
|
# Label Encoding für die Zielvariable
|
||||||
|
label_encoder = LabelEncoder()
|
||||||
|
df_clean['income_encoded'] = label_encoder.fit_transform(df_clean['income'])
|
||||||
|
print("\nLabel Encoding für 'income':")
|
||||||
|
for i, label in enumerate(label_encoder.classes_):
|
||||||
|
print(f"{label} -> {i}")
|
||||||
|
|
||||||
|
# One-Hot Encoding für kategorische Variablen (außer der Zielvariable)
|
||||||
|
categorical_cols = categorical_cols.drop('income')
|
||||||
|
df_encoded = pd.get_dummies(df_clean, columns=categorical_cols, drop_first=False)
|
||||||
|
|
||||||
|
|
||||||
|
print("\nNeue Spalten durch One-Hot Encoding:")
|
||||||
|
print(df_encoded.columns[:10].tolist())
|
||||||
|
|
||||||
|
print("\nDatensatz nach Vorverarbeitung:", df_encoded.shape)
|
||||||
BIN
output/Confusions_Matrix.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
output/Feature_Importance.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
output/LIME_Erklaerung_mit_Tabelle.png
Normal file
|
After Width: | Height: | Size: 279 KiB |
BIN
output/LIME_Erklärung.png
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
output/LIME_Genauigkeit_Features.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
output/LIME_Vergleihc_Vorhersage.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
output/Lime_Varianz_Features.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
output/Random_Forest_Tree_Example.png
Normal file
|
After Width: | Height: | Size: 851 KiB |
BIN
output/Verteilung_Einkommensklassen.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
output/daten_uebersicht.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
output/lime_accuracy.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
output/lime_accuracy_vs_features.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
output/lime_consistency.png
Normal file
|
After Width: | Height: | Size: 230 KiB |
BIN
output/lime_feature_importance.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
output/lime_fidelity.png
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
output/lime_stability.png
Normal file
|
After Width: | Height: | Size: 390 KiB |
BIN
output/surrogate_accuracy.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
output/surrogate_accuracy_consistency.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
output/surrogate_feature_consistency.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
BIN
outputlime_prediction_comparison.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
32562
sample_data/adult_census_income/adult.csv
Normal file
13
slides/_extensions/grantmcdermott/clean/_extension.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
title: clean
|
||||||
|
author: Grant McDermott
|
||||||
|
version: 1.3.0
|
||||||
|
quarto-required: ">=1.3.0"
|
||||||
|
contributes:
|
||||||
|
formats:
|
||||||
|
revealjs:
|
||||||
|
theme: [default, clean.scss]
|
||||||
|
menu:
|
||||||
|
side: left
|
||||||
|
slide-number: true
|
||||||
|
date-format: long
|
||||||
|
|
||||||
351
slides/_extensions/grantmcdermott/clean/clean.scss
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
/*-- scss:defaults --*/
|
||||||
|
|
||||||
|
// Custom colours and variables
|
||||||
|
|
||||||
|
$jet: #131516;
|
||||||
|
$accent: #107895;
|
||||||
|
$accent2: #9a2515;
|
||||||
|
// $accent2: #e64173;
|
||||||
|
$right-arrow: "\2192"; // Unicode character for right arrow
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note: This theme uses the Roboto font family, which it imports from Google
|
||||||
|
Fonts to ensure consistent weighting in addition to availability. While
|
||||||
|
you can use a local installation of Roboto, this is generally not
|
||||||
|
recommended since the weighting will likely be wrong (probably too
|
||||||
|
light). OTOH, importing from Google Fonts can cause some issues in
|
||||||
|
certain secure environments due the external CDN (see:
|
||||||
|
https://github.com/grantmcdermott/quarto-revealjs-clean/issues/7). If
|
||||||
|
that's the case for you, simply comment out the `@import url(...)` line
|
||||||
|
below and it will default for the default Sans Serif font on your system
|
||||||
|
(e.g., Helvetica on a Mac). Circling back to the earlier point about
|
||||||
|
preserving consistent font weights, you may also wish to remove "Roboto"
|
||||||
|
from the choice set if the family is installed locally.
|
||||||
|
*/
|
||||||
|
@import url('https://fonts.googleapis.com/css?family=Roboto:200,200i,300,300i,350,350i,400,400i&display=swap');
|
||||||
|
|
||||||
|
$font-family-sans-serif: "Roboto", sans-serif !default;
|
||||||
|
$presentation-heading-font: "Roboto", sans-serif !default;
|
||||||
|
|
||||||
|
$presentation-heading-color: $jet !default;
|
||||||
|
$presentation-heading-font-weight: lighter;
|
||||||
|
//$presentation-heading-line-height: 2;
|
||||||
|
//$presentation-block-margin: 28px;
|
||||||
|
$presentation-font-size-root: 32px;
|
||||||
|
|
||||||
|
// colors
|
||||||
|
//$body-bg: #f0f1eb !default;
|
||||||
|
$body-color: $jet !default;
|
||||||
|
$link-color: $accent !default;
|
||||||
|
$selection-bg: #26351c !default;
|
||||||
|
|
||||||
|
|
||||||
|
/*-- scss:rules --*/
|
||||||
|
|
||||||
|
.reveal a {
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal p {
|
||||||
|
// font-weight: 300;
|
||||||
|
font-weight: lighter;
|
||||||
|
margin-top: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// title and headings
|
||||||
|
|
||||||
|
#title-slide {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: $body-color;
|
||||||
|
font-size: 1.4em;
|
||||||
|
// font-weight: 350;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: $accent;
|
||||||
|
font-style: italic;
|
||||||
|
margin-top: 0em;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.institute,
|
||||||
|
.quarto-title-affiliation,
|
||||||
|
.quarto-title-author-email {
|
||||||
|
font-style: italic;
|
||||||
|
// font-size: 80%;
|
||||||
|
// color: #7F7F7F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author,
|
||||||
|
.quarto-title-author-name {
|
||||||
|
color: $body-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quarto-title-authors {
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
|
||||||
|
.quarto-title-author {
|
||||||
|
padding-left: 0em;
|
||||||
|
padding-right: 0em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.reveal h2 {
|
||||||
|
// font-weight: 350;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal h3 {
|
||||||
|
color: $accent;
|
||||||
|
font-style: italic;
|
||||||
|
// font-weight: 350;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal h4 {
|
||||||
|
color: $accent2;
|
||||||
|
// font-weight: 350;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-top: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// alerts etc.
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
color: $accent2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fg {
|
||||||
|
color: var(--col, $jet);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
background-color: var(--col, #fff);
|
||||||
|
padding: 0.1em;
|
||||||
|
border-radius: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lists
|
||||||
|
|
||||||
|
// Unordered lists
|
||||||
|
|
||||||
|
.reveal ul {
|
||||||
|
// font-weight: 300;
|
||||||
|
font-weight: lighter;
|
||||||
|
padding-left: 16px;
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
color: mix($accent, white, 70%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal ul ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li:before {
|
||||||
|
content: $right-arrow;
|
||||||
|
color: mix($accent, white, 60%);
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
margin-left: -1em;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ordered lists
|
||||||
|
|
||||||
|
.reveal ol {
|
||||||
|
// font-weight: 300;
|
||||||
|
font-weight: lighter;
|
||||||
|
padding-left: 16px;
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move "hamburger" menu button to top right
|
||||||
|
|
||||||
|
.reveal .slide-menu-button {
|
||||||
|
position: fixed;
|
||||||
|
top: 6px;
|
||||||
|
right: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-start;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .slide-menu-button > * {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same for chalkboard buttons (with an offset)
|
||||||
|
|
||||||
|
.reveal .slide-chalkboard-buttons {
|
||||||
|
position: fixed;
|
||||||
|
top: 12px;
|
||||||
|
right: 24px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-start;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .slide-chalkboard-buttons > * {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logo to the bottom-left
|
||||||
|
.slide-logo {
|
||||||
|
display: block !important;
|
||||||
|
position: fixed !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
left: 10px !important;
|
||||||
|
max-width: 150px; // Adjust if necessary
|
||||||
|
max-height: 50px;
|
||||||
|
width: auto !important;
|
||||||
|
color: $body-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also need to enforce slide numbers at bottom-right (if logo is present)
|
||||||
|
.slide-number, .reveal.has-logo .slide-number {
|
||||||
|
bottom: 6px !important;
|
||||||
|
right: 10px !important;
|
||||||
|
top: unset !important;
|
||||||
|
color: #777777 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Beamer-style button link environment
|
||||||
|
|
||||||
|
.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 12px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.42857143;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: $accent;
|
||||||
|
border: 1px solid $accent;
|
||||||
|
color: #fff !important;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
border-color: #0056b3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button::before {
|
||||||
|
content: "▶";
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tables
|
||||||
|
|
||||||
|
.reveal table {
|
||||||
|
// height: auto; /* Adjust table width to fit content up to the available slide space */
|
||||||
|
margin: auto;
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal table th,
|
||||||
|
.reveal table td {
|
||||||
|
border: none; /* Remove internal row lines */
|
||||||
|
padding: .23em; /* Adjust padding as needed */
|
||||||
|
text-align: left; /* Adjust text alignment as needed */
|
||||||
|
font-weight: lighter; /* Lighter font weight for main table text */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Adds a bottom border to the table header row for distinction */
|
||||||
|
.reveal table thead th,
|
||||||
|
.reveal .slides table tr:last-child td,
|
||||||
|
.reveal .slides table {
|
||||||
|
border-bottom: 2px solid #D3D3D3; /* Dark grey color for the bottom border */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make column headers bold */
|
||||||
|
.reveal table thead th {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Styling table captions */
|
||||||
|
.reveal table caption {
|
||||||
|
color: #666666; /* Dark grey color for the caption */
|
||||||
|
font-variant: small-caps; /* Use small caps for the caption text */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special catch for etable environment to ensure these table images
|
||||||
|
// don't overflow the slide.
|
||||||
|
// See: https://lrberge.github.io/fixest/articles/etable_new_features.html
|
||||||
|
|
||||||
|
.etable {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 3em); /* Adjust 3em based on the height of your header, if necessary */
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etable img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the relative widths of `output-location: column`.
|
||||||
|
// See: https://github.com/grantmcdermott/quarto-revealjs-clean/pull/16
|
||||||
|
// Example usage:
|
||||||
|
// ```{python}
|
||||||
|
// #| echo: true
|
||||||
|
// #| output-location: column
|
||||||
|
// #| classes: columns3070
|
||||||
|
// <code>
|
||||||
|
// ```
|
||||||
|
.reveal .columns3070 > div.column:first-child {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
.reveal .columns3070 div.column:not(:first-child) {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
.reveal .columns7030 > div.column:first-child {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
.reveal .columns7030 div.column:not(:first-child) {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
.reveal .columns4060 > div.column:first-child {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
|
.reveal .columns4060 div.column:not(:first-child) {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.reveal .columns6040 > div.column:first-child {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.reveal .columns6040 div.column:not(:first-child) {
|
||||||
|
width: 40%;
|
||||||
|
}
|
||||||
7
slides/_extensions/metropolis-theme/_extension.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
title: Quarto Metropolis Theme
|
||||||
|
author: Patrick Schratz
|
||||||
|
version: 1.0.0
|
||||||
|
contributes:
|
||||||
|
formats:
|
||||||
|
revealjs:
|
||||||
|
theme: metropolis.scss
|
||||||
218
slides/_extensions/metropolis-theme/metropolis.scss
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
/*-- scss:defaults --*/
|
||||||
|
|
||||||
|
// fonts
|
||||||
|
@import url(https://fonts.googleapis.com/css?family=Fira+Sans:300,300i,400,400i,500,500i,700,700i);
|
||||||
|
@import url(https://cdn.rawgit.com/tonsky/FiraCode/1.204/distr/fira_code.css);
|
||||||
|
@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|JetBrains+Mono&display=swap");
|
||||||
|
@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap");
|
||||||
|
|
||||||
|
$font-family-sans-serif: "Roboto", "Fira Sans", "Droid Serif", serif !default;
|
||||||
|
$font-family-monospace: "JetBrains Mono", "Fira Code", monospace;
|
||||||
|
$presentation-font-size-root: 32px;
|
||||||
|
$presentation-line-height: 1.5em;
|
||||||
|
$presentation-heading-font-weight: 400;
|
||||||
|
|
||||||
|
// colors
|
||||||
|
$body-bg: #fafafa !default;
|
||||||
|
$body-color: #000 !default;
|
||||||
|
// $link-color: #EB811B !default;
|
||||||
|
$selection-bg: #26351c;
|
||||||
|
|
||||||
|
// headings
|
||||||
|
// $presentation-heading-font: "Palatino Linotype", "Book Antiqua", Palatino,
|
||||||
|
// FreeSerif, serif !default;
|
||||||
|
// $presentation-heading-color: #383d3d !default;
|
||||||
|
|
||||||
|
/*-- scss:rules --*/
|
||||||
|
|
||||||
|
.reveal a {
|
||||||
|
line-height: 1.5em;
|
||||||
|
color: #eb811b;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .footer a {
|
||||||
|
color: #eb811b !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal p {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .slide ul li,
|
||||||
|
.reveal .slide ol li {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maximum height of code blocks before scrolling is used
|
||||||
|
.reveal pre.sourceCode code {
|
||||||
|
max-height: 700px; // default 500
|
||||||
|
}
|
||||||
|
|
||||||
|
// title slide
|
||||||
|
.title-slide {
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-top: 80px solid #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1.title {
|
||||||
|
color: #1a292c;
|
||||||
|
font-size: 45px;
|
||||||
|
text-shadow: none;
|
||||||
|
font-weight: 400;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 15px;
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
p.subtitle {
|
||||||
|
// margin-top: -10px;
|
||||||
|
// padding-bottom: -20px;
|
||||||
|
color: #1a292c;
|
||||||
|
text-shadow: none;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 40px;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
p.author {
|
||||||
|
color: #1a292c;
|
||||||
|
text-shadow: none;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 30px;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.date {
|
||||||
|
color: #1a292c;
|
||||||
|
text-shadow: none;
|
||||||
|
font-weight: 300;
|
||||||
|
font-size: 30px;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 15px;
|
||||||
|
// margin-bottom: -30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.subtitle:after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
border: none;
|
||||||
|
background-color: #eb811b;
|
||||||
|
color: #eb811b;
|
||||||
|
height: 1px;
|
||||||
|
margin: 25px 0 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section break slide
|
||||||
|
hr,
|
||||||
|
h1::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
border: none;
|
||||||
|
background-color: #eb811b;
|
||||||
|
color: #eb811b;
|
||||||
|
height: 1px;
|
||||||
|
margin: 1em 10px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override h1 style for title slide (remove section break slide style)
|
||||||
|
hr,
|
||||||
|
h1.title::after {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent !important;
|
||||||
|
color: transparent !important;
|
||||||
|
height: 0px;
|
||||||
|
margin: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2::after.title {
|
||||||
|
margin: 10px 15px 35px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .slide-number a {
|
||||||
|
font-size: 120%;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline
|
||||||
|
.reveal code {
|
||||||
|
font-size: 70%;
|
||||||
|
background-color: #afb8c133;
|
||||||
|
color: #000;
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code blocks
|
||||||
|
.reveal div.sourceCode pre code {
|
||||||
|
font-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// code output
|
||||||
|
.reveal pre code {
|
||||||
|
font-size: 100%;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colored-column {
|
||||||
|
border: 2px solid red;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
// #column;
|
||||||
|
// border: 2px solid red;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
// background-color: #ededed;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal h2 {
|
||||||
|
background-color: #23373b;
|
||||||
|
padding: 5px 0px 5px 10px;
|
||||||
|
color: #fafafa;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-font {
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
display: block;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
.reveal .slide-menu-button .fa-bars::before {
|
||||||
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(35, 55, 59)" class="bi bi-list" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .slide-chalkboard-buttons .fa-easel2::before {
|
||||||
|
padding-bottom: 6px;
|
||||||
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(35, 55, 59)" class="bi bi-easel2" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 0a.5.5 0 0 1 .447.276L8.81 1h4.69A1.5 1.5 0 0 1 15 2.5V11h.5a.5.5 0 0 1 0 1h-2.86l.845 3.379a.5.5 0 0 1-.97.242L12.11 14H3.89l-.405 1.621a.5.5 0 0 1-.97-.242L3.36 12H.5a.5.5 0 0 1 0-1H1V2.5A1.5 1.5 0 0 1 2.5 1h4.691l.362-.724A.5.5 0 0 1 8 0ZM2 11h12V2.5a.5.5 0 0 0-.5-.5h-11a.5.5 0 0 0-.5.5V11Zm9.61 1H4.39l-.25 1h7.72l-.25-1Z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .slide-chalkboard-buttons .fa-brush::before {
|
||||||
|
padding-bottom: 6px;
|
||||||
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="rgb(35, 55, 59)" class="bi bi-brush" viewBox="0 0 16 16"><path d="M15.825.12a.5.5 0 0 1 .132.584c-1.53 3.43-4.743 8.17-7.095 10.64a6.067 6.067 0 0 1-2.373 1.534c-.018.227-.06.538-.16.868-.201.659-.667 1.479-1.708 1.74a8.118 8.118 0 0 1-3.078.132 3.659 3.659 0 0 1-.562-.135 1.382 1.382 0 0 1-.466-.247.714.714 0 0 1-.204-.288.622.622 0 0 1 .004-.443c.095-.245.316-.38.461-.452.394-.197.625-.453.867-.826.095-.144.184-.297.287-.472l.117-.198c.151-.255.326-.54.546-.848.528-.739 1.201-.925 1.746-.896.126.007.243.025.348.048.062-.172.142-.38.238-.608.261-.619.658-1.419 1.187-2.069 2.176-2.67 6.18-6.206 9.117-8.104a.5.5 0 0 1 .596.04zM4.705 11.912a1.23 1.23 0 0 0-.419-.1c-.246-.013-.573.05-.879.479-.197.275-.355.532-.5.777l-.105.177c-.106.181-.213.362-.32.528a3.39 3.39 0 0 1-.76.861c.69.112 1.736.111 2.657-.12.559-.139.843-.569.993-1.06a3.122 3.122 0 0 0 .126-.75l-.793-.792zm1.44.026c.12-.04.277-.1.458-.183a5.068 5.068 0 0 0 1.535-1.1c1.9-1.996 4.412-5.57 6.052-8.631-2.59 1.927-5.566 4.66-7.302 6.792-.442.543-.795 1.243-1.042 1.826-.121.288-.214.54-.275.72v.001l.575.575zm-4.973 3.04.007-.005a.031.031 0 0 1-.007.004zm3.582-3.043.002.001h-.002z"/></svg>');
|
||||||
|
}
|
||||||
|
|
||||||
|
.reveal .progress {
|
||||||
|
color: #23373b;
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
title: Include Code Files
|
||||||
|
author: Bruno Beaufils
|
||||||
|
version: 1.0.0
|
||||||
|
quarto-required: ">=1.2"
|
||||||
|
contributes:
|
||||||
|
filters:
|
||||||
|
- include-code-files.lua
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,130 @@
|
|||||||
|
--- include-code-files.lua – filter to include code from source files
|
||||||
|
---
|
||||||
|
--- Copyright: © 2020 Bruno BEAUFILS
|
||||||
|
--- License: MIT – see LICENSE file for details
|
||||||
|
|
||||||
|
--- Dedent a line
|
||||||
|
local function dedent(line, n)
|
||||||
|
return line:sub(1, n):gsub(" ", "") .. line:sub(n + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find snippet start and end.
|
||||||
|
--
|
||||||
|
-- Use this to populate startline and endline.
|
||||||
|
-- This should work like pandocs snippet functionality: https://github.com/owickstrom/pandoc-include-code/tree/master
|
||||||
|
local function snippet(cb, fh)
|
||||||
|
if not cb.attributes.snippet then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cannot capture enum: http://lua-users.org/wiki/PatternsTutorial
|
||||||
|
local comment
|
||||||
|
local comment_stop = ""
|
||||||
|
if
|
||||||
|
string.match(cb.attributes.include, ".py$")
|
||||||
|
or string.match(cb.attributes.include, ".jl$")
|
||||||
|
or string.match(cb.attributes.include, ".r$")
|
||||||
|
then
|
||||||
|
comment = "#"
|
||||||
|
elseif string.match(cb.attributes.include, ".o?js$") or string.match(cb.attributes.include, ".css$") then
|
||||||
|
comment = "//"
|
||||||
|
elseif string.match(cb.attributes.include, ".lua$") then
|
||||||
|
comment = "--"
|
||||||
|
elseif string.match(cb.attributes.include, ".html$") then
|
||||||
|
comment = "<!%-%-"
|
||||||
|
comment_stop = " *%-%->"
|
||||||
|
else
|
||||||
|
-- If not known assume that it is something one or two long and not alphanumeric.
|
||||||
|
comment = "%W%W?"
|
||||||
|
end
|
||||||
|
|
||||||
|
local p_start = string.format("^ *%s start snippet %s%s", comment, cb.attributes.snippet, comment_stop)
|
||||||
|
local p_stop = string.format("^ *%s end snippet %s%s", comment, cb.attributes.snippet, comment_stop)
|
||||||
|
local start, stop = nil, nil
|
||||||
|
|
||||||
|
-- Cannot use pairs.
|
||||||
|
local line_no = 1
|
||||||
|
for line in fh:lines() do
|
||||||
|
if start == nil then
|
||||||
|
if string.match(line, p_start) then
|
||||||
|
start = line_no + 1
|
||||||
|
end
|
||||||
|
elseif stop == nil then
|
||||||
|
if string.match(line, p_stop) then
|
||||||
|
stop = line_no - 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
line_no = line_no + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Reset so nothing is broken later on.
|
||||||
|
fh:seek("set")
|
||||||
|
|
||||||
|
-- If start and stop not found, just continue
|
||||||
|
if start == nil or stop == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
cb.attributes.startLine = tostring(start)
|
||||||
|
cb.attributes.endLine = tostring(stop)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Filter function for code blocks
|
||||||
|
local function transclude(cb)
|
||||||
|
if cb.attributes.include then
|
||||||
|
local content = ""
|
||||||
|
local fh = io.open(cb.attributes.include)
|
||||||
|
if not fh then
|
||||||
|
io.stderr:write("Cannot open file " .. cb.attributes.include .. " | Skipping includes\n")
|
||||||
|
else
|
||||||
|
local number = 1
|
||||||
|
local start = 1
|
||||||
|
|
||||||
|
-- change hyphenated attributes to PascalCase
|
||||||
|
for i, pascal in pairs({ "startLine", "endLine" }) do
|
||||||
|
local hyphen = pascal:gsub("%u", "-%0"):lower()
|
||||||
|
if cb.attributes[hyphen] then
|
||||||
|
cb.attributes[pascal] = cb.attributes[hyphen]
|
||||||
|
cb.attributes[hyphen] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Overwrite startLine and stopLine with the snippet if any.
|
||||||
|
snippet(cb, fh)
|
||||||
|
|
||||||
|
if cb.attributes.startLine then
|
||||||
|
cb.attributes.startFrom = cb.attributes.startLine
|
||||||
|
start = tonumber(cb.attributes.startLine)
|
||||||
|
end
|
||||||
|
|
||||||
|
for line in fh:lines("L") do
|
||||||
|
if cb.attributes.dedent then
|
||||||
|
line = dedent(line, cb.attributes.dedent)
|
||||||
|
end
|
||||||
|
if number >= start then
|
||||||
|
if not cb.attributes.endLine or number <= tonumber(cb.attributes.endLine) then
|
||||||
|
content = content .. line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
number = number + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
fh:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- remove key-value pair for used keys
|
||||||
|
cb.attributes.include = nil
|
||||||
|
cb.attributes.startLine = nil
|
||||||
|
cb.attributes.endLine = nil
|
||||||
|
cb.attributes.dedent = nil
|
||||||
|
|
||||||
|
-- return final code block
|
||||||
|
return pandoc.CodeBlock(content, cb.attr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
{ CodeBlock = transclude },
|
||||||
|
}
|
||||||
68
slides/custom.css
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/* Benutzerdefinierte Schriftart für die gesamte Präsentation */
|
||||||
|
.reveal {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Überschriften */
|
||||||
|
.reveal h1,
|
||||||
|
.reveal h2
|
||||||
|
{
|
||||||
|
padding-bottom: 20px;
|
||||||
|
font-size: 58px;
|
||||||
|
}
|
||||||
|
.reveal h3
|
||||||
|
{
|
||||||
|
font-size: 38px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.reveal h4
|
||||||
|
{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.reveal h5
|
||||||
|
{
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
.reveal h6 {
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text */
|
||||||
|
.reveal p {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tabellen */
|
||||||
|
.reveal li {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code-Blöcke */
|
||||||
|
.reveal .sourceCode {
|
||||||
|
font-size: 17px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tabellen */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 1px solid black;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #63bdc2;
|
||||||
|
border: 1px solid black;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1px solid black;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Importiere die Schriftarten von Google Fonts */
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&family=Montserrat:wght@500;600&family=Fira+Code&display=swap');
|
||||||
BIN
slides/img/Anwendungszwecke.jpg
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
slides/img/Confusions_Matrix.png
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
slides/img/Feature_Importance.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
slides/img/Komplett.bmp
Normal file
|
After Width: | Height: | Size: 13 MiB |
BIN
slides/img/LIME_Erklärung.png
Normal file
|
After Width: | Height: | Size: 266 KiB |
BIN
slides/img/LIME_Erklärung_Tabelle.PNG
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
slides/img/LIME_Genauigkeit_Features.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
slides/img/LIME_Vergleich_Vorhersage.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
slides/img/LIME_education.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
slides/img/LIME_fnlwgt.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
slides/img/Lime_Varianz_Features.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
slides/img/Nachteile_gespiegelt.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
slides/img/Neue_Regel.bmp
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
slides/img/Nur Input.bmp
Normal file
|
After Width: | Height: | Size: 6.1 MiB |
BIN
slides/img/Random_Forest_Tree_Example.png
Normal file
|
After Width: | Height: | Size: 851 KiB |
BIN
slides/img/Verteilung_Einkommensklassen.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
slides/img/Vorteile.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
slides/img/surrogate_accuracy.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
2150
slides/slides.html
Normal file
914
slides/slides.qmd
Normal file
@ -0,0 +1,914 @@
|
|||||||
|
---
|
||||||
|
title: "Explainable AI"
|
||||||
|
subtitle: "LIME & RBE auf Random Forest <br> mit Adult Census Datensatz"
|
||||||
|
filters:
|
||||||
|
- include-code-files
|
||||||
|
date: today
|
||||||
|
date-format: "D.M.YYYY"
|
||||||
|
format:
|
||||||
|
revealjs:
|
||||||
|
incremental: true
|
||||||
|
slide-number: c
|
||||||
|
css: custom.css
|
||||||
|
footer: "Mario, Jan, Nick, Andi, Sebastian, Paul | TINF22B3 | Explainable AI "
|
||||||
|
---
|
||||||
|
|
||||||
|
# Datensatz
|
||||||
|
Analyse der Daten
|
||||||
|
|
||||||
|
## Adult Census Datensatz
|
||||||
|
|
||||||
|
- Enthält demografische Informationen aus der US-Volkszählung
|
||||||
|
- Soll Einkommen einer Person vorhersagen (<|>50000$ pro Jahr)
|
||||||
|
|
||||||
|
|
||||||
|
## Datensatzauswahl
|
||||||
|
- Gut strukturiert und saubere Daten
|
||||||
|
- Weit verbreiteter Benchmark in der Forschung und im maschinellen Lernen --> wird in vielen Studien verwendet
|
||||||
|
- Vielfalt der Variablen Typen --> enthält sowohl kategorische als auch numerische Variablen
|
||||||
|
- Realistisches Datenset --> gut geeignet für praxisnahe Anwendungen
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
1. Gut strukturierte und saubere Daten
|
||||||
|
Der Datensatz enthält strukturierte, tabellarische Daten mit klar definierten Spalten.
|
||||||
|
Fehlende Werte sind begrenzt, sodass weniger Datenbereinigung erforderlich ist.
|
||||||
|
2. Klassifikationsproblem für Machine Learning
|
||||||
|
Zielvariable: Einkommen (über oder unter 50.000 USD jährlich).
|
||||||
|
Ideal für binäre Klassifikationsmodelle (z. B. Logistic Regression, Decision Trees, Random Forests, Neural Networks).
|
||||||
|
3. Vielfältige Feature-Typen
|
||||||
|
Beinhaltet sowohl kategorische (z. B. Beruf, Geschlecht, Bildungsniveau) als auch numerische (z. B. Alter, Arbeitsstunden pro Woche) Variablen.
|
||||||
|
Ermöglicht das Testen verschiedener Feature-Engineering-Methoden.
|
||||||
|
4. Ein realistisches Datenset
|
||||||
|
Reale demografische und sozioökonomische Daten aus der US-Volkszählung (1994).
|
||||||
|
Praktisch für praxisnahe Anwendungen wie Sozialforschung, Wirtschaftsanalyse oder politische Entscheidungsfindung.
|
||||||
|
5. Gut dokumentiert & weit verbreitet
|
||||||
|
Häufig verwendet in Tutorials, Forschungsarbeiten und Kaggle-Wettbewerben.
|
||||||
|
Viele Ressourcen und Beispielcodes verfügbar.
|
||||||
|
6. Ideal für Fairness- und Bias-Analysen
|
||||||
|
Der Datensatz enthält sensible Merkmale wie Geschlecht und Ethnie.
|
||||||
|
Eignet sich für Studien zur Diskriminierung und Fairness von Algorithmen.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Enthaltene Daten im Datensatz {.smaller}
|
||||||
|
|
||||||
|
| Column Name | Data Type |
|
||||||
|
|------------|-----------|
|
||||||
|
| age | int64 |
|
||||||
|
| workclass | object |
|
||||||
|
| fnlwgt | int64 |
|
||||||
|
| education | object |
|
||||||
|
| education.num | int64 |
|
||||||
|
| marital.status | object |
|
||||||
|
| occupation | object |
|
||||||
|
| relationship | object |
|
||||||
|
| race | object |
|
||||||
|
| sex | object |
|
||||||
|
| capital.gain | int64 |
|
||||||
|
| capital.loss | int64 |
|
||||||
|
| hours.per.week | int64 |
|
||||||
|
| native.country | object |
|
||||||
|
| income | object |
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
1. age (numerisch)
|
||||||
|
Alter der Person (in Jahren).
|
||||||
|
2. workclass (kategorisch)
|
||||||
|
Art der Anstellung der Person.
|
||||||
|
Private (privatwirtschaftlich)
|
||||||
|
Self-emp-not-inc (selbstständig, aber nicht als Unternehmen)
|
||||||
|
Self-emp-inc (selbstständig mit Unternehmen)
|
||||||
|
Government (staatliche Anstellung: Local, State, Federal)
|
||||||
|
Without-pay (ohne Bezahlung)
|
||||||
|
Never-worked (nie gearbeitet)
|
||||||
|
3. fnlwgt (numerisch)
|
||||||
|
Final weight: Gewichtungsfaktor für Hochrechnungen in der Volkszählung -> gibt an wie viele Menschen mit ähnlichen Merkmalen in der Gesamtbevölkerung durch diese einzelne Person repräsentiert werden
|
||||||
|
Wird meist für ML-Modelle nicht genutzt. --> für unseren Datensatz normalerweise nicht wichtig aber da wir eine Gewichtung machen
|
||||||
|
4. education (kategorisch)
|
||||||
|
Bachelors (Bachelor-Abschluss)
|
||||||
|
Masters (Master-Abschluss)
|
||||||
|
Doctorate (Promotion)
|
||||||
|
5. education.num (numerisch)
|
||||||
|
9 (High School)
|
||||||
|
16 (Promotion)
|
||||||
|
6. marital.status (kategorisch)
|
||||||
|
Familienstand der Person.
|
||||||
|
Married (verheiratet)
|
||||||
|
Divorced (geschieden)
|
||||||
|
Never-married (nie verheiratet)
|
||||||
|
Separated (getrennt lebend)
|
||||||
|
Widowed (verwitwet)
|
||||||
|
7. occupation (kategorisch)
|
||||||
|
Beruf der Person.
|
||||||
|
8. relationship (kategorisch)
|
||||||
|
Beziehung zur Familie im Haushalt.
|
||||||
|
Husband (Ehemann)
|
||||||
|
Wife (Ehefrau)
|
||||||
|
Own-child (eigenes Kind)
|
||||||
|
Unmarried (unverheiratet)
|
||||||
|
9. race (kategorisch)
|
||||||
|
Ethnische Zugehörigkeit der Person.
|
||||||
|
White (weiß)
|
||||||
|
Black (schwarz)
|
||||||
|
Asian-Pac-Islander (asiatisch-pazifische Herkunft)
|
||||||
|
10. sex (kategorisch)
|
||||||
|
Geschlecht der Person.
|
||||||
|
Werte: Male (männlich), Female (weiblich)
|
||||||
|
11. capital.gain (numerisch)
|
||||||
|
Kapitalgewinne (z. B. durch Investitionen).
|
||||||
|
12. capital.loss (numerisch)
|
||||||
|
Kapitalverluste (z. B. aus Investitionen).
|
||||||
|
13. hours.per.week (numerisch)
|
||||||
|
Durchschnittliche Arbeitsstunden pro Woche.
|
||||||
|
14. native.country (kategorisch)
|
||||||
|
Herkunftsland der Person.
|
||||||
|
15. income (Zielvariable, kategorisch)
|
||||||
|
Einkommensklasse der Person.
|
||||||
|
<=50K (Jahreseinkommen ≤50.000 USD)
|
||||||
|
>50K (Jahreseinkommen >50.000 USD)
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Analyse des Datensatzes {.smaller}
|
||||||
|
- Datensatzgröße
|
||||||
|
- Anzahl und Art der Datentypen
|
||||||
|
- Fehlende Werte in den einzelnen Spalten
|
||||||
|
- Statistische Zusammenfasung
|
||||||
|
|
||||||
|
. . .
|
||||||
|
|
||||||
|
```{python}
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
# Daten einlesen
|
||||||
|
df = pd.read_csv("../sample_data/adult_census_income/adult.csv")
|
||||||
|
df.describe()
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Prozentuale Verteilung der Personen im Datensatz
|
||||||
|
|
||||||
|
{height=500 fig-align="center"}
|
||||||
|
|
||||||
|
|
||||||
|
# Datenvorverarbeitung
|
||||||
|
Auffüllen von fehlenden Werten, Umwandlung von Variablen
|
||||||
|
|
||||||
|
## Überprüfung auf fehlende oder ungültige Werte
|
||||||
|
|
||||||
|
```{python}
|
||||||
|
#| echo: true
|
||||||
|
# Überprüfen auf fehlende Werte oder '?'
|
||||||
|
for col in df.columns:
|
||||||
|
missing_count = df[df[col] == '?'].shape[0]
|
||||||
|
if missing_count > 0:
|
||||||
|
print(f"Spalte '{col}' hat {missing_count} Einträge mit '?'")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ersetzung von fehlenden/ ungültigen Werten (vorher) {.smaller}
|
||||||
|
```{python}
|
||||||
|
df.iloc[:5, :7]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ersetzung von fehlenden/ ungültigen Werten (nachher){.smaller}
|
||||||
|
|
||||||
|
```{python}
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# Ersetzen von '?' durch NaN und dann durch den häufigsten Wert
|
||||||
|
df_clean = df.copy()
|
||||||
|
|
||||||
|
for col in df_clean.columns:
|
||||||
|
if df_clean[col].dtype == 'object':
|
||||||
|
# Ersetze '?' durch NaN
|
||||||
|
df_clean[col] = df_clean[col].replace('?', np.nan)
|
||||||
|
|
||||||
|
# Ersetze NaN durch den häufigsten Wert
|
||||||
|
most_frequent = df_clean[col].mode()[0]
|
||||||
|
df_clean[col] = df_clean[col].fillna(most_frequent)
|
||||||
|
|
||||||
|
|
||||||
|
df_clean.iloc[:5, :7]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Umwandlung der Variablen
|
||||||
|
|
||||||
|
- Von Katergorischen Variablen zu Numerischen
|
||||||
|
- Beispiel Geschlecht:
|
||||||
|
- Mann -> 1
|
||||||
|
- Frau -> 0
|
||||||
|
|
||||||
|
- Wird benötigt um mit dem Datensatz weiter zu arbeiten zu können -> ML-Modelle benötigen numerische Werte um sinnvolle Berechnungen
|
||||||
|
durchführen zu können
|
||||||
|
|
||||||
|
|
||||||
|
# ML-Modell
|
||||||
|
Auswahl, Beschreibung, Hyperparameter, Güte
|
||||||
|
|
||||||
|
## Auswahl eines geeigneten Modells
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
| Kriterium | Unsere Situation |
|
||||||
|
|- |-- |
|
||||||
|
| Art der Daten | Strukturiert |
|
||||||
|
| Datenmenge | ~32.500 Einträge |
|
||||||
|
| Komplexität | 13-dimensionale Daten --> eher komplex |
|
||||||
|
| Ziel und Art der Klassifizierung | Binäre Klassifizierung (>/< 50.000$ Einkommen)|
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
--> Gute Voraussetzungen für **Random Forest**
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
Außerdem:
|
||||||
|
|
||||||
|
- Rechenressourcen
|
||||||
|
- Zeit für Training
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## Random Forest
|
||||||
|
|
||||||
|
- **Ensemble-Learning-Verfahren**, bestehend aus vielen Entscheidungsbaummodellen (bei uns 100 Bäume)
|
||||||
|
- **Training:** Jeder Baum wird auf zufälliger Teilmenge der Daten und zufälliger Teilmenge der Features trainiert
|
||||||
|
- **Vorhersagen:** Ergebnisse aller Bäume werden aggregiert, um eine finale Vorhersage zu erhalten
|
||||||
|
- Keine Aktivierungsfunktionen oder Layer
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
- **Ensemble Learning**: Mehrere verschiedene Lernalgorithmen, um bessere Ergebnisse zu erhalten als mit einem einzelnen Lernalgorithmus
|
||||||
|
- Gute Leistung bei verschiedenen Arten von Daten
|
||||||
|
- Robustheit gegenüber Overfitting
|
||||||
|
- Fähigkeit, mit fehlenden Werten und kategorischen Variablen umzugehen
|
||||||
|
- **Eingebaute Feature Importance**
|
||||||
|
- **Aktivierungsfunktionen:** Entscheidungsbäume, basieren auf Regeln und Splits, ohne mathematische Aktivierungsfunktionen.
|
||||||
|
- **Layers:** Es gibt keine Schichten (Layers) im klassischen Sinne, wie bei neuronalen Netzwerken. Jeder Baum ist ein eigenständiger Klassifikator.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Random Forest {background-image="img/Random_Forest_Tree_Example.png"}
|
||||||
|
|
||||||
|
|
||||||
|
## Hyperparameter
|
||||||
|
|
||||||
|
> *Parameter, die vor dem Training festgelegt werden müssen*
|
||||||
|
|
||||||
|
| Hyperparameter | Bedeutung |
|
||||||
|
|- |-- |
|
||||||
|
| n_estimators | Anzahl an Bäumen |
|
||||||
|
| max_depth | Maximale Tiefe eines Baumes |
|
||||||
|
| min_samples_split | Mindestanzahl an Samples, um ein Split durchzuführen |
|
||||||
|
| min_samples_leaf | Mindestanzahl an Samples in einem Blatt |
|
||||||
|
|
||||||
|
## Hyperparameter-Tuning {.smaller}
|
||||||
|
|
||||||
|
> *Optimierung der Hyperparameter, um die Modellgüte zu verbessern*
|
||||||
|
|
||||||
|
```{.python include="../extracted_cells/cell15.py"
|
||||||
|
code-line-numbers="|2-7|10-11|15"
|
||||||
|
end-line=15}
|
||||||
|
```
|
||||||
|
. . .
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
'max_depth': None,
|
||||||
|
'min_samples_leaf': 2,
|
||||||
|
'min_samples_split': 2,
|
||||||
|
'n_estimators': 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hyperparameter-Tuning {.smaller}
|
||||||
|
```{.python include="../extracted_cells/cell15.py"
|
||||||
|
code-line-numbers="2-7,10-11,15|18,21|24-27"
|
||||||
|
end-line=27}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Modellgüte {.smaller auto-animate=true}
|
||||||
|
:::{.notes}
|
||||||
|
- **Accuracy**: Anteil der korrekt klassifizierten Instanzen
|
||||||
|
- **Precision**: Anteil der als positiv klassifizierten Instanzen, die tatsächlich positiv sind
|
||||||
|
- **Recall**: Anteil der tatsächlich positiven Instanzen, die als positiv klassifiziert wurden
|
||||||
|
- **F1-Score**: Harmonisches Mittel aus Precision und Recall
|
||||||
|
:::
|
||||||
|
|
||||||
|
```{.json code-line-numbers="|2|3|4|5"}
|
||||||
|
{
|
||||||
|
'Accuracy': 0.8607,
|
||||||
|
'Precision': 0.7613,
|
||||||
|
'Recall': 0.6142,
|
||||||
|
'F1 Score': 0.6798
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
. . .
|
||||||
|
|
||||||
|
**Konfusionsmatrix**
|
||||||
|
|
||||||
|
. . .
|
||||||
|
|
||||||
|
{width=50%}
|
||||||
|
|
||||||
|
|
||||||
|
## Konfusionsmatrix {auto-animate=true}
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
- (oben links) Tatsächlich sind die meisten weniger als 50K und auch richtig vorhergesagt
|
||||||
|
- (unten rechts) Zweitgrößte Gruppe: Mehr als 50k und auch so vorhergesagt
|
||||||
|
- (unten links) Drittgrößte Gruppe: Eigentlich mehr als 50k aber als weniger vorhergesagt
|
||||||
|
- (oben rechts) Kleinste Gruppe: Tatsächlich weniger als 50k aber als mehr vorhergesagt
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Feature Importances
|
||||||
|
|
||||||
|
> *Metrik, wie viel ein Feature zur Vorhersage des Modells beiträgt*
|
||||||
|
|
||||||
|
{width=80%}
|
||||||
|
|
||||||
|
|
||||||
|
# LIME {visibility="uncounted"}
|
||||||
|
Local Interpretable Model-agnostic Explanations
|
||||||
|
|
||||||
|
## 1. Grundprinzip:
|
||||||
|
- LIME erklärt einzelne Vorhersagen eines beliebigen ML-Modells
|
||||||
|
- Es arbeitet modellunabhängig (model-agnostic)
|
||||||
|
- Erzeugt lokale, interpretierbare Erklärungen für einzelne Vorhersagen
|
||||||
|
|
||||||
|
## 2. Funktionsweise im Detail:
|
||||||
|
|
||||||
|
### Ausgangssituation:
|
||||||
|
|
||||||
|
- Ein trainiertes ML-Modell liegt vor
|
||||||
|
- Eine spezifische Vorhersage soll erklärt werden
|
||||||
|
- Das Original-Modell wird als "Black Box" behandelt
|
||||||
|
|
||||||
|
## 2. Funktionsweise im Detail:
|
||||||
|
|
||||||
|
### Prozessschritte:
|
||||||
|
|
||||||
|
- Sampling um den Datenpunkt:
|
||||||
|
- Erzeugt synthetische Samples in der Nachbarschaft des zu erklärenden Datenpunkts
|
||||||
|
- Verwendet Perturbationen (kleine Änderungen) der Original-Features
|
||||||
|
|
||||||
|
- Gewichtung der Samples:
|
||||||
|
- Näher liegende Samples erhalten höhere Gewichte
|
||||||
|
|
||||||
|
|
||||||
|
## 2. Funktionsweise im Detail:
|
||||||
|
|
||||||
|
### Prozessschritte:
|
||||||
|
|
||||||
|
- Feature-Transformation:
|
||||||
|
- Konvertiert die Daten in ein interpretierbares Format
|
||||||
|
- Bei Texten z.B. Umwandlung in binäre Features (Wort vorhanden/nicht vorhanden)
|
||||||
|
|
||||||
|
- Training eines einfachen Modells:
|
||||||
|
- typischer weise durch lineare Regression
|
||||||
|
- Verwendet die gewichteten Samples
|
||||||
|
- Optimiert auf lokale Genauigkeit
|
||||||
|
- damit man ein leicht Interpretierbares Modell bekommt
|
||||||
|
|
||||||
|
## 2. Funktionsweise im Detail:
|
||||||
|
|
||||||
|
Extraktion der Erklärung:
|
||||||
|
|
||||||
|
{width=80% fig-align="center"}
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
Hier sind zwei Beispiele wie die Erklärung von LIME extrahiert werden kann.
|
||||||
|
Einmal als Grafik durch ein Balkendiagramm und einmal als Tabelle.
|
||||||
|
Roter Bereich: Je größer der Rote Bereich, je geringer wird die Chance mehr als 50k zu bekommen
|
||||||
|
Grüner Bereich: Je größer der Grüne Bereich, je größer wird die Chance mehr als 50k zu bekommen
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 3. Wichtige Eigenschaften: {.smaller}
|
||||||
|
|
||||||
|
- Lokalität:
|
||||||
|
- Fokussiert sich auf lokale Umgebung der zu erklärenden Instanz
|
||||||
|
- Erzeugt keine globalen Erklärungen für das gesamte Modell
|
||||||
|
|
||||||
|
- Interpretierbarkeit:
|
||||||
|
- Nutzt einfache, verständliche Modelle für Erklärungen
|
||||||
|
- Meist lineare Modelle oder Entscheidungsbäume
|
||||||
|
|
||||||
|
- Modell-Agnostik:
|
||||||
|
- Funktioniert mit jedem ML-Modell
|
||||||
|
- Benötigt nur Zugriff auf Vorhersagefunktion
|
||||||
|
|
||||||
|
## 4. Vorteile und Grenzen: {.smaller}
|
||||||
|
:::{.notes}
|
||||||
|
LIME ist instabil, bedeuted das wenn man LIME zweimal ausführen würde, kann es sein das es nicht auf das gleiche Ergebnis kommt, sondern die Gewichtung anders verteilt.
|
||||||
|
:::
|
||||||
|
|
||||||
|
- Vorteile:
|
||||||
|
- Flexibel einsetzbar
|
||||||
|
- Intuitiv verständliche Erklärungen
|
||||||
|
- Unterstützt verschiedene Datentypen
|
||||||
|
|
||||||
|
- Grenzen:
|
||||||
|
- Nur lokale Erklärungen
|
||||||
|
- Sampling kann rechenintensiv sein
|
||||||
|
- LIME ist instabil
|
||||||
|
|
||||||
|
- LIME ist besonders nützlich, wenn man:
|
||||||
|
- Einzelne Vorhersagen verstehen möchten
|
||||||
|
- Mit komplexen Modellen arbeitet
|
||||||
|
- Modelle debuggen oder verbessern will
|
||||||
|
|
||||||
|
|
||||||
|
## LIME Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
**Medizinische Diagnostik und Gesundheitswesen**
|
||||||
|
|
||||||
|
- Krebsdiagnose: Erklärung, welche Merkmale in medizinischen Bildern zu einer Krebsdiagnose beitragen
|
||||||
|
:::
|
||||||
|
|
||||||
|
## LIME Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
**Medizinische Diagnostik und Gesundheitswesen**
|
||||||
|
|
||||||
|
- Krebsdiagnose: Erklärung, welche Merkmale in medizinischen Bildern zu einer Krebsdiagnose beitragen
|
||||||
|
|
||||||
|
**Finanzwesen und Kreditvergabe**
|
||||||
|
|
||||||
|
- Kreditwürdigkeitsprüfung: Transparente Begründung für Kreditablehnungen oder -genehmigungen
|
||||||
|
- Betrugserkennung: Erklärung, warum bestimmte Transaktionen als verdächtig eingestuft werden
|
||||||
|
:::
|
||||||
|
|
||||||
|
## LIME Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
**Medizinische Diagnostik und Gesundheitswesen**
|
||||||
|
|
||||||
|
- Krebsdiagnose: Erklärung, welche Merkmale in medizinischen Bildern zu einer Krebsdiagnose beitragen
|
||||||
|
|
||||||
|
**Finanzwesen und Kreditvergabe**
|
||||||
|
|
||||||
|
- Kreditwürdigkeitsprüfung: Transparente Begründung für Kreditablehnungen oder -genehmigungen
|
||||||
|
- Betrugserkennung: Erklärung, warum bestimmte Transaktionen als verdächtig eingestuft werden
|
||||||
|
|
||||||
|
**Personalabteilung und Recruiting**
|
||||||
|
|
||||||
|
- Bewerberselektion: Erklärung, welche Qualifikationen oder Fähigkeiten bei der Kandidatenauswahl entscheidend waren
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## LIME Berechnungskosten
|
||||||
|
|
||||||
|
LIME skaliert:
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Linear mit der Anzahl der Samples (N)
|
||||||
|
- Quadratisch bis kubisch mit der Anzahl der Features (D)
|
||||||
|
- Linear mit der Komplexität des zu erklärenden Modells M(D)
|
||||||
|
:::
|
||||||
|
|
||||||
|
Komplexität in BigO Notation:
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- **O(N × M(D))**, wenn das zu erklärende Modell komplex ist
|
||||||
|
- **O(N × D²)**, wenn das lineare Modell der rechenintensivste Teil ist
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
**Große Datensätze generell Machbar**
|
||||||
|
- Dürfen nur nicht zu viele Features haben, das erhöhe stark den Rechenaufwand
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Rule Based Explenation
|
||||||
|
|
||||||
|
{height=400 fig-align="center"}
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
- Verfahren wird gefüttert mit
|
||||||
|
- Daten und Labels
|
||||||
|
|
||||||
|
- Erstellt draus Regeln
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Detaillierte Erklärung und Funktionsweise
|
||||||
|
|
||||||
|
{height=400 fig-align="center"}
|
||||||
|
|
||||||
|
::: {.notes}
|
||||||
|
- Menschen lernen schneller als Tiere
|
||||||
|
- Menschen kommunizieren schneller / besser
|
||||||
|
- Vor allem auf Grund von Regeln
|
||||||
|
- Satzbau, Erklärungen, Logik
|
||||||
|
|
||||||
|
- Hund Sitz beibringen
|
||||||
|
- braucht viele iterationen (mit Belohnungen)
|
||||||
|
- Mensch kann deutlicher erklären was das Ziel ist
|
||||||
|
|
||||||
|
- Regel Basiertes Lernen kommt zum Einsatz
|
||||||
|
- Anstatt (unverständlicher) neuronaler Netze
|
||||||
|
- oder komplexen mathematischen Modellen
|
||||||
|
|
||||||
|
- Verwendung von logischen Rgeln
|
||||||
|
- extrahiert aus Daten oder durch Experten definiert
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Beispiel Regel
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
> WENN
|
||||||
|
|
||||||
|
> Fieber über 38°C
|
||||||
|
|
||||||
|
> UND Husten
|
||||||
|
|
||||||
|
> DANN Grippe
|
||||||
|
|
||||||
|
## Beispiel Regel {.text-align-right}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
> WENN
|
||||||
|
|
||||||
|
> capital.gain ≤ 7073.50 UND
|
||||||
|
|
||||||
|
> education.num ≤ 12.50 UND
|
||||||
|
|
||||||
|
> capital.loss > 2218.50 UND
|
||||||
|
|
||||||
|
> DANN Einkommen > 50K
|
||||||
|
|
||||||
|
## Vorteile
|
||||||
|
- Transparenz: Menschen können Entscheidungen leicht <br> nachvollziehen
|
||||||
|
- Überprüfbarkeit: Regeln können durch Experten überprüft <br> und Ausgewertet werden
|
||||||
|
- Anpassbarkeit: Vordefinierte Regeln von Experten können <br> mit maschinell erstellten Regeln ergänzt werden
|
||||||
|
|
||||||
|
|
||||||
|
{.absolute top=10 right=10 height="900"}
|
||||||
|
|
||||||
|
|
||||||
|
::: {.notes}
|
||||||
|
Transparenz: Menschen können Entscheidungen leicht nachvollziehen
|
||||||
|
Überprüfbarkeit: Regeln können durch Experten überprüft werden und Anpassungen vorgenommen werden
|
||||||
|
Anpassbarkeit: Vordefinierte Regeln von Experten können mit maschinell erstellten Regeln kombiniert werden
|
||||||
|
- (Modellunabhängig)
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Nachteile/Herausforderungen
|
||||||
|
- Skalierbarkeit: komplexen Anwendungsgebiete können eine hohe <br> Anzahl an Regeln schnell erreichen
|
||||||
|
- Gereralität: Regeln sind oft domänenspezifisch und benötigen <br> für unterschiedliche Kontexte entsprechend anpassungen
|
||||||
|
- Widersprüche: Regeln können zu Widersprüchen untereinander <br> führen und müssen daher angepasst werden
|
||||||
|
|
||||||
|
{.absolute top=10 right=10 height="900"}
|
||||||
|
|
||||||
|
::: {.notes}
|
||||||
|
- Skalierbarkeit: In komplexen Anwendungsgebieten können die Anzahl an Regeln schnell sehr hoch werden.
|
||||||
|
- Gereralität: Regeln sind oft domänenspezifisch und müssen für unterschiedliche Kontexte entsprechend angepasst werden
|
||||||
|
- Widersprüche: Regeln können zu Widersprüchen untereinander führen und müssen daher angepasst werden
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## RBE Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
**Medizinische Diagnostik und Gesundheitswesen**
|
||||||
|
|
||||||
|
- Diagnostische Systeme: Ärzte benötigen klare Regeln, um Diagnosevorschläge zu verstehen und zu validieren
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
Medizin / Gesundheitswesen
|
||||||
|
|
||||||
|
- **Regeln, warum welche Diagnose vorgeschlagen wurde**
|
||||||
|
|
||||||
|
Finanzwesen
|
||||||
|
|
||||||
|
- **Regeln, warum Transaktion verdächtig ist**
|
||||||
|
- **Regeln, warum Kredit genehmigt / abgelehnt wurde**
|
||||||
|
|
||||||
|
Versicherungswesen
|
||||||
|
|
||||||
|
- **Regeln, warum Prämien zu stande kommen**
|
||||||
|
- **Regeln, warum Schadensansprüche geltend gemacht werden können**
|
||||||
|
:::
|
||||||
|
|
||||||
|
## RBE Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
**Medizinische Diagnostik und Gesundheitswesen**
|
||||||
|
|
||||||
|
- Diagnostische Systeme: Ärzte benötigen klare Regeln, um Diagnosevorschläge zu verstehen und zu validieren
|
||||||
|
|
||||||
|
**Versicherungswesen**
|
||||||
|
|
||||||
|
- Risikobewertung: Transparente Regeln zur Prämienberechnung
|
||||||
|
- Schadenregulierung: Nachvollziehbare Entscheidungskriterien für Schadensansprüche
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
Medizin / Gesundheitswesen
|
||||||
|
|
||||||
|
- **Regeln, warum welche Diagnose vorgeschlagen wurde**
|
||||||
|
|
||||||
|
Finanzwesen
|
||||||
|
|
||||||
|
- **Regeln, warum Transaktion verdächtig ist**
|
||||||
|
- **Regeln, warum Kredit genehmigt / abgelehnt wurde**
|
||||||
|
|
||||||
|
Versicherungswesen
|
||||||
|
|
||||||
|
- **Regeln, warum Prämien zu stande kommen**
|
||||||
|
- **Regeln, warum Schadensansprüche geltend gemacht werden können**
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## RBE Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
**Medizinische Diagnostik und Gesundheitswesen**
|
||||||
|
|
||||||
|
- Diagnostische Systeme: Ärzte benötigen klare Regeln, um Diagnosevorschläge zu verstehen und zu validieren
|
||||||
|
|
||||||
|
**Versicherungswesen**
|
||||||
|
|
||||||
|
- Risikobewertung: Transparente Regeln zur Prämienberechnung
|
||||||
|
- Schadenregulierung: Nachvollziehbare Entscheidungskriterien für Schadensansprüche
|
||||||
|
|
||||||
|
**Finanzwesen und Kreditvergabe**
|
||||||
|
|
||||||
|
- Betrugserkennung: Einfach verständliche Regeln zur Identifikation verdächtiger Transaktionen
|
||||||
|
- Kreditentscheidungen: Banken müssen Ablehnungen oder Genehmigungen rechtlich begründen können
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
Medizin / Gesundheitswesen
|
||||||
|
|
||||||
|
- **Regeln, warum welche Diagnose vorgeschlagen wurde**
|
||||||
|
|
||||||
|
Finanzwesen
|
||||||
|
|
||||||
|
- **Regeln, warum Transaktion verdächtig ist**
|
||||||
|
- **Regeln, warum Kredit genehmigt / abgelehnt wurde**
|
||||||
|
|
||||||
|
Versicherungswesen
|
||||||
|
|
||||||
|
- **Regeln, warum Prämien zu stande kommen**
|
||||||
|
- **Regeln, warum Schadensansprüche geltend gemacht werden können**
|
||||||
|
:::
|
||||||
|
|
||||||
|
## RBE Output
|
||||||
|
|
||||||
|
```
|
||||||
|
|--- marital.status_Married-civ-spouse <= 0.50
|
||||||
|
| |--- capital.gain <= 7073.50
|
||||||
|
| | |--- education.num <= 12.50
|
||||||
|
| | | |--- capital.loss <= 2218.50
|
||||||
|
| | | | |--- hours.per.week <= 40.50
|
||||||
|
| | | | | |--- Einkommen ≤ 50K
|
||||||
|
| | | | |--- hours.per.week > 40.50
|
||||||
|
| | | | | |--- Einkommen ≤ 50K
|
||||||
|
| | | |--- capital.loss > 2218.50
|
||||||
|
| | | | |--- fnlwgt <= 125450.50
|
||||||
|
| | | | | |--- Einkommen > 50K
|
||||||
|
| | | | |--- fnlwgt > 125450.50
|
||||||
|
| | | | | |--- Einkommen ≤ 50K
|
||||||
|
```
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
- Ich zeige euch keinen code aber zumindes den Output solltet ihr mal gesehen haben
|
||||||
|
- Als erstes den Decission Tree
|
||||||
|
- Und dann gescheit formatiert als Satz
|
||||||
|
- Richtigen Code wird später im Notebook vorgestellt
|
||||||
|
:::
|
||||||
|
|
||||||
|
## RBE Berechnungskosten
|
||||||
|
|
||||||
|
RBE skaliert:
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- N = Anzahl der Trainingsbeispiele
|
||||||
|
- D = Anzahl der Merkmale (Features)
|
||||||
|
:::
|
||||||
|
|
||||||
|
Komplexität in BigO Notation: **O(N × D × log N)**
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
Komplexität zusammengesetzt aus:
|
||||||
|
- Training des Surrogate-Modells (DecisionTreeClassifier)
|
||||||
|
- Vorhersagen mit dem Random Forest (Referenzmodell)
|
||||||
|
- Extraktion der Regeln aus dem Entscheidungsbaum
|
||||||
|
- Evaluierung des Surrogate-Modells
|
||||||
|
|
||||||
|
**Große Datensätze generell Machbar**
|
||||||
|
- Dürfen nur nicht zu viele Features haben, das erhöhe stark den Rechenaufwand
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: {.center}
|
||||||
|
## Jetzt wird es Praktisch
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
Jetzt das Notebook zeigen und konkret durchgehen
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## Vergleich der XAI-Verfahren
|
||||||
|
|
||||||
|
- Interpretierbarkeit
|
||||||
|
- Modellunabhängigkeit
|
||||||
|
- Genauigkeit / Konsistenz
|
||||||
|
- Anwendungsszenarien
|
||||||
|
|
||||||
|
|
||||||
|
## LIME Interpretierbarkeit
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Leicht verständlich, welche Features relevant waren
|
||||||
|
- Auch verständlich Laien
|
||||||
|
:::
|
||||||
|
|
||||||
|
{height=400 fig-align="center"}
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
**Rote Balken**
|
||||||
|
- Features die die Wahrscheinlichkeit von >=50k verringern
|
||||||
|
|
||||||
|
**Grüne Balken**
|
||||||
|
- Features die die Wahrscheinlichkeit von >=50k erhöhen
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## LIME Modellabhängigkeit
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Ist Model-Agnostic
|
||||||
|
- Behandelt jedes Modell als Blackbox
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
Wie in LIME schon beschrieben
|
||||||
|
|
||||||
|
L: local
|
||||||
|
I: Interpretable
|
||||||
|
M: Model Agnostic
|
||||||
|
:::
|
||||||
|
|
||||||
|
## LIME Konsistenz
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Die Ergebnisse sind instabil
|
||||||
|
- Können aber stark variieren da es auf zufälligen Abänderungen der Daten basiert
|
||||||
|
:::
|
||||||
|
|
||||||
|
{height=400 fig-align="center"}
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
**Wir haben 10 Erklärungen laufen lassen**
|
||||||
|
|
||||||
|
- Dann immer die Feature Wichtigkeit verglichen
|
||||||
|
- Geringe Varianz: Ähnliche Ergebnisse
|
||||||
|
- Hohe Varianz: Unterschiedliche Ergebnisse
|
||||||
|
|
||||||
|
=> Wir haben hier die Top 5 mit der größten Varianz => Immernoch gering
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## LIME Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Medizinische Diagnostik und Gesundheitswesen
|
||||||
|
- Finanzwesen und Kreditvergabe
|
||||||
|
- Personalabteilung und Recruiting
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
**Verständnis einer einzelnen Vorhersage**
|
||||||
|
:::
|
||||||
|
|
||||||
|
## RBE Interpretierbarkeit
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Regeln Leicht Nachvollziehbar: Wenn-Dann-Struktur von Regeln
|
||||||
|
- Potenziell komplex bei vielen Regeln
|
||||||
|
- Auch für Laien Verständlich
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
1. **Wenn-Dann-Struktur ist leicht zu verstehen, wie man auf Ergebnis kommt**
|
||||||
|
2. **Komplex / unübersichtlich wenn man viele Regeln hat**
|
||||||
|
3. **Regeln verstehen auch Lian**
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## RBE Interpretierbarkeit
|
||||||
|
|
||||||
|
Beispiel einer Regel: Vorhersagt > 50K
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- *WENN*
|
||||||
|
- *capital.gain ≤ 7073.50 UND*
|
||||||
|
- *education.num ≤ 12.50 UND*
|
||||||
|
- *capital.loss > 2218.50 UND*
|
||||||
|
- *DANN Einkommen > 50K*
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
**Beispielregel für > 50K Einkommen**
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## RBE Modellabhängigkeit
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- **Inhärent regelbasierte Modelle** (modellabhängig)
|
||||||
|
- **Post-hoc regelbasierte Erklärungen** (modellunabhängig)
|
||||||
|
:::
|
||||||
|
|
||||||
|
Wir verwenden einen: **Surrogate-Entscheidungsbaum**
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Dieser ist modellunabhängig
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
**Inhärent regelbasierte Modelle**
|
||||||
|
- Diese Modelle sind ihre eigenen Erklärungen - die Regeln sind das Modell.
|
||||||
|
|
||||||
|
**Post-hoc regelbasierte Erklärungen**
|
||||||
|
- Diese Ansätze können prinzipiell auf jedes Modell angewendet werden und sind daher modellunabhängig.
|
||||||
|
|
||||||
|
|
||||||
|
**Surrogate-Entscheidungsbaum**
|
||||||
|
|
||||||
|
- **Post-hoc (Modellunabhängig)**
|
||||||
|
- **Bekommt Trainingsdaten und Random Forest Vorhersagen**
|
||||||
|
|
||||||
|
Die extrahierten Regeln bieten eine **globale Erklärung des Random Forest-Verhaltens**.
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
## RBE Genauigkeit & Konsistenz
|
||||||
|
|
||||||
|
::: {.nonincremental}
|
||||||
|
- Genauigkeit: Wie nahe an Ergebnis von RF
|
||||||
|
- Konsistenz: Schwankungen der Genauigkeit
|
||||||
|
:::
|
||||||
|
|
||||||
|
{height=400 fig-align="center"}
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
Vorgehen:
|
||||||
|
|
||||||
|
- **Surrogate-Modell trainiert, sodass es RF-Vorhersagen nachahmt**
|
||||||
|
- **Vorhersage von Surrogate-Modell mit RF vergleichen**
|
||||||
|
- Berechnen die oft Vorhersagen gleich ist in %
|
||||||
|
|
||||||
|
|
||||||
|
Das ganze mit mehreren Baumtiefen
|
||||||
|
|
||||||
|
- So finden wir die **optimale Baumtiefe**
|
||||||
|
|
||||||
|
|
||||||
|
=> Man sieht **sehr hohe Genauigkeit**
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## RBE Anwendungsszenarien
|
||||||
|
::: {.nonincremental}
|
||||||
|
|
||||||
|
- Medizinische Diagnostik und Gesundheitswesen
|
||||||
|
- Finanzwesen und Kreditvergabe
|
||||||
|
- Versicherungswesen
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::{.notes}
|
||||||
|
|
||||||
|
**Globlaler Überblick von Entscheidungslogik**
|
||||||
|
|
||||||
|
**Wieso Entscheidet das Modell Allgemein**
|
||||||
|
|
||||||
|
|
||||||
|
Medizin / Gesundheitswesen
|
||||||
|
|
||||||
|
- **Beispiel Schlaganfall**
|
||||||
|
|
||||||
|
- => Wir wollen wissen, warum er allgemein zur Diagnose kommt
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
::: {.center}
|
||||||
|
## Vielen Dank für Eure Aufmerksamkeit!
|
||||||
|
:::
|
||||||
BIN
slides/slides_files/figure-revealjs/cell-5-output-1.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
821
slides/slides_files/figure-typst/cell-5-output-1.svg
Normal file
@ -0,0 +1,821 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" width="513.290625pt" height="392.514375pt" viewBox="0 0 513.290625 392.514375" xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||||
|
<metadata>
|
||||||
|
<rdf:RDF xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||||
|
<cc:Work>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
|
||||||
|
<dc:date>2025-03-31T15:54:30.691742</dc:date>
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:creator>
|
||||||
|
<cc:Agent>
|
||||||
|
<dc:title>Matplotlib v3.10.1, https://matplotlib.org/</dc:title>
|
||||||
|
</cc:Agent>
|
||||||
|
</dc:creator>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs>
|
||||||
|
<style type="text/css">*{stroke-linejoin: round; stroke-linecap: butt}</style>
|
||||||
|
</defs>
|
||||||
|
<g id="figure_1">
|
||||||
|
<g id="patch_1">
|
||||||
|
<path d="M 0 392.514375
|
||||||
|
L 513.290625 392.514375
|
||||||
|
L 513.290625 0
|
||||||
|
L 0 0
|
||||||
|
z
|
||||||
|
" style="fill: #ffffff"/>
|
||||||
|
</g>
|
||||||
|
<g id="axes_1">
|
||||||
|
<g id="patch_2">
|
||||||
|
<path d="M 59.690625 354.958125
|
||||||
|
L 506.090625 354.958125
|
||||||
|
L 506.090625 22.318125
|
||||||
|
L 59.690625 22.318125
|
||||||
|
z
|
||||||
|
" style="fill: #ffffff"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_3">
|
||||||
|
<path d="M 82.010625 354.958125
|
||||||
|
L 260.570625 354.958125
|
||||||
|
L 260.570625 38.158125
|
||||||
|
L 82.010625 38.158125
|
||||||
|
z
|
||||||
|
" clip-path="url(#pd48399ff98)" style="fill: #3274a1"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_4">
|
||||||
|
<path d="M 305.210625 354.958125
|
||||||
|
L 483.770625 354.958125
|
||||||
|
L 483.770625 254.471523
|
||||||
|
L 305.210625 254.471523
|
||||||
|
z
|
||||||
|
" clip-path="url(#pd48399ff98)" style="fill: #3274a1"/>
|
||||||
|
</g>
|
||||||
|
<g id="matplotlib.axis_1">
|
||||||
|
<g id="xtick_1">
|
||||||
|
<g id="line2d_1">
|
||||||
|
<defs>
|
||||||
|
<path id="m7a2f3ae2f9" d="M 0 0
|
||||||
|
L 0 3.5
|
||||||
|
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m7a2f3ae2f9" x="171.290625" y="354.958125" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_1">
|
||||||
|
<!-- <=50K -->
|
||||||
|
<g transform="translate(153.269531 369.556562) scale(0.1 -0.1)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-3c" d="M 4684 3150
|
||||||
|
L 1459 2003
|
||||||
|
L 4684 863
|
||||||
|
L 4684 294
|
||||||
|
L 678 1747
|
||||||
|
L 678 2266
|
||||||
|
L 4684 3719
|
||||||
|
L 4684 3150
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-3d" d="M 678 2906
|
||||||
|
L 4684 2906
|
||||||
|
L 4684 2381
|
||||||
|
L 678 2381
|
||||||
|
L 678 2906
|
||||||
|
z
|
||||||
|
M 678 1631
|
||||||
|
L 4684 1631
|
||||||
|
L 4684 1100
|
||||||
|
L 678 1100
|
||||||
|
L 678 1631
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-35" d="M 691 4666
|
||||||
|
L 3169 4666
|
||||||
|
L 3169 4134
|
||||||
|
L 1269 4134
|
||||||
|
L 1269 2991
|
||||||
|
Q 1406 3038 1543 3061
|
||||||
|
Q 1681 3084 1819 3084
|
||||||
|
Q 2600 3084 3056 2656
|
||||||
|
Q 3513 2228 3513 1497
|
||||||
|
Q 3513 744 3044 326
|
||||||
|
Q 2575 -91 1722 -91
|
||||||
|
Q 1428 -91 1123 -41
|
||||||
|
Q 819 9 494 109
|
||||||
|
L 494 744
|
||||||
|
Q 775 591 1075 516
|
||||||
|
Q 1375 441 1709 441
|
||||||
|
Q 2250 441 2565 725
|
||||||
|
Q 2881 1009 2881 1497
|
||||||
|
Q 2881 1984 2565 2268
|
||||||
|
Q 2250 2553 1709 2553
|
||||||
|
Q 1456 2553 1204 2497
|
||||||
|
Q 953 2441 691 2322
|
||||||
|
L 691 4666
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-30" d="M 2034 4250
|
||||||
|
Q 1547 4250 1301 3770
|
||||||
|
Q 1056 3291 1056 2328
|
||||||
|
Q 1056 1369 1301 889
|
||||||
|
Q 1547 409 2034 409
|
||||||
|
Q 2525 409 2770 889
|
||||||
|
Q 3016 1369 3016 2328
|
||||||
|
Q 3016 3291 2770 3770
|
||||||
|
Q 2525 4250 2034 4250
|
||||||
|
z
|
||||||
|
M 2034 4750
|
||||||
|
Q 2819 4750 3233 4129
|
||||||
|
Q 3647 3509 3647 2328
|
||||||
|
Q 3647 1150 3233 529
|
||||||
|
Q 2819 -91 2034 -91
|
||||||
|
Q 1250 -91 836 529
|
||||||
|
Q 422 1150 422 2328
|
||||||
|
Q 422 3509 836 4129
|
||||||
|
Q 1250 4750 2034 4750
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-4b" d="M 628 4666
|
||||||
|
L 1259 4666
|
||||||
|
L 1259 2694
|
||||||
|
L 3353 4666
|
||||||
|
L 4166 4666
|
||||||
|
L 1850 2491
|
||||||
|
L 4331 0
|
||||||
|
L 3500 0
|
||||||
|
L 1259 2247
|
||||||
|
L 1259 0
|
||||||
|
L 628 0
|
||||||
|
L 628 4666
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-3c"/>
|
||||||
|
<use xlink:href="#DejaVuSans-3d" transform="translate(83.789062 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-35" transform="translate(167.578125 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(231.201172 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-4b" transform="translate(294.824219 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="xtick_2">
|
||||||
|
<g id="line2d_2">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#m7a2f3ae2f9" x="394.490625" y="354.958125" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_2">
|
||||||
|
<!-- >50K -->
|
||||||
|
<g transform="translate(380.659375 369.556562) scale(0.1 -0.1)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-3e" d="M 678 3150
|
||||||
|
L 678 3719
|
||||||
|
L 4684 2266
|
||||||
|
L 4684 1747
|
||||||
|
L 678 294
|
||||||
|
L 678 863
|
||||||
|
L 3897 2003
|
||||||
|
L 678 3150
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-3e"/>
|
||||||
|
<use xlink:href="#DejaVuSans-35" transform="translate(83.789062 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(147.412109 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-4b" transform="translate(211.035156 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_3">
|
||||||
|
<!-- Einkommen -->
|
||||||
|
<g transform="translate(253.414062 383.234687) scale(0.1 -0.1)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-45" d="M 628 4666
|
||||||
|
L 3578 4666
|
||||||
|
L 3578 4134
|
||||||
|
L 1259 4134
|
||||||
|
L 1259 2753
|
||||||
|
L 3481 2753
|
||||||
|
L 3481 2222
|
||||||
|
L 1259 2222
|
||||||
|
L 1259 531
|
||||||
|
L 3634 531
|
||||||
|
L 3634 0
|
||||||
|
L 628 0
|
||||||
|
L 628 4666
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-69" d="M 603 3500
|
||||||
|
L 1178 3500
|
||||||
|
L 1178 0
|
||||||
|
L 603 0
|
||||||
|
L 603 3500
|
||||||
|
z
|
||||||
|
M 603 4863
|
||||||
|
L 1178 4863
|
||||||
|
L 1178 4134
|
||||||
|
L 603 4134
|
||||||
|
L 603 4863
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-6e" d="M 3513 2113
|
||||||
|
L 3513 0
|
||||||
|
L 2938 0
|
||||||
|
L 2938 2094
|
||||||
|
Q 2938 2591 2744 2837
|
||||||
|
Q 2550 3084 2163 3084
|
||||||
|
Q 1697 3084 1428 2787
|
||||||
|
Q 1159 2491 1159 1978
|
||||||
|
L 1159 0
|
||||||
|
L 581 0
|
||||||
|
L 581 3500
|
||||||
|
L 1159 3500
|
||||||
|
L 1159 2956
|
||||||
|
Q 1366 3272 1645 3428
|
||||||
|
Q 1925 3584 2291 3584
|
||||||
|
Q 2894 3584 3203 3211
|
||||||
|
Q 3513 2838 3513 2113
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-6b" d="M 581 4863
|
||||||
|
L 1159 4863
|
||||||
|
L 1159 1991
|
||||||
|
L 2875 3500
|
||||||
|
L 3609 3500
|
||||||
|
L 1753 1863
|
||||||
|
L 3688 0
|
||||||
|
L 2938 0
|
||||||
|
L 1159 1709
|
||||||
|
L 1159 0
|
||||||
|
L 581 0
|
||||||
|
L 581 4863
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-6f" d="M 1959 3097
|
||||||
|
Q 1497 3097 1228 2736
|
||||||
|
Q 959 2375 959 1747
|
||||||
|
Q 959 1119 1226 758
|
||||||
|
Q 1494 397 1959 397
|
||||||
|
Q 2419 397 2687 759
|
||||||
|
Q 2956 1122 2956 1747
|
||||||
|
Q 2956 2369 2687 2733
|
||||||
|
Q 2419 3097 1959 3097
|
||||||
|
z
|
||||||
|
M 1959 3584
|
||||||
|
Q 2709 3584 3137 3096
|
||||||
|
Q 3566 2609 3566 1747
|
||||||
|
Q 3566 888 3137 398
|
||||||
|
Q 2709 -91 1959 -91
|
||||||
|
Q 1206 -91 779 398
|
||||||
|
Q 353 888 353 1747
|
||||||
|
Q 353 2609 779 3096
|
||||||
|
Q 1206 3584 1959 3584
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-6d" d="M 3328 2828
|
||||||
|
Q 3544 3216 3844 3400
|
||||||
|
Q 4144 3584 4550 3584
|
||||||
|
Q 5097 3584 5394 3201
|
||||||
|
Q 5691 2819 5691 2113
|
||||||
|
L 5691 0
|
||||||
|
L 5113 0
|
||||||
|
L 5113 2094
|
||||||
|
Q 5113 2597 4934 2840
|
||||||
|
Q 4756 3084 4391 3084
|
||||||
|
Q 3944 3084 3684 2787
|
||||||
|
Q 3425 2491 3425 1978
|
||||||
|
L 3425 0
|
||||||
|
L 2847 0
|
||||||
|
L 2847 2094
|
||||||
|
Q 2847 2600 2669 2842
|
||||||
|
Q 2491 3084 2119 3084
|
||||||
|
Q 1678 3084 1418 2786
|
||||||
|
Q 1159 2488 1159 1978
|
||||||
|
L 1159 0
|
||||||
|
L 581 0
|
||||||
|
L 581 3500
|
||||||
|
L 1159 3500
|
||||||
|
L 1159 2956
|
||||||
|
Q 1356 3278 1631 3431
|
||||||
|
Q 1906 3584 2284 3584
|
||||||
|
Q 2666 3584 2933 3390
|
||||||
|
Q 3200 3197 3328 2828
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-65" d="M 3597 1894
|
||||||
|
L 3597 1613
|
||||||
|
L 953 1613
|
||||||
|
Q 991 1019 1311 708
|
||||||
|
Q 1631 397 2203 397
|
||||||
|
Q 2534 397 2845 478
|
||||||
|
Q 3156 559 3463 722
|
||||||
|
L 3463 178
|
||||||
|
Q 3153 47 2828 -22
|
||||||
|
Q 2503 -91 2169 -91
|
||||||
|
Q 1331 -91 842 396
|
||||||
|
Q 353 884 353 1716
|
||||||
|
Q 353 2575 817 3079
|
||||||
|
Q 1281 3584 2069 3584
|
||||||
|
Q 2775 3584 3186 3129
|
||||||
|
Q 3597 2675 3597 1894
|
||||||
|
z
|
||||||
|
M 3022 2063
|
||||||
|
Q 3016 2534 2758 2815
|
||||||
|
Q 2500 3097 2075 3097
|
||||||
|
Q 1594 3097 1305 2825
|
||||||
|
Q 1016 2553 972 2059
|
||||||
|
L 3022 2063
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-45"/>
|
||||||
|
<use xlink:href="#DejaVuSans-69" transform="translate(63.183594 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(90.966797 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6b" transform="translate(154.345703 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6f" transform="translate(208.630859 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6d" transform="translate(269.8125 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6d" transform="translate(367.224609 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-65" transform="translate(464.636719 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(526.160156 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="matplotlib.axis_2">
|
||||||
|
<g id="ytick_1">
|
||||||
|
<g id="line2d_3">
|
||||||
|
<defs>
|
||||||
|
<path id="ma37fdb22a8" d="M 0 0
|
||||||
|
L -3.5 0
|
||||||
|
" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#ma37fdb22a8" x="59.690625" y="354.958125" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_4">
|
||||||
|
<!-- 0 -->
|
||||||
|
<g transform="translate(46.328125 358.757344) scale(0.1 -0.1)">
|
||||||
|
<use xlink:href="#DejaVuSans-30"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_2">
|
||||||
|
<g id="line2d_4">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#ma37fdb22a8" x="59.690625" y="290.880455" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_5">
|
||||||
|
<!-- 5000 -->
|
||||||
|
<g transform="translate(27.240625 294.679674) scale(0.1 -0.1)">
|
||||||
|
<use xlink:href="#DejaVuSans-35"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(63.623047 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(127.246094 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(190.869141 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_3">
|
||||||
|
<g id="line2d_5">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#ma37fdb22a8" x="59.690625" y="226.802785" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_6">
|
||||||
|
<!-- 10000 -->
|
||||||
|
<g transform="translate(20.878125 230.602004) scale(0.1 -0.1)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-31" d="M 794 531
|
||||||
|
L 1825 531
|
||||||
|
L 1825 4091
|
||||||
|
L 703 3866
|
||||||
|
L 703 4441
|
||||||
|
L 1819 4666
|
||||||
|
L 2450 4666
|
||||||
|
L 2450 531
|
||||||
|
L 3481 531
|
||||||
|
L 3481 0
|
||||||
|
L 794 0
|
||||||
|
L 794 531
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-31"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(63.623047 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(127.246094 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(190.869141 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(254.492188 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_4">
|
||||||
|
<g id="line2d_6">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#ma37fdb22a8" x="59.690625" y="162.725115" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_7">
|
||||||
|
<!-- 15000 -->
|
||||||
|
<g transform="translate(20.878125 166.524334) scale(0.1 -0.1)">
|
||||||
|
<use xlink:href="#DejaVuSans-31"/>
|
||||||
|
<use xlink:href="#DejaVuSans-35" transform="translate(63.623047 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(127.246094 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(190.869141 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(254.492188 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_5">
|
||||||
|
<g id="line2d_7">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#ma37fdb22a8" x="59.690625" y="98.647445" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_8">
|
||||||
|
<!-- 20000 -->
|
||||||
|
<g transform="translate(20.878125 102.446664) scale(0.1 -0.1)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-32" d="M 1228 531
|
||||||
|
L 3431 531
|
||||||
|
L 3431 0
|
||||||
|
L 469 0
|
||||||
|
L 469 531
|
||||||
|
Q 828 903 1448 1529
|
||||||
|
Q 2069 2156 2228 2338
|
||||||
|
Q 2531 2678 2651 2914
|
||||||
|
Q 2772 3150 2772 3378
|
||||||
|
Q 2772 3750 2511 3984
|
||||||
|
Q 2250 4219 1831 4219
|
||||||
|
Q 1534 4219 1204 4116
|
||||||
|
Q 875 4013 500 3803
|
||||||
|
L 500 4441
|
||||||
|
Q 881 4594 1212 4672
|
||||||
|
Q 1544 4750 1819 4750
|
||||||
|
Q 2544 4750 2975 4387
|
||||||
|
Q 3406 4025 3406 3419
|
||||||
|
Q 3406 3131 3298 2873
|
||||||
|
Q 3191 2616 2906 2266
|
||||||
|
Q 2828 2175 2409 1742
|
||||||
|
Q 1991 1309 1228 531
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-32"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(63.623047 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(127.246094 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(190.869141 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(254.492188 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="ytick_6">
|
||||||
|
<g id="line2d_8">
|
||||||
|
<g>
|
||||||
|
<use xlink:href="#ma37fdb22a8" x="59.690625" y="34.569775" style="stroke: #000000; stroke-width: 0.8"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_9">
|
||||||
|
<!-- 25000 -->
|
||||||
|
<g transform="translate(20.878125 38.368994) scale(0.1 -0.1)">
|
||||||
|
<use xlink:href="#DejaVuSans-32"/>
|
||||||
|
<use xlink:href="#DejaVuSans-35" transform="translate(63.623047 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(127.246094 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(190.869141 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-30" transform="translate(254.492188 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="text_10">
|
||||||
|
<!-- Anzahl -->
|
||||||
|
<g transform="translate(14.798438 205.473281) rotate(-90) scale(0.1 -0.1)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-41" d="M 2188 4044
|
||||||
|
L 1331 1722
|
||||||
|
L 3047 1722
|
||||||
|
L 2188 4044
|
||||||
|
z
|
||||||
|
M 1831 4666
|
||||||
|
L 2547 4666
|
||||||
|
L 4325 0
|
||||||
|
L 3669 0
|
||||||
|
L 3244 1197
|
||||||
|
L 1141 1197
|
||||||
|
L 716 0
|
||||||
|
L 50 0
|
||||||
|
L 1831 4666
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-7a" d="M 353 3500
|
||||||
|
L 3084 3500
|
||||||
|
L 3084 2975
|
||||||
|
L 922 459
|
||||||
|
L 3084 459
|
||||||
|
L 3084 0
|
||||||
|
L 275 0
|
||||||
|
L 275 525
|
||||||
|
L 2438 3041
|
||||||
|
L 353 3041
|
||||||
|
L 353 3500
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-61" d="M 2194 1759
|
||||||
|
Q 1497 1759 1228 1600
|
||||||
|
Q 959 1441 959 1056
|
||||||
|
Q 959 750 1161 570
|
||||||
|
Q 1363 391 1709 391
|
||||||
|
Q 2188 391 2477 730
|
||||||
|
Q 2766 1069 2766 1631
|
||||||
|
L 2766 1759
|
||||||
|
L 2194 1759
|
||||||
|
z
|
||||||
|
M 3341 1997
|
||||||
|
L 3341 0
|
||||||
|
L 2766 0
|
||||||
|
L 2766 531
|
||||||
|
Q 2569 213 2275 61
|
||||||
|
Q 1981 -91 1556 -91
|
||||||
|
Q 1019 -91 701 211
|
||||||
|
Q 384 513 384 1019
|
||||||
|
Q 384 1609 779 1909
|
||||||
|
Q 1175 2209 1959 2209
|
||||||
|
L 2766 2209
|
||||||
|
L 2766 2266
|
||||||
|
Q 2766 2663 2505 2880
|
||||||
|
Q 2244 3097 1772 3097
|
||||||
|
Q 1472 3097 1187 3025
|
||||||
|
Q 903 2953 641 2809
|
||||||
|
L 641 3341
|
||||||
|
Q 956 3463 1253 3523
|
||||||
|
Q 1550 3584 1831 3584
|
||||||
|
Q 2591 3584 2966 3190
|
||||||
|
Q 3341 2797 3341 1997
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-68" d="M 3513 2113
|
||||||
|
L 3513 0
|
||||||
|
L 2938 0
|
||||||
|
L 2938 2094
|
||||||
|
Q 2938 2591 2744 2837
|
||||||
|
Q 2550 3084 2163 3084
|
||||||
|
Q 1697 3084 1428 2787
|
||||||
|
Q 1159 2491 1159 1978
|
||||||
|
L 1159 0
|
||||||
|
L 581 0
|
||||||
|
L 581 4863
|
||||||
|
L 1159 4863
|
||||||
|
L 1159 2956
|
||||||
|
Q 1366 3272 1645 3428
|
||||||
|
Q 1925 3584 2291 3584
|
||||||
|
Q 2894 3584 3203 3211
|
||||||
|
Q 3513 2838 3513 2113
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-6c" d="M 603 4863
|
||||||
|
L 1178 4863
|
||||||
|
L 1178 0
|
||||||
|
L 603 0
|
||||||
|
L 603 4863
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-41"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(68.408203 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-7a" transform="translate(131.787109 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-61" transform="translate(184.277344 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-68" transform="translate(245.556641 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6c" transform="translate(308.935547 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g id="patch_5">
|
||||||
|
<path d="M 59.690625 354.958125
|
||||||
|
L 59.690625 22.318125
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_6">
|
||||||
|
<path d="M 506.090625 354.958125
|
||||||
|
L 506.090625 22.318125
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_7">
|
||||||
|
<path d="M 59.690625 354.958125
|
||||||
|
L 506.090625 354.958125
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="patch_8">
|
||||||
|
<path d="M 59.690625 22.318125
|
||||||
|
L 506.090625 22.318125
|
||||||
|
" style="fill: none; stroke: #000000; stroke-width: 0.8; stroke-linejoin: miter; stroke-linecap: square"/>
|
||||||
|
</g>
|
||||||
|
<g id="text_11">
|
||||||
|
<!-- Verteilung der Einkommensklassen -->
|
||||||
|
<g transform="translate(177.458438 16.318125) scale(0.12 -0.12)">
|
||||||
|
<defs>
|
||||||
|
<path id="DejaVuSans-56" d="M 1831 0
|
||||||
|
L 50 4666
|
||||||
|
L 709 4666
|
||||||
|
L 2188 738
|
||||||
|
L 3669 4666
|
||||||
|
L 4325 4666
|
||||||
|
L 2547 0
|
||||||
|
L 1831 0
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-72" d="M 2631 2963
|
||||||
|
Q 2534 3019 2420 3045
|
||||||
|
Q 2306 3072 2169 3072
|
||||||
|
Q 1681 3072 1420 2755
|
||||||
|
Q 1159 2438 1159 1844
|
||||||
|
L 1159 0
|
||||||
|
L 581 0
|
||||||
|
L 581 3500
|
||||||
|
L 1159 3500
|
||||||
|
L 1159 2956
|
||||||
|
Q 1341 3275 1631 3429
|
||||||
|
Q 1922 3584 2338 3584
|
||||||
|
Q 2397 3584 2469 3576
|
||||||
|
Q 2541 3569 2628 3553
|
||||||
|
L 2631 2963
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-74" d="M 1172 4494
|
||||||
|
L 1172 3500
|
||||||
|
L 2356 3500
|
||||||
|
L 2356 3053
|
||||||
|
L 1172 3053
|
||||||
|
L 1172 1153
|
||||||
|
Q 1172 725 1289 603
|
||||||
|
Q 1406 481 1766 481
|
||||||
|
L 2356 481
|
||||||
|
L 2356 0
|
||||||
|
L 1766 0
|
||||||
|
Q 1100 0 847 248
|
||||||
|
Q 594 497 594 1153
|
||||||
|
L 594 3053
|
||||||
|
L 172 3053
|
||||||
|
L 172 3500
|
||||||
|
L 594 3500
|
||||||
|
L 594 4494
|
||||||
|
L 1172 4494
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-75" d="M 544 1381
|
||||||
|
L 544 3500
|
||||||
|
L 1119 3500
|
||||||
|
L 1119 1403
|
||||||
|
Q 1119 906 1312 657
|
||||||
|
Q 1506 409 1894 409
|
||||||
|
Q 2359 409 2629 706
|
||||||
|
Q 2900 1003 2900 1516
|
||||||
|
L 2900 3500
|
||||||
|
L 3475 3500
|
||||||
|
L 3475 0
|
||||||
|
L 2900 0
|
||||||
|
L 2900 538
|
||||||
|
Q 2691 219 2414 64
|
||||||
|
Q 2138 -91 1772 -91
|
||||||
|
Q 1169 -91 856 284
|
||||||
|
Q 544 659 544 1381
|
||||||
|
z
|
||||||
|
M 1991 3584
|
||||||
|
L 1991 3584
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-67" d="M 2906 1791
|
||||||
|
Q 2906 2416 2648 2759
|
||||||
|
Q 2391 3103 1925 3103
|
||||||
|
Q 1463 3103 1205 2759
|
||||||
|
Q 947 2416 947 1791
|
||||||
|
Q 947 1169 1205 825
|
||||||
|
Q 1463 481 1925 481
|
||||||
|
Q 2391 481 2648 825
|
||||||
|
Q 2906 1169 2906 1791
|
||||||
|
z
|
||||||
|
M 3481 434
|
||||||
|
Q 3481 -459 3084 -895
|
||||||
|
Q 2688 -1331 1869 -1331
|
||||||
|
Q 1566 -1331 1297 -1286
|
||||||
|
Q 1028 -1241 775 -1147
|
||||||
|
L 775 -588
|
||||||
|
Q 1028 -725 1275 -790
|
||||||
|
Q 1522 -856 1778 -856
|
||||||
|
Q 2344 -856 2625 -561
|
||||||
|
Q 2906 -266 2906 331
|
||||||
|
L 2906 616
|
||||||
|
Q 2728 306 2450 153
|
||||||
|
Q 2172 0 1784 0
|
||||||
|
Q 1141 0 747 490
|
||||||
|
Q 353 981 353 1791
|
||||||
|
Q 353 2603 747 3093
|
||||||
|
Q 1141 3584 1784 3584
|
||||||
|
Q 2172 3584 2450 3431
|
||||||
|
Q 2728 3278 2906 2969
|
||||||
|
L 2906 3500
|
||||||
|
L 3481 3500
|
||||||
|
L 3481 434
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-20" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-64" d="M 2906 2969
|
||||||
|
L 2906 4863
|
||||||
|
L 3481 4863
|
||||||
|
L 3481 0
|
||||||
|
L 2906 0
|
||||||
|
L 2906 525
|
||||||
|
Q 2725 213 2448 61
|
||||||
|
Q 2172 -91 1784 -91
|
||||||
|
Q 1150 -91 751 415
|
||||||
|
Q 353 922 353 1747
|
||||||
|
Q 353 2572 751 3078
|
||||||
|
Q 1150 3584 1784 3584
|
||||||
|
Q 2172 3584 2448 3432
|
||||||
|
Q 2725 3281 2906 2969
|
||||||
|
z
|
||||||
|
M 947 1747
|
||||||
|
Q 947 1113 1208 752
|
||||||
|
Q 1469 391 1925 391
|
||||||
|
Q 2381 391 2643 752
|
||||||
|
Q 2906 1113 2906 1747
|
||||||
|
Q 2906 2381 2643 2742
|
||||||
|
Q 2381 3103 1925 3103
|
||||||
|
Q 1469 3103 1208 2742
|
||||||
|
Q 947 2381 947 1747
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
<path id="DejaVuSans-73" d="M 2834 3397
|
||||||
|
L 2834 2853
|
||||||
|
Q 2591 2978 2328 3040
|
||||||
|
Q 2066 3103 1784 3103
|
||||||
|
Q 1356 3103 1142 2972
|
||||||
|
Q 928 2841 928 2578
|
||||||
|
Q 928 2378 1081 2264
|
||||||
|
Q 1234 2150 1697 2047
|
||||||
|
L 1894 2003
|
||||||
|
Q 2506 1872 2764 1633
|
||||||
|
Q 3022 1394 3022 966
|
||||||
|
Q 3022 478 2636 193
|
||||||
|
Q 2250 -91 1575 -91
|
||||||
|
Q 1294 -91 989 -36
|
||||||
|
Q 684 19 347 128
|
||||||
|
L 347 722
|
||||||
|
Q 666 556 975 473
|
||||||
|
Q 1284 391 1588 391
|
||||||
|
Q 1994 391 2212 530
|
||||||
|
Q 2431 669 2431 922
|
||||||
|
Q 2431 1156 2273 1281
|
||||||
|
Q 2116 1406 1581 1522
|
||||||
|
L 1381 1569
|
||||||
|
Q 847 1681 609 1914
|
||||||
|
Q 372 2147 372 2553
|
||||||
|
Q 372 3047 722 3315
|
||||||
|
Q 1072 3584 1716 3584
|
||||||
|
Q 2034 3584 2315 3537
|
||||||
|
Q 2597 3491 2834 3397
|
||||||
|
z
|
||||||
|
" transform="scale(0.015625)"/>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#DejaVuSans-56"/>
|
||||||
|
<use xlink:href="#DejaVuSans-65" transform="translate(60.658203 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-72" transform="translate(122.181641 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-74" transform="translate(163.294922 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-65" transform="translate(202.503906 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-69" transform="translate(264.027344 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6c" transform="translate(291.810547 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-75" transform="translate(319.59375 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(382.972656 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-67" transform="translate(446.351562 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-20" transform="translate(509.828125 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-64" transform="translate(541.615234 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-65" transform="translate(605.091797 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-72" transform="translate(666.615234 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-20" transform="translate(707.728516 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-45" transform="translate(739.515625 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-69" transform="translate(802.699219 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(830.482422 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6b" transform="translate(893.861328 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6f" transform="translate(948.146484 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6d" transform="translate(1009.328125 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6d" transform="translate(1106.740234 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-65" transform="translate(1204.152344 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(1265.675781 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-73" transform="translate(1329.054688 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6b" transform="translate(1381.154297 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6c" transform="translate(1439.064453 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-61" transform="translate(1466.847656 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-73" transform="translate(1528.126953 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-73" transform="translate(1580.226562 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-65" transform="translate(1632.326172 0)"/>
|
||||||
|
<use xlink:href="#DejaVuSans-6e" transform="translate(1693.849609 0)"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="pd48399ff98">
|
||||||
|
<rect x="59.690625" y="22.318125" width="446.4" height="332.64"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 21 KiB |
7
slides/slides_files/libs/clipboard/clipboard.min.js
vendored
Normal file
1
slides/slides_files/libs/quarto-html/light-border.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
.tippy-box[data-theme~=light-border]{background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,8,16,.15);color:#333;box-shadow:0 4px 14px -2px rgba(0,8,16,.08)}.tippy-box[data-theme~=light-border]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light-border]>.tippy-arrow:after,.tippy-box[data-theme~=light-border]>.tippy-svg-arrow:after{content:"";position:absolute;z-index:-1}.tippy-box[data-theme~=light-border]>.tippy-arrow:after{border-color:transparent;border-style:solid}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-arrow:after{border-top-color:rgba(0,8,16,.2);border-width:7px 7px 0;top:17px;left:1px}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-svg-arrow>svg{top:16px}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-svg-arrow:after{top:17px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff;bottom:16px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-arrow:after{border-bottom-color:rgba(0,8,16,.2);border-width:0 7px 7px;bottom:17px;left:1px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-svg-arrow>svg{bottom:16px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-svg-arrow:after{bottom:17px}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-arrow:after{border-left-color:rgba(0,8,16,.2);border-width:7px 0 7px 7px;left:17px;top:1px}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-svg-arrow>svg{left:11px}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-svg-arrow:after{left:12px}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff;right:16px}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-arrow:after{border-width:7px 7px 7px 0;right:17px;top:1px;border-right-color:rgba(0,8,16,.2)}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-svg-arrow>svg{right:11px}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-svg-arrow:after{right:12px}.tippy-box[data-theme~=light-border]>.tippy-svg-arrow{fill:#fff}.tippy-box[data-theme~=light-border]>.tippy-svg-arrow:after{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMCA2czEuNzk2LS4wMTMgNC42Ny0zLjYxNUM1Ljg1MS45IDYuOTMuMDA2IDggMGMxLjA3LS4wMDYgMi4xNDguODg3IDMuMzQzIDIuMzg1QzE0LjIzMyA2LjAwNSAxNiA2IDE2IDZIMHoiIGZpbGw9InJnYmEoMCwgOCwgMTYsIDAuMikiLz48L3N2Zz4=);background-size:16px 6px;width:16px;height:6px}
|
||||||
6
slides/slides_files/libs/quarto-html/popper.min.js
vendored
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/* quarto syntax highlight colors */
|
||||||
|
:root {
|
||||||
|
--quarto-hl-ot-color: #003B4F;
|
||||||
|
--quarto-hl-at-color: #657422;
|
||||||
|
--quarto-hl-ss-color: #20794D;
|
||||||
|
--quarto-hl-an-color: #5E5E5E;
|
||||||
|
--quarto-hl-fu-color: #4758AB;
|
||||||
|
--quarto-hl-st-color: #20794D;
|
||||||
|
--quarto-hl-cf-color: #003B4F;
|
||||||
|
--quarto-hl-op-color: #5E5E5E;
|
||||||
|
--quarto-hl-er-color: #AD0000;
|
||||||
|
--quarto-hl-bn-color: #AD0000;
|
||||||
|
--quarto-hl-al-color: #AD0000;
|
||||||
|
--quarto-hl-va-color: #111111;
|
||||||
|
--quarto-hl-bu-color: inherit;
|
||||||
|
--quarto-hl-ex-color: inherit;
|
||||||
|
--quarto-hl-pp-color: #AD0000;
|
||||||
|
--quarto-hl-in-color: #5E5E5E;
|
||||||
|
--quarto-hl-vs-color: #20794D;
|
||||||
|
--quarto-hl-wa-color: #5E5E5E;
|
||||||
|
--quarto-hl-do-color: #5E5E5E;
|
||||||
|
--quarto-hl-im-color: #00769E;
|
||||||
|
--quarto-hl-ch-color: #20794D;
|
||||||
|
--quarto-hl-dt-color: #AD0000;
|
||||||
|
--quarto-hl-fl-color: #AD0000;
|
||||||
|
--quarto-hl-co-color: #5E5E5E;
|
||||||
|
--quarto-hl-cv-color: #5E5E5E;
|
||||||
|
--quarto-hl-cn-color: #8f5902;
|
||||||
|
--quarto-hl-sc-color: #5E5E5E;
|
||||||
|
--quarto-hl-dv-color: #AD0000;
|
||||||
|
--quarto-hl-kw-color: #003B4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* other quarto variables */
|
||||||
|
:root {
|
||||||
|
--quarto-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre > code.sourceCode > span {
|
||||||
|
color: #003B4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span {
|
||||||
|
color: #003B4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.sourceCode > span {
|
||||||
|
color: #003B4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sourceCode,
|
||||||
|
div.sourceCode pre.sourceCode {
|
||||||
|
color: #003B4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.ot {
|
||||||
|
color: #003B4F;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.at {
|
||||||
|
color: #657422;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.ss {
|
||||||
|
color: #20794D;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.an {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.fu {
|
||||||
|
color: #4758AB;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.st {
|
||||||
|
color: #20794D;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.cf {
|
||||||
|
color: #003B4F;
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.op {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.er {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.bn {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.al {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.va {
|
||||||
|
color: #111111;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.bu {
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.ex {
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.pp {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.in {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.vs {
|
||||||
|
color: #20794D;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.wa {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.do {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.im {
|
||||||
|
color: #00769E;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.ch {
|
||||||
|
color: #20794D;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.dt {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.fl {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.co {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.cv {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.cn {
|
||||||
|
color: #8f5902;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.sc {
|
||||||
|
color: #5E5E5E;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.dv {
|
||||||
|
color: #AD0000;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
code span.kw {
|
||||||
|
color: #003B4F;
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prevent-inlining {
|
||||||
|
content: "</";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=ae99138f4fbc2fb4c9f5e5cd69c22459.css.map */
|
||||||
418
slides/slides_files/libs/quarto-html/tabby.min.js
vendored
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
(function (root, factory) {
|
||||||
|
if (typeof define === "function" && define.amd) {
|
||||||
|
define([], function () {
|
||||||
|
return factory(root);
|
||||||
|
});
|
||||||
|
} else if (typeof exports === "object") {
|
||||||
|
module.exports = factory(root);
|
||||||
|
} else {
|
||||||
|
root.Tabby = factory(root);
|
||||||
|
}
|
||||||
|
})(
|
||||||
|
typeof global !== "undefined"
|
||||||
|
? global
|
||||||
|
: typeof window !== "undefined"
|
||||||
|
? window
|
||||||
|
: this,
|
||||||
|
function (window) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//
|
||||||
|
// Variables
|
||||||
|
//
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
idPrefix: "tabby-toggle_",
|
||||||
|
default: "[data-tabby-default]",
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Methods
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge two or more objects together.
|
||||||
|
* @param {Object} objects The objects to merge together
|
||||||
|
* @returns {Object} Merged values of defaults and options
|
||||||
|
*/
|
||||||
|
var extend = function () {
|
||||||
|
var merged = {};
|
||||||
|
Array.prototype.forEach.call(arguments, function (obj) {
|
||||||
|
for (var key in obj) {
|
||||||
|
if (!obj.hasOwnProperty(key)) return;
|
||||||
|
merged[key] = obj[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return merged;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a custom event
|
||||||
|
* @param {String} type The event type
|
||||||
|
* @param {Node} tab The tab to attach the event to
|
||||||
|
* @param {Node} details Details about the event
|
||||||
|
*/
|
||||||
|
var emitEvent = function (tab, details) {
|
||||||
|
// Create a new event
|
||||||
|
var event;
|
||||||
|
if (typeof window.CustomEvent === "function") {
|
||||||
|
event = new CustomEvent("tabby", {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
detail: details,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
event = document.createEvent("CustomEvent");
|
||||||
|
event.initCustomEvent("tabby", true, true, details);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the event
|
||||||
|
tab.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
var focusHandler = function (event) {
|
||||||
|
toggle(event.target);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getKeyboardFocusableElements = function (element) {
|
||||||
|
return [
|
||||||
|
...element.querySelectorAll(
|
||||||
|
'a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])'
|
||||||
|
),
|
||||||
|
].filter(
|
||||||
|
(el) => !el.hasAttribute("disabled") && !el.getAttribute("aria-hidden")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove roles and attributes from a tab and its content
|
||||||
|
* @param {Node} tab The tab
|
||||||
|
* @param {Node} content The tab content
|
||||||
|
* @param {Object} settings User settings and options
|
||||||
|
*/
|
||||||
|
var destroyTab = function (tab, content, settings) {
|
||||||
|
// Remove the generated ID
|
||||||
|
if (tab.id.slice(0, settings.idPrefix.length) === settings.idPrefix) {
|
||||||
|
tab.id = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove event listener
|
||||||
|
tab.removeEventListener("focus", focusHandler, true);
|
||||||
|
|
||||||
|
// Remove roles
|
||||||
|
tab.removeAttribute("role");
|
||||||
|
tab.removeAttribute("aria-controls");
|
||||||
|
tab.removeAttribute("aria-selected");
|
||||||
|
tab.removeAttribute("tabindex");
|
||||||
|
tab.closest("li").removeAttribute("role");
|
||||||
|
content.removeAttribute("role");
|
||||||
|
content.removeAttribute("aria-labelledby");
|
||||||
|
content.removeAttribute("hidden");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the required roles and attributes to a tab and its content
|
||||||
|
* @param {Node} tab The tab
|
||||||
|
* @param {Node} content The tab content
|
||||||
|
* @param {Object} settings User settings and options
|
||||||
|
*/
|
||||||
|
var setupTab = function (tab, content, settings) {
|
||||||
|
// Give tab an ID if it doesn't already have one
|
||||||
|
if (!tab.id) {
|
||||||
|
tab.id = settings.idPrefix + content.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add roles
|
||||||
|
tab.setAttribute("role", "tab");
|
||||||
|
tab.setAttribute("aria-controls", content.id);
|
||||||
|
tab.closest("li").setAttribute("role", "presentation");
|
||||||
|
content.setAttribute("role", "tabpanel");
|
||||||
|
content.setAttribute("aria-labelledby", tab.id);
|
||||||
|
|
||||||
|
// Add selected state
|
||||||
|
if (tab.matches(settings.default)) {
|
||||||
|
tab.setAttribute("aria-selected", "true");
|
||||||
|
} else {
|
||||||
|
tab.setAttribute("aria-selected", "false");
|
||||||
|
content.setAttribute("hidden", "hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add focus event listender
|
||||||
|
tab.addEventListener("focus", focusHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide a tab and its content
|
||||||
|
* @param {Node} newTab The new tab that's replacing it
|
||||||
|
*/
|
||||||
|
var hide = function (newTab) {
|
||||||
|
// Variables
|
||||||
|
var tabGroup = newTab.closest('[role="tablist"]');
|
||||||
|
if (!tabGroup) return {};
|
||||||
|
var tab = tabGroup.querySelector('[role="tab"][aria-selected="true"]');
|
||||||
|
if (!tab) return {};
|
||||||
|
var content = document.querySelector(tab.hash);
|
||||||
|
|
||||||
|
// Hide the tab
|
||||||
|
tab.setAttribute("aria-selected", "false");
|
||||||
|
|
||||||
|
// Hide the content
|
||||||
|
if (!content) return { previousTab: tab };
|
||||||
|
content.setAttribute("hidden", "hidden");
|
||||||
|
|
||||||
|
// Return the hidden tab and content
|
||||||
|
return {
|
||||||
|
previousTab: tab,
|
||||||
|
previousContent: content,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a tab and its content
|
||||||
|
* @param {Node} tab The tab
|
||||||
|
* @param {Node} content The tab content
|
||||||
|
*/
|
||||||
|
var show = function (tab, content) {
|
||||||
|
tab.setAttribute("aria-selected", "true");
|
||||||
|
content.removeAttribute("hidden");
|
||||||
|
tab.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle a new tab
|
||||||
|
* @param {Node} tab The tab to show
|
||||||
|
*/
|
||||||
|
var toggle = function (tab) {
|
||||||
|
// Make sure there's a tab to toggle and it's not already active
|
||||||
|
if (!tab || tab.getAttribute("aria-selected") == "true") return;
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
var content = document.querySelector(tab.hash);
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
// Hide active tab and content
|
||||||
|
var details = hide(tab);
|
||||||
|
|
||||||
|
// Show new tab and content
|
||||||
|
show(tab, content);
|
||||||
|
|
||||||
|
// Add event details
|
||||||
|
details.tab = tab;
|
||||||
|
details.content = content;
|
||||||
|
|
||||||
|
// Emit a custom event
|
||||||
|
emitEvent(tab, details);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all of the tabs in a tablist
|
||||||
|
* @param {Node} tab A tab from the list
|
||||||
|
* @return {Object} The tabs and the index of the currently active one
|
||||||
|
*/
|
||||||
|
var getTabsMap = function (tab) {
|
||||||
|
var tabGroup = tab.closest('[role="tablist"]');
|
||||||
|
var tabs = tabGroup ? tabGroup.querySelectorAll('[role="tab"]') : null;
|
||||||
|
if (!tabs) return;
|
||||||
|
return {
|
||||||
|
tabs: tabs,
|
||||||
|
index: Array.prototype.indexOf.call(tabs, tab),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch the active tab based on keyboard activity
|
||||||
|
* @param {Node} tab The currently active tab
|
||||||
|
* @param {Key} key The key that was pressed
|
||||||
|
*/
|
||||||
|
var switchTabs = function (tab, key) {
|
||||||
|
// Get a map of tabs
|
||||||
|
var map = getTabsMap(tab);
|
||||||
|
if (!map) return;
|
||||||
|
var length = map.tabs.length - 1;
|
||||||
|
var index;
|
||||||
|
|
||||||
|
// Go to previous tab
|
||||||
|
if (["ArrowUp", "ArrowLeft", "Up", "Left"].indexOf(key) > -1) {
|
||||||
|
index = map.index < 1 ? length : map.index - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to next tab
|
||||||
|
else if (["ArrowDown", "ArrowRight", "Down", "Right"].indexOf(key) > -1) {
|
||||||
|
index = map.index === length ? 0 : map.index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to home
|
||||||
|
else if (key === "Home") {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to end
|
||||||
|
else if (key === "End") {
|
||||||
|
index = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the tab
|
||||||
|
toggle(map.tabs[index]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Constructor object
|
||||||
|
*/
|
||||||
|
var Constructor = function (selector, options) {
|
||||||
|
//
|
||||||
|
// Variables
|
||||||
|
//
|
||||||
|
|
||||||
|
var publicAPIs = {};
|
||||||
|
var settings, tabWrapper;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Methods
|
||||||
|
//
|
||||||
|
|
||||||
|
publicAPIs.destroy = function () {
|
||||||
|
// Get all tabs
|
||||||
|
var tabs = tabWrapper.querySelectorAll("a");
|
||||||
|
|
||||||
|
// Add roles to tabs
|
||||||
|
Array.prototype.forEach.call(tabs, function (tab) {
|
||||||
|
// Get the tab content
|
||||||
|
var content = document.querySelector(tab.hash);
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
// Setup the tab
|
||||||
|
destroyTab(tab, content, settings);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove role from wrapper
|
||||||
|
tabWrapper.removeAttribute("role");
|
||||||
|
|
||||||
|
// Remove event listeners
|
||||||
|
document.documentElement.removeEventListener(
|
||||||
|
"click",
|
||||||
|
clickHandler,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
tabWrapper.removeEventListener("keydown", keyHandler, true);
|
||||||
|
|
||||||
|
// Reset variables
|
||||||
|
settings = null;
|
||||||
|
tabWrapper = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup the DOM with the proper attributes
|
||||||
|
*/
|
||||||
|
publicAPIs.setup = function () {
|
||||||
|
// Variables
|
||||||
|
tabWrapper = document.querySelector(selector);
|
||||||
|
if (!tabWrapper) return;
|
||||||
|
var tabs = tabWrapper.querySelectorAll("a");
|
||||||
|
|
||||||
|
// Add role to wrapper
|
||||||
|
tabWrapper.setAttribute("role", "tablist");
|
||||||
|
|
||||||
|
// Add roles to tabs. provide dynanmic tab indexes if we are within reveal
|
||||||
|
var contentTabindexes =
|
||||||
|
window.document.body.classList.contains("reveal-viewport");
|
||||||
|
var nextTabindex = 1;
|
||||||
|
Array.prototype.forEach.call(tabs, function (tab) {
|
||||||
|
if (contentTabindexes) {
|
||||||
|
tab.setAttribute("tabindex", "" + nextTabindex++);
|
||||||
|
} else {
|
||||||
|
tab.setAttribute("tabindex", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the tab content
|
||||||
|
var content = document.querySelector(tab.hash);
|
||||||
|
if (!content) return;
|
||||||
|
|
||||||
|
// set tab indexes for content
|
||||||
|
if (contentTabindexes) {
|
||||||
|
getKeyboardFocusableElements(content).forEach(function (el) {
|
||||||
|
el.setAttribute("tabindex", "" + nextTabindex++);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the tab
|
||||||
|
setupTab(tab, content, settings);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle a tab based on an ID
|
||||||
|
* @param {String|Node} id The tab to toggle
|
||||||
|
*/
|
||||||
|
publicAPIs.toggle = function (id) {
|
||||||
|
// Get the tab
|
||||||
|
var tab = id;
|
||||||
|
if (typeof id === "string") {
|
||||||
|
tab = document.querySelector(
|
||||||
|
selector + ' [role="tab"][href*="' + id + '"]'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle the tab
|
||||||
|
toggle(tab);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle click events
|
||||||
|
*/
|
||||||
|
var clickHandler = function (event) {
|
||||||
|
// Only run on toggles
|
||||||
|
var tab = event.target.closest(selector + ' [role="tab"]');
|
||||||
|
if (!tab) return;
|
||||||
|
|
||||||
|
// Prevent link behavior
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Toggle the tab
|
||||||
|
toggle(tab);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle keydown events
|
||||||
|
*/
|
||||||
|
var keyHandler = function (event) {
|
||||||
|
// Only run if a tab is in focus
|
||||||
|
var tab = document.activeElement;
|
||||||
|
if (!tab.matches(selector + ' [role="tab"]')) return;
|
||||||
|
|
||||||
|
// Only run for specific keys
|
||||||
|
if (["Home", "End"].indexOf(event.key) < 0) return;
|
||||||
|
|
||||||
|
// Switch tabs
|
||||||
|
switchTabs(tab, event.key);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the instance
|
||||||
|
*/
|
||||||
|
var init = function () {
|
||||||
|
// Merge user options with defaults
|
||||||
|
settings = extend(defaults, options || {});
|
||||||
|
|
||||||
|
// Setup the DOM
|
||||||
|
publicAPIs.setup();
|
||||||
|
|
||||||
|
// Add event listeners
|
||||||
|
document.documentElement.addEventListener("click", clickHandler, true);
|
||||||
|
tabWrapper.addEventListener("keydown", keyHandler, true);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize and return the Public APIs
|
||||||
|
//
|
||||||
|
|
||||||
|
init();
|
||||||
|
return publicAPIs;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Return the Constructor
|
||||||
|
//
|
||||||
|
|
||||||
|
return Constructor;
|
||||||
|
}
|
||||||
|
);
|
||||||
1
slides/slides_files/libs/quarto-html/tippy.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{position:relative;background-color:#333;color:#fff;border-radius:4px;font-size:14px;line-height:1.4;white-space:normal;outline:0;transition-property:transform,visibility,opacity}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{bottom:-7px;left:0;border-width:8px 8px 0;border-top-color:initial;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{top:-7px;left:0;border-width:0 8px 8px;border-bottom-color:initial;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-width:8px 0 8px 8px;border-left-color:initial;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{left:-7px;border-width:8px 8px 8px 0;border-right-color:initial;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{width:16px;height:16px;color:#333}.tippy-arrow:before{content:"";position:absolute;border-color:transparent;border-style:solid}.tippy-content{position:relative;padding:5px 9px;z-index:1}
|
||||||
2
slides/slides_files/libs/quarto-html/tippy.umd.min.js
vendored
Normal file
30
slides/slides_files/libs/revealjs/dist/reset.css
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v4.0 | 20180602
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
main, menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, main, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
8
slides/slides_files/libs/revealjs/dist/reveal.css
vendored
Normal file
9
slides/slides_files/libs/revealjs/dist/reveal.esm.js
vendored
Normal file
1
slides/slides_files/libs/revealjs/dist/reveal.esm.js.map
vendored
Normal file
9
slides/slides_files/libs/revealjs/dist/reveal.js
vendored
Normal file
1
slides/slides_files/libs/revealjs/dist/reveal.js.map
vendored
Normal file
2
slides/slides_files/libs/revealjs/dist/theme/fonts/league-gothic/LICENSE
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SIL Open Font License (OFL)
|
||||||
|
http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
|
||||||
10
slides/slides_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.css
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'League Gothic';
|
||||||
|
src: url('./league-gothic.eot');
|
||||||
|
src: url('./league-gothic.eot?#iefix') format('embedded-opentype'),
|
||||||
|
url('./league-gothic.woff') format('woff'),
|
||||||
|
url('./league-gothic.ttf') format('truetype');
|
||||||
|
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
BIN
slides/slides_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot
vendored
Executable file
BIN
slides/slides_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf
vendored
Executable file
BIN
slides/slides_files/libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff
vendored
Executable file
45
slides/slides_files/libs/revealjs/dist/theme/fonts/source-sans-pro/LICENSE
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
SIL Open Font License
|
||||||
|
|
||||||
|
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
—————————————————————————————-
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
—————————————————————————————-
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
|
||||||
|
|
||||||
|
“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
|
||||||
|
|
||||||
|
“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||