{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PROGRAM ENHANCER+ | POLITECHNIKA WARSZAWSKA\n",
    "## Kurs: Jakość danych i Data Governance w analizie danych oraz ML\n",
    "\n",
    "### 🧪 LAB 1: Wprowadzenie do analizy jakości danych w Pandas (Zasada GIGO w praktyce)\n",
    "\n",
    "---\n",
    "\n",
    "### 🎯 Cel ćwiczenia:\n",
    "Celem tego laboratorium jest pierwsze zderzenie ze skażonym zbiorem danych w kontekście budowy modeli uczenia maszynowego. Po zakończeniu ćwiczenia będziesz potrafił/a:\n",
    "1. Wykorzystać podstawowe metody biblioteki `pandas` (`.info()`, `.describe()`, `.isnull()`) do identyfikacji anomalii.\n",
    "2. Wykryć ciche błędy danych (np. ukryte braki danych, nielogiczne wartości numeryczne), które nie powodują awarii kodu, ale niszczą model.\n",
    "3. Przetrenować model drzewa decyzyjnego na surowych danych i przeanalizować, jak uczy się on \"szumu\" zamiast rzeczywistych reguł.\n",
    "4. Sformułować wnioski biznesowo-procesowe i zaproponować wstępne ramy odpowiedzialności organizacyjnej (Data Governance).\n",
    "\n",
    "### 📊 Case Study: Customer 360 & Scoring Kredytowy\n",
    "Pracujesz jako Audytor Jakości Danych i Systemów ML w banku. Zespół Data Science przygotował zbiór treningowy `customer360_train.csv`. Na jego podstawie ma zostać zbudowany model oceniający wiarygodność kredytową klientów. Twoim zadaniem jest zweryfikowanie, czy ten zbiór nadaje się do modelowania."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Krok 0: Przygotowanie środowiska i generowanie danych\n",
    "Aby zapewnić pełną niezależność ćwiczenia na platformie LeIA (limit 2 GB RAM) i uniknąć błędów braku plików, poniższa komórka wygeneruje dla Ciebie realistyczny, skażony zbiór danych bezpośrednio w pamięci roboczej i zapisze go na dysku."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import os\n",
    "from sklearn.tree import DecisionTreeClassifier, plot_tree\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "print(\"Uruchomiono komórkę. Generowanie zbioru danych...\")\n",
    "\n",
    "# Ustawienie ziarna losowości dla powtarzalności\n",
    "np.random.seed(42)\n",
    "n_samples = 1000\n",
    "\n",
    "# Generowanie syntetycznych, skażonych danych Customer 360\n",
    "ages = np.random.normal(38, 12, n_samples).astype(int)\n",
    "# Wstrzyknięcie anomalii numerycznych (brak wartości oznaczony jako -99 oraz wiek kosmiczny)\n",
    "ages[np.random.choice(n_samples, 50, replace=False)] = -99\n",
    "ages[np.random.choice(n_samples, 5, replace=False)] = 250\n",
    "\n",
    "incomes = np.random.normal(5500, 1500, n_samples)\n",
    "income_strings = incomes.astype(int).astype(str)\n",
    "# Wstrzyknięcie ukrytych braków danych zamaskowanych jako tekst \"Unknown\"\n",
    "income_strings[np.random.choice(n_samples, 80, replace=False)] = \"Unknown\"\n",
    "\n",
    "# Cechy stabilne\n",
    "credit_scores = np.random.randint(300, 850, n_samples)\n",
    "\n",
    "# Generowanie targetu (Default): ryzyko niewypłacalności skorelowane ze scorem i dochodem\n",
    "# Dodajemy szum losowy, aby symulować niedoskonałość rzeczywistości\n",
    "default_prob = 1 / (1 + np.exp(-((500 - credit_scores)/100 + (5000 - incomes)/2000)))\n",
    "defaults = np.random.binomial(1, default_prob)\n",
    "\n",
    "# Tworzenie DataFrame\n",
    "df = pd.DataFrame({\n",
    "    'Client_ID': range(10000, 10000 + n_samples),\n",
    "    'Age': ages,\n",
    "    'Income': income_strings,\n",
    "    'Credit_Score': credit_scores,\n",
    "    'Default_Target': defaults\n",
    "})\n",
    "\n",
    "# Zapis do pliku CSV\n",
    "df.to_csv('customer360_train.csv', index=False)\n",
    "print(f\"Plik 'customer360_train.csv' został pomyślnie utworzony w folderze: {os.getcwd()}\")\n",
    "df.head(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Krok 1: Wstępna inspekcja techniczna (Profilowanie surowe)\n",
    "Wczytajmy zbiór danych i sprawdźmy jego podstawową strukturę techniczną za pomocą metod biblioteki Pandas. To jest moment, w którym większość początkujących analityków kończy weryfikację, zakładając, że dane są \"poprawne, bo się wczytały\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Wczytanie danych z zapisanego pliku\n",
    "data = pd.read_csv('customer360_train.csv')\n",
    "\n",
    "# Sprawdzenie typów kolumn i braków danych wykrywanych automatycznie przez system\n",
    "print(\"--- PODSTAWOWE INFORMACJE O ZBIORZE ---\")\n",
    "print(data.info())\n",
    "\n",
    "print(\"\\n--- LICZBA BRAKÓW DANYCH WYKRYTYCH PRZEZ PANDAS ---\")\n",
    "print(data.isnull().sum())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 🔍 Zadanie analityczne 1:\n",
    "Przyjrzyj się uważnie wynikom działania metody `.info()`. \n",
    "1. Jaki typ danych został przypisany do kolumny `Income`?\n",
    "2. Dlaczego Pandas nie wykrył żadnych braków danych (`NaN`) w kolumnach `Age` oraz `Income`?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Krok 2: Detekcja anomalii numerycznych i semantycznych\n",
    "Uruchommy statystyki opisowe dla kolumn numerycznych za pomocą `.describe()`. Następnie zbadajmy unikalne wartości w kolumnie `Income`, aby zrozumieć jej strukturę."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"--- STATYSTYKI OPISOWE DLA KOLUMN NUMERYCZNYCH ---\")\n",
    "print(data.describe())\n",
    "\n",
    "print(\"\\n--- PRZYKŁADOWE UNIKALNE WARTOŚCI W KOLUMNIE INCOME ---\")\n",
    "print(data['Income'].value_counts().head(10))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 🔍 Zadanie analityczne 2:\n",
    "Zidentyfikuj anomalie w powyższych statystykach:\n",
    "1. Co jest rażąco nielogiczne w statystykach kolumny `Age` (zwróć uwagę na wartość minimalną oraz maksymalną)?\n",
    "2. Dlaczego obecność wartości tekstowej `\"Unknown\"` w kolumnie `Income` zablokowała możliwość obliczenia dla niej średniej czy odchylenia standardowego w metodzie `.describe()`?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Krok 3: Zasada GIGO w akcji (Trening modelu na skażonych danych)\n",
    "Aby udowodnić główną tezę kursu, że **model wzmacnia błędy w danych**, spróbujmy wytrenować model drzewa decyzyjnego, ignorując obecność anomalii. \n",
    "\n",
    "Zrobimy proste i błędne \"czyszczenie\" na szybko: \n",
    "- Zastąpimy wartości tekstowe `\"Unknown\"` w dochodzie zerem, aby móc przekształcić kolumnę na liczbową.\n",
    "- Pozostawimy ujemny wiek (`-99`) oraz wiek `250` bez zmian."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Kopia danych pod szybkie, wadliwe modelowanie\n",
    "df_bad = data.copy()\n",
    "\n",
    "# Szybka i błędna transformacja: zastąpienie 'Unknown' zerem\n",
    "df_bad['Income'] = df_bad['Income'].replace('Unknown', 0).astype(float)\n",
    "\n",
    "# Definicja cech wejściowych (X) oraz targetu (y)\n",
    "X = df_bad[['Age', 'Income', 'Credit_Score']]\n",
    "y = df_bad['Default_Target']\n",
    "\n",
    "# Trening płytkiego drzewa decyzyjnego, aby móc je łatwo zwizualizować\n",
    "clf = DecisionTreeClassifier(max_depth=3, random_state=42)\n",
    "clf.fit(X, y)\n",
    "\n",
    "# Wizualizacja drzewa\n",
    "plt.figure(figsize=(15, 10))\n",
    "plot_tree(clf, feature_names=X.columns, class_names=['Solvent', 'Default'], filled=True, rounded=True)\n",
    "plt.title(\"Drzewo decyzyjne wytrenowane na skażonych danych\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 🔍 Zadanie analityczne 3:\n",
    "Przeanalizuj pierwszy podział (root node) lub kolejne gałęzie wygenerowanego drzewa.\n",
    "1. Sprawdź warunek podziału dla cechy `Age` lub `Income`. Czy model zaczął traktować sztuczne wartości `-99` lub `0` jako realne granice decyzyjne dla ryzyka kredytowego?\n",
    "2. Jaka decyzja zostanie podjęta przez model dla klienta w wieku 40 lat o zarobkach 6000 PLN, u którego w systemie z powodu błędu wpisano dochód jako \"Unknown\" (czyli model zobaczy `0`)?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Krok 4: Diagnoza przyczyn źródłowych i ład organizacyjny (Data Governance)\n",
    "Jako inżynier jakości danych musisz nie tylko umieć napisać kod czyszczący (to zrobimy w module 2), ale przede wszystkim zrozumieć, **dlaczego te błędy powstały w organizacji** i jak im zapobiec na poziomie procesów ludzkich."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 📝 MINI-RAPORT AUDYTORSKI (ZADANIE DO ZALICZENIA LAB 1)\n",
    "\n",
    "Wpisz swoje odpowiedzi w poniższej komórce tekstowej (Markdown), zastępując teksty w nawiasach kwadratowych swoimi analizami. Raport powinien być napisany językiem profesjonalnym, inżynieryjnym i strukturalnym.\n",
    "\n",
    "---\n",
    "\n",
    "#### 1. Co jest problemem danych? (Diagnoza techniczna)\n",
    "* **Kolumna Age:** [Wpisz diagnozę dotyczącą wartości -99 oraz 250. Skąd mogły się wziąć w systemie?]\n",
    "* **Kolumna Income:** [Wpisz diagnozę dotyczącą wartości 'Unknown'. Dlaczego Pandas nie wykrył ich automatycznie jako braki?]\n",
    "\n",
    "#### 2. Co jest problemem procesu biznesowego? (Gdzie leży źródło błędu?)\n",
    "* [Zastanów się, jak wygląda formularz rejestracyjny klienta. Dlaczego system pozwolił na wpisanie wieku 250 lub zatwierdzenie wieku ujemnego? Jakie mechanizmy walidacji na wejściu (u źródła) zawiodły?]\n",
    "\n",
    "#### 3. Co jest problemem modelu ML? (Wpływ na predykcję i stabilność)\n",
    "* [Na podstawie analizy drzewa decyzyjnego opisz, jak model zinterpretował wartość dochodu równą 0 (która była zamaskowanym brakiem danych). Dlaczego uczenie modelu na takich danych bez uprzedniego czyszczenia zagraża stabilności finansowej banku?]\n",
    "\n",
    "#### 4. Kto w organizacji powinien odpowiadać za te błędy? (Perspektywa Data Governance)\n",
    "* **Właściciel Danych (Data Owner - biznes):** [Jaka jest jego rola w zdefiniowaniu reguł poprawności wieku i dochodu?]\n",
    "* **Opiekun Danych (Data Steward - operacyjny):** [Jak powinien monitorować jakość tego zbioru na co dzień?]\n",
    "* **Data Scientist (Konsument):** [Czy ma prawo bezkrytycznie trenować model na dowolnych danych? Jakie są jego obowiązki?]\n",
    "\n",
    "#### 5. Działania korygujące i zapobiegawcze (Rekomendacja na przyszłość)\n",
    "* **Działanie natychmiastowe (Korygujące):** [Jak technicznie powinieneś oczyścić ten konkretny dataset przed treningiem modelu?]\n",
    "* **Działanie długoterminowe (Zapobiegawcze):** [Gdzie i jakie reguły walidacji należy wdrożyć, aby te błędy nigdy więcej nie trafiły do bazy danych?]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}