# -*- coding: utf-8 -*-
"""Analiza_danych_wzor.ipynb

Automatically generated by Colab.

Original file is located at
    https://colab.research.google.com/drive/1BYZ1vHi2Vvu_JzO1B4yAJddKuPOp9Uo4
"""

# =========================================================
# 0) IMPORTY I USTAWIENIA (zawsze na początku notebooka)
# =========================================================

import pandas as pd  # biblioteka do pracy z tabelami (DataFrame)
import numpy as np  # biblioteka do obliczeń matematycznych
import matplotlib.pyplot as plt  # biblioteka do tworzenia wykresów
import seaborn as sns  # biblioteka do ładniejszych wykresów statystycznych
from pathlib import Path  # wygodna praca ze ścieżkami plików

pd.set_option("display.max_columns", 200)  # pokazuj dużo kolumn w tabelach
pd.set_option("display.width", 140)  # ustaw szerokość wyświetlania tabel
sns.set_theme(style="whitegrid")  # ustaw przyjemny styl wykresów

RANDOM_STATE = 42  # stałe ziarno losowości (dla powtarzalności)
SAMPLE_N = 8000  # próbka do cięższych wykresów (żeby nie liczyć na 250k wierszy)

# =========================================================
# 1) WCZYTANIE DANYCH
# =========================================================

FILE_PATH = Path("diabetes_binary_health_indicators_BRFSS2015 (3) (1) (1).csv")  # nazwa pliku danych
df = pd.read_csv(FILE_PATH)  # wczytaj plik CSV do DataFrame

print("Wymiary danych (wiersze, kolumny):", df.shape)  # pokaż rozmiar danych
df.head(10)  # pokaż pierwsze 10 wierszy (szybki podgląd)

# =========================================================
# 2) SPRAWDZENIE STRUKTURY DANYCH (typy, kolumny, podstawy)
# =========================================================

print("\nNazwy kolumn:\n", list(df.columns))  # wypisz nazwy kolumn
df.info()  # pokaż typy danych i liczbę niepustych wartości w kolumnach
df.describe().T  # pokaż podstawowe statystyki liczbowe (transponowane)

# =========================================================
# 3) BRAKI DANYCH I DUPLIKATY (kontrola jakości)
# =========================================================

missing_count = df.isna().sum()  # policz braki danych w każdej kolumnie
missing_percent = (missing_count / len(df)) * 100  # policz procent braków w każdej kolumnie

missing_summary = pd.DataFrame({  # zbuduj tabelę podsumowania braków
    "missing_count": missing_count,  # liczba braków
    "missing_percent": missing_percent  # procent braków
}).sort_values("missing_count", ascending=False)  # posortuj od największej liczby braków

print("\nBraki danych (top 10 kolumn):")  # opis
display(missing_summary.head(10))  # pokaż 10 kolumn z największą liczbą braków

duplicates_n = df.duplicated().sum()  # policz liczbę zduplikowanych wierszy
print("\nLiczba duplikatów wierszy:", duplicates_n)  # wyświetl wynik

# Uwaga: w EDA zwykle nie usuwamy nic od razu, tylko najpierw rozumiemy dane.  # komentarz dydaktyczny

# =========================================================
# 4) ZMIENNA DOCELOWA (co chcemy analizować / przewidywać)
# =========================================================

target_col = "Diabetes_binary"  # nazwa zmiennej docelowej (0 = brak, 1 = cukrzyca)

target_counts = df[target_col].value_counts(dropna=False)  # policz liczbę przypadków w klasach
target_percent = df[target_col].value_counts(normalize=True) * 100  # policz procent klas

print("\nRozkład klas (liczności):\n", target_counts)  # pokaż liczności
print("\nRozkład klas (procent):\n", target_percent.round(2))  # pokaż procenty

plt.figure(figsize=(6, 4))  # ustaw rozmiar wykresu
sns.countplot(data=df, x=target_col)  # wykres liczności klas
plt.title("Rozkład klasy docelowej: cukrzyca (1) vs brak (0)")  # tytuł
plt.xlabel("Diabetes_binary")  # podpis osi X
plt.ylabel("Liczba obserwacji")  # podpis osi Y
plt.show()  # pokaż wykres

# =========================================================
# 5) PODZIAŁ KOLUMN NA GRUPY (żeby analizować mądrze)
# =========================================================

"""
W tych danych większość kolumn jest binarna (0/1).
Są też kolumny porządkowe (np. Age, GenHlth, Education, Income),
oraz liczbowe (np. BMI, MentHlth, PhysHlth).

Ten podział pomaga dobrać odpowiedni wykres i sposób analizy.
"""

binary_cols = [  # kolumny binarne (0/1)
    "HighBP", "HighChol", "CholCheck", "Smoker", "Stroke", "HeartDiseaseorAttack",
    "PhysActivity", "Fruits", "Veggies", "HvyAlcoholConsump", "AnyHealthcare",
    "NoDocbcCost", "DiffWalk", "Sex"
]  # koniec listy binarnej

ordinal_cols = ["GenHlth", "Age", "Education", "Income"]  # kolumny porządkowe (liczby oznaczają kategorie)
numeric_cols = ["BMI", "MentHlth", "PhysHlth"]  # kolumny liczbowe (ciągłe lub dyskretne)

all_used = set(binary_cols + ordinal_cols + numeric_cols + [target_col])  # zbiór wszystkich użytych kolumn
unused_cols = [c for c in df.columns if c not in all_used]  # kolumny nieuwzględnione w podziale

print("\nKolumny nieuwzględnione w podziale (sprawdź czy chcesz je dodać):", unused_cols)  # pokaż ewentualne braki

# =========================================================
# 6) MIARY STATYSTYCZNE (średnia, mediana, odchylenie, kwartyle, IQR)
# =========================================================

"""
Miary statystyczne pomagają opisać dane liczbowo:
- średnia i mediana mówią o "typowej" wartości,
- odchylenie standardowe mówi, jak bardzo dane są rozproszone,
- kwartyle i IQR pomagają rozumieć rozkład bez wrażliwości na skrajności.
"""

desc_num = df[numeric_cols].describe().T  # podstawowe statystyki (count, mean, std, min, Q1, Q2, Q3, max)
desc_num["median"] = df[numeric_cols].median()  # dodaj medianę
desc_num["skew"] = df[numeric_cols].skew()  # dodaj skośność
desc_num["kurtosis"] = df[numeric_cols].kurtosis()  # dodaj kurtozę

q1 = df[numeric_cols].quantile(0.25)  # pierwszy kwartyl
q3 = df[numeric_cols].quantile(0.75)  # trzeci kwartyl
iqr = q3 - q1  # rozstęp międzykwartylowy

desc_num["Q1"] = q1  # dodaj Q1
desc_num["Q3"] = q3  # dodaj Q3
desc_num["IQR"] = iqr  # dodaj IQR

print("\nStatystyki dla kolumn liczbowych:")  # opis
display(desc_num)  # pokaż tabelę

# =========================================================
# 7) ROZKŁADY ZMIENNYCH LICZBOWYCH (histogram + KDE)
# =========================================================

for col in numeric_cols:  # przejdź po każdej kolumnie liczbowej
    plt.figure(figsize=(7, 4))  # ustaw rozmiar wykresu
    sns.histplot(df[col], bins=40, kde=True)  # histogram z linią KDE
    plt.title(f"Rozkład zmiennej: {col}")  # tytuł
    plt.xlabel(col)  # podpis osi X
    plt.ylabel("Liczba obserwacji")  # podpis osi Y
    plt.show()  # pokaż wykres

# =========================================================
# 8) OUTLIERY (wartości odstające) – boxploty dla liczb
# =========================================================

for col in numeric_cols:  # iteruj po kolumnach liczbowych
    plt.figure(figsize=(7, 2.5))  # rozmiar wykresu
    sns.boxplot(x=df[col])  # boxplot pokazuje medianę, kwartyle i wartości odstające
    plt.title(f"Wartości odstające (boxplot): {col}")  # tytuł
    plt.xlabel(col)  # podpis osi
    plt.show()  # pokaż wykres

# =========================================================
# 9) PORÓWNANIE LICZBOWYCH CECH DLA KLAS (0 vs 1)
# =========================================================

"""
Tutaj sprawdzamy: czy np. BMI różni się między osobami z cukrzycą i bez cukrzycy?
To jest kluczowy element EDA pod ML: "co odróżnia klasy?"
"""

for col in numeric_cols:  # iteruj po cechach liczbowych
    plt.figure(figsize=(7, 4))  # rozmiar wykresu
    sns.boxplot(data=df, x=target_col, y=col)  # boxplot w podziale na klasy 0/1
    plt.title(f"{col} w podziale na klasy (Diabetes_binary)")  # tytuł
    plt.xlabel("Diabetes_binary (0=brak, 1=cukrzyca)")  # opis osi X
    plt.ylabel(col)  # opis osi Y
    plt.show()  # pokaż wykres

# Dodatkowe zestawienie liczbowe (średnie w klasach)  # komentarz dydaktyczny
means_by_class = df.groupby(target_col)[numeric_cols].mean()  # policz średnie cech liczbowych w klasach
std_by_class = df.groupby(target_col)[numeric_cols].std()  # policz odchylenia w klasach

print("\nŚrednie cech liczbowych w klasach:")  # opis
display(means_by_class)  # pokaż średnie

print("\nOdchylenia standardowe cech liczbowych w klasach:")  # opis
display(std_by_class)  # pokaż odchylenia

# =========================================================
# 10) ZMIENNE BINARNE (0/1) – rozkład i związek z cukrzycą
# =========================================================

"""
Dla zmiennych 0/1 najlepszy jest:
- countplot (ile jest 0 i 1),
- wykres udziału (procent),
- porównanie z targetem (np. odsetek cukrzycy w grupie 0 vs 1).
"""

for col in binary_cols:  # przejdź po każdej zmiennej binarnej
    plt.figure(figsize=(7, 4))  # rozmiar wykresu
    sns.countplot(data=df, x=col, hue=target_col)  # rozkład 0/1 i jednocześnie klasy docelowej
    plt.title(f"{col} vs Diabetes_binary (liczności)")  # tytuł
    plt.xlabel(col)  # oś X
    plt.ylabel("Liczba obserwacji")  # oś Y
    plt.legend(title="Diabetes_binary")  # legenda
    plt.show()  # pokaż wykres

# Tabela: jaki procent cukrzycy jest w grupie 0 i 1 dla każdej cechy binarnej  # komentarz dydaktyczny
binary_target_rates = {}  # słownik na wyniki

for col in binary_cols:  # iteruj po cechach binarnych
    rate = df.groupby(col)[target_col].mean() * 100  # średnia z 0/1 daje odsetek (w %)
    binary_target_rates[col] = rate  # zapisz do słownika

binary_target_rates_df = pd.DataFrame(binary_target_rates).T  # zamień słownik na tabelę
binary_target_rates_df.columns = ["% cukrzycy gdy cecha=0", "% cukrzycy gdy cecha=1"]  # nadaj nazwy kolumn

print("\nOdsetek cukrzycy w grupach 0/1 dla zmiennych binarnych:")  # opis
display(binary_target_rates_df.sort_values("% cukrzycy gdy cecha=1", ascending=False))  # pokaż posortowane

# =========================================================
# 11) ZMIENNE PORZĄDKOWE – rozkłady i związek z targetem
# =========================================================

for col in ordinal_cols:  # iteruj po zmiennych porządkowych
    plt.figure(figsize=(7, 4))  # rozmiar
    sns.countplot(data=df, x=col, hue=target_col)  # rozkład kategorii i podział na target
    plt.title(f"{col} vs Diabetes_binary (liczności)")  # tytuł
    plt.xlabel(col)  # oś X
    plt.ylabel("Liczba obserwacji")  # oś Y
    plt.legend(title="Diabetes_binary")  # legenda
    plt.show()  # pokaż

# Średni odsetek cukrzycy w zależności od kategorii porządkowej  # komentarz dydaktyczny
for col in ordinal_cols:  # iteruj po zmiennych porządkowych
    rate_by_level = df.groupby(col)[target_col].mean() * 100  # odsetek cukrzycy na poziomach
    print(f"\nOdsetek cukrzycy (%) w zależności od {col}:")  # opis
    display(rate_by_level)  # pokaż

# =========================================================
# 12) KORELACJE (heatmapa + lista najmocniejszych)
# =========================================================

"""
Korelacja pokazuje, jak silnie zmienne są ze sobą powiązane liniowo.
Warto pamiętać: korelacja nie oznacza przyczynowości.
To jednak świetny szybki podgląd zależności w danych.
"""

corr = df.corr(numeric_only=True)  # policz macierz korelacji (tylko liczby)

plt.figure(figsize=(12, 9))  # rozmiar wykresu
sns.heatmap(corr, cmap="coolwarm", center=0, square=True)  # heatmapa korelacji
plt.title("Macierz korelacji (heatmapa)")  # tytuł
plt.show()  # pokaż

# Najmocniejsze korelacje ze zmienną docelową  # komentarz dydaktyczny
corr_with_target = corr[target_col].sort_values(ascending=False)  # sortuj korelacje z targetem
print("\nKorelacje z targetem (od największej):")  # opis
display(corr_with_target)  # pokaż

# =========================================================
# 13) PAIRPLOT (opcjonalnie) – na próbce danych (bo pełny zbiór jest duży)
# =========================================================

"""
Pairplot jest bardzo fajny, ale kosztowny obliczeniowo.
Dlatego robimy go na losowej próbce danych.
"""

df_sample = df.sample(n=min(SAMPLE_N, len(df)), random_state=RANDOM_STATE)  # weź próbkę danych
sns.pairplot(df_sample[[target_col] + numeric_cols], hue=target_col, diag_kind="hist")  # pairplot na cechach liczbowych
plt.show()  # pokaż

# =========================================================
# 14) PODSUMOWANIE EDA – wnioski (szablon do uzupełnienia)
# =========================================================

"""
W tym miejscu (w skrypcie) warto dopisać 5–10 zdań wniosków, np.:
- czy klasy są zbalansowane?
- które cechy najbardziej różnią się między 0 i 1?
- czy są outliery w BMI / PhysHlth / MentHlth?
- jakie cechy mają najwyższą korelację z targetem?
- jakie problemy jakości danych zauważyliśmy?
"""