Krok 1: Import bibliotek¶

In [1]:
# Importujemy pandas - bibliotekę do pracy z danymi (jak Excel w Pythonie)
import pandas as pd

# Importujemy PyCaret - narzędzie AutoML, które automatyzuje proces uczenia maszynowego
from pycaret.classification import *

# Importujemy numpy - bibliotekę do obliczeń numerycznych
import numpy as np

print("✅ Biblioteki zaimportowane!")
✅ Biblioteki zaimportowane!

Krok 2: Wczytanie danych¶

In [2]:
# Wczytujemy dane o klientach firmy telekomunikacyjnej z pliku CSV
# CSV to format pliku, gdzie dane są oddzielone przecinkami (jak tabela w Excelu)
df = pd.read_csv('data/WA_Fn-UseC_-Telco-Customer-Churn.csv')

# Wyświetlamy informacje o danych
print(f"📊 Liczba klientów w bazie: {len(df)}")
print(f"📋 Liczba kolumn (cech): {len(df.columns)}")

# Pokazujemy pierwsze 5 wierszy, żeby zobaczyć jak wyglądają dane
print("\n🔍 Pierwsze 5 wierszy danych:")
df.head()
📊 Liczba klientów w bazie: 7043
📋 Liczba kolumn (cech): 21

🔍 Pierwsze 5 wierszy danych:
Out[2]:
customerID gender SeniorCitizen Partner Dependents tenure PhoneService MultipleLines InternetService OnlineSecurity ... DeviceProtection TechSupport StreamingTV StreamingMovies Contract PaperlessBilling PaymentMethod MonthlyCharges TotalCharges Churn
0 7590-VHVEG Female 0 Yes No 1 No No phone service DSL No ... No No No No Month-to-month Yes Electronic check 29.85 29.85 No
1 5575-GNVDE Male 0 No No 34 Yes No DSL Yes ... Yes No No No One year No Mailed check 56.95 1889.5 No
2 3668-QPYBK Male 0 No No 2 Yes No DSL Yes ... No No No No Month-to-month Yes Mailed check 53.85 108.15 Yes
3 7795-CFOCW Male 0 No No 45 No No phone service DSL Yes ... Yes Yes No No One year No Bank transfer (automatic) 42.30 1840.75 No
4 9237-HQITU Female 0 No No 2 Yes No Fiber optic No ... No No No No Month-to-month Yes Electronic check 70.70 151.65 Yes

5 rows × 21 columns

In [3]:
# Sprawdzamy kolumny w danych
print("\n📋 Kolumny w danych:")
print(df.columns.tolist())

# Informacje o typach danych
print("\n🔍 Typy danych:")
print(df.info())
📋 Kolumny w danych:
['customerID', 'gender', 'SeniorCitizen', 'Partner', 'Dependents', 'tenure', 'PhoneService', 'MultipleLines', 'InternetService', 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection', 'TechSupport', 'StreamingTV', 'StreamingMovies', 'Contract', 'PaperlessBilling', 'PaymentMethod', 'MonthlyCharges', 'TotalCharges', 'Churn']

🔍 Typy danych:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7043 entries, 0 to 7042
Data columns (total 21 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   customerID        7043 non-null   object 
 1   gender            7043 non-null   object 
 2   SeniorCitizen     7043 non-null   int64  
 3   Partner           7043 non-null   object 
 4   Dependents        7043 non-null   object 
 5   tenure            7043 non-null   int64  
 6   PhoneService      7043 non-null   object 
 7   MultipleLines     7043 non-null   object 
 8   InternetService   7043 non-null   object 
 9   OnlineSecurity    7043 non-null   object 
 10  OnlineBackup      7043 non-null   object 
 11  DeviceProtection  7043 non-null   object 
 12  TechSupport       7043 non-null   object 
 13  StreamingTV       7043 non-null   object 
 14  StreamingMovies   7043 non-null   object 
 15  Contract          7043 non-null   object 
 16  PaperlessBilling  7043 non-null   object 
 17  PaymentMethod     7043 non-null   object 
 18  MonthlyCharges    7043 non-null   float64
 19  TotalCharges      7043 non-null   object 
 20  Churn             7043 non-null   object 
dtypes: float64(1), int64(2), object(18)
memory usage: 1.1+ MB
None

Krok 3: Przygotowanie danych¶

In [4]:
# Usuwamy kolumnę customerID, bo to tylko numer identyfikacyjny
# Nie pomaga w przewidywaniu (jak numer PESEL - nic nie mówi o zachowaniu klienta)
df = df.drop('customerID', axis=1)

# Sprawdzamy czy są brakujące wartości (puste komórki w danych)
print("🔍 Sprawdzanie brakujących wartości...")
missing = df.isnull().sum()
print(f"\nLiczba brakujących wartości: {missing.sum()}")

# Sprawdzamy rozkład kolumny Churn (ile klientów odeszło, ile zostało)
print("\n📊 Rozkład targetu (Churn):")
print(df['Churn'].value_counts())
print(f"\nProcent klientów, którzy odeszli: {(df['Churn'] == 'Yes').sum() / len(df) * 100:.1f}%")
🔍 Sprawdzanie brakujących wartości...

Liczba brakujących wartości: 0

📊 Rozkład targetu (Churn):
Churn
No     5174
Yes    1869
Name: count, dtype: int64

Procent klientów, którzy odeszli: 26.5%
In [5]:
# TotalCharges powinno być liczbą, ale może być tekstem z spacjami
# Sprawdzamy i naprawiamy
print("\n🔧 Naprawiamy kolumnę TotalCharges...")

# Konwertujemy na liczby, nieprawidłowe wartości zastępujemy NaN
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')

# Sprawdzamy ile mamy NaN
nan_count = df['TotalCharges'].isnull().sum()
print(f"Znaleziono {nan_count} nieprawidłowych wartości w TotalCharges")

if nan_count > 0:
    # Wypełniamy NaN zerem (nowi klienci mają tenure=0, więc TotalCharges=0 ma sens)
    df['TotalCharges'].fillna(0, inplace=True)
    print("✅ Wypełniono zerami (nowi klienci bez historii płatności)")
🔧 Naprawiamy kolumnę TotalCharges...
Znaleziono 11 nieprawidłowych wartości w TotalCharges
✅ Wypełniono zerami (nowi klienci bez historii płatności)

Krok 4: Konfiguracja PyCaret¶

In [6]:
# Inicjalizujemy PyCaret - przygotowujemy środowisko do uczenia maszynowego
# data = nasze dane
# target = kolumna, którą chcemy przewidzieć (czy klient odejdzie: Yes/No)
# session_id = ustawienie ziarna losowości (dla powtarzalności wyników)
# train_size = 80% danych do treningu, 20% do testu (jak nauka do egzaminu)
# fold = 5-fold cross-validation (dzielimy dane na 5 części i testujemy na każdej)
# normalize = normalizujemy dane numeryczne (wszystkie wartości w podobnej skali)

clf_setup = setup(
    data=df,
    target='Churn',
    session_id=123,
    train_size=0.8,
    fold=5,
    normalize=True,
    verbose=False
)

print("\n✅ PyCaret skonfigurowany i gotowy do pracy!")
✅ PyCaret skonfigurowany i gotowy do pracy!

Krok 5: Porównanie modeli i wybór najlepszego¶

In [7]:
# Porównujemy różne algorytmy ML (Gradient Boosting, Random Forest, itd.)
# sort='AUC' - sortujemy modele według AUC (najlepsza metryka dla churn)
# n_select=1 - wybieramy tylko najlepszy model

print("🔄 Porównujemy ~15 różnych algorytmów ML...")
print("To zajmie kilka minut - testujemy każdy algorytm na 5 foldach!\n")

# Porównujemy wszystkie dostępne modele
best_model = compare_models(sort='AUC', n_select=1)

print("\n✅ Najlepszy model wybrany!")
🔄 Porównujemy ~15 różnych algorytmów ML...
To zajmie kilka minut - testujemy każdy algorytm na 5 foldach!

  Model Accuracy AUC Recall Prec. F1 Kappa MCC TT (Sec)
gbc Gradient Boosting Classifier 0.7993 0.8463 0.7993 0.7903 0.7924 0.4534 0.4578 0.3940
lr Logistic Regression 0.8039 0.8457 0.8039 0.7967 0.7985 0.4720 0.4753 1.1140
ada Ada Boost Classifier 0.8019 0.8441 0.8019 0.7936 0.7953 0.4617 0.4661 0.2320
ridge Ridge Classifier 0.8003 0.8373 0.8003 0.7908 0.7921 0.4508 0.4571 0.1240
lda Linear Discriminant Analysis 0.7971 0.8372 0.7971 0.7906 0.7927 0.4586 0.4607 0.1220
lightgbm Light Gradient Boosting Machine 0.7900 0.8359 0.7900 0.7811 0.7837 0.4321 0.4352 0.3060
rf Random Forest Classifier 0.7913 0.8248 0.7913 0.7802 0.7823 0.4242 0.4302 0.2240
svm SVM - Linear Kernel 0.7815 0.8202 0.7815 0.7803 0.7805 0.4351 0.4358 0.1360
nb Naive Bayes 0.6926 0.8193 0.6926 0.7965 0.7105 0.3795 0.4258 0.8480
et Extra Trees Classifier 0.7746 0.7984 0.7746 0.7630 0.7663 0.3839 0.3880 0.2220
knn K Neighbors Classifier 0.7581 0.7739 0.7581 0.7535 0.7552 0.3657 0.3667 0.8880
qda Quadratic Discriminant Analysis 0.6793 0.6575 0.6793 0.6862 0.6696 0.1771 0.1855 0.1380
dt Decision Tree Classifier 0.7240 0.6522 0.7240 0.7277 0.7257 0.3013 0.3014 0.7560
dummy Dummy Classifier 0.7346 0.5000 0.7346 0.5397 0.6223 0.0000 0.0000 0.1180
✅ Najlepszy model wybrany!

Krok 6: Zapisanie wyników PRZED tuningiem¶

In [8]:
# Trenujemy najlepszy model jeszcze raz, żeby zobaczyć szczegółowe wyniki
# fold=5 - testujemy na 5 różnych podzbiór danych (cross-validation)

print("📊 Trenujemy najlepszy model i zapisujemy wyniki PRZED tuningiem...\n")

# Tworzymy model (ten sam algorytm co wybrany w compare_models)
model_before = create_model(best_model, fold=5)

# Pobieramy wyniki cross-validation (średnie wyniki z 5 testów)
# pull() pobiera ostatnią tabelę wyników
results_before = pull()

# Zapisujemy kluczowe metryki PRZED tuningiem
# loc['Mean'] - bierzemy wiersz ze średnimi wynikami
accuracy_before = results_before.loc['Mean', 'Accuracy']
auc_before = results_before.loc['Mean', 'AUC']
recall_before = results_before.loc['Mean', 'Recall']
precision_before = results_before.loc['Mean', 'Prec.']

print("\n✅ Wyniki PRZED tuningiem zapisane!")
print(f"\n📈 Kluczowe metryki PRZED tuningiem:")
print(f"   - Accuracy (dokładność ogólna): {accuracy_before:.4f}")
print(f"   - AUC (zdolność rozróżniania): {auc_before:.4f}")
print(f"   - Recall (% wykrytych odejść): {recall_before:.4f}")
print(f"   - Precision (% trafnych alertów): {precision_before:.4f}")
📊 Trenujemy najlepszy model i zapisujemy wyniki PRZED tuningiem...

  Accuracy AUC Recall Prec. F1 Kappa MCC
Fold              
0 0.7924 0.8331 0.7924 0.7853 0.7879 0.4461 0.4477
1 0.7968 0.8504 0.7968 0.7861 0.7883 0.4399 0.4455
2 0.7986 0.8418 0.7986 0.7899 0.7925 0.4548 0.4580
3 0.7941 0.8431 0.7941 0.7853 0.7881 0.4434 0.4464
4 0.8144 0.8634 0.8144 0.8050 0.8051 0.4827 0.4913
Mean 0.7993 0.8463 0.7993 0.7903 0.7924 0.4534 0.4578
Std 0.0079 0.0101 0.0079 0.0075 0.0066 0.0155 0.0173
✅ Wyniki PRZED tuningiem zapisane!

📈 Kluczowe metryki PRZED tuningiem:
   - Accuracy (dokładność ogólna): 0.7993
   - AUC (zdolność rozróżniania): 0.8463
   - Recall (% wykrytych odejść): 0.7993
   - Precision (% trafnych alertów): 0.7903

Krok 7: Tuning modelu (optymalizacja)¶

In [ ]:
# Tunujemy model - szukamy najlepszych ustawień (hiperparametrów)
# To jak regulacja silnika w samochodzie - szukamy optymalnych ustawień
# optimize='AUC' - optymalizujemy pod kątem AUC (najważniejsza metryka dla churn)
# do optymalizacji używamy domyślnego scikit-optimize (w tym przypadku nie modyfikujemy tego podejścia)
# n_iter=20 - testujemy 20 różnych kombinacji ustawień

print("🔧 Rozpoczynamy tuning modelu...")
print("Szukamy najlepszych ustawień algorytmu (hiperparametrów)")
print("To zajmie kilka minut - testujemy 20 różnych konfiguracji!\n")

# Tunujemy model
model_after = tune_model(model_before, optimize='AUC', n_iter=20)

# Pobieramy wyniki PO tuningu
results_after = pull()

# Zapisujemy kluczowe metryki PO tuningu
accuracy_after = results_after.loc['Mean', 'Accuracy']
auc_after = results_after.loc['Mean', 'AUC']
recall_after = results_after.loc['Mean', 'Recall']
precision_after = results_after.loc['Mean', 'Prec.']

print("\n✅ Tuning zakończony!")
print(f"\n📈 Kluczowe metryki PO tuningu:")
print(f"   - Accuracy (dokładność ogólna): {accuracy_after:.4f}")
print(f"   - AUC (zdolność rozróżniania): {auc_after:.4f}")
print(f"   - Recall (% wykrytych odejść): {recall_after:.4f}")
print(f"   - Precision (% trafnych alertów): {precision_after:.4f}")
🔧 Rozpoczynamy tuning modelu...
Szukamy najlepszych ustawień algorytmu (hiperparametrów)
To zajmie kilka minut - testujemy 20 różnych konfiguracji!

  Accuracy AUC Recall Prec. F1 Kappa MCC
Fold              
0 0.7933 0.8359 0.7933 0.7800 0.7718 0.3834 0.4087
1 0.7959 0.8545 0.7959 0.7851 0.7717 0.3813 0.4136
2 0.7959 0.8474 0.7959 0.7829 0.7768 0.3980 0.4200
3 0.7844 0.8463 0.7844 0.7685 0.7611 0.3537 0.3790
4 0.8011 0.8622 0.8011 0.7939 0.7755 0.3912 0.4296
Mean 0.7941 0.8493 0.7941 0.7821 0.7714 0.3815 0.4102
Std 0.0055 0.0088 0.0055 0.0082 0.0055 0.0151 0.0171
Fitting 5 folds for each of 20 candidates, totalling 100 fits

✅ Tuning zakończony!

📈 Kluczowe metryki PO tuningu:
   - Accuracy (dokładność ogólna): 0.7941
   - AUC (zdolność rozróżniania): 0.8493
   - Recall (% wykrytych odejść): 0.7941
   - Precision (% trafnych alertów): 0.7821

Krok 8: Porównanie wyników PRZED vs PO tuningu¶

In [10]:
# Obliczamy różnice między wynikami przed i po tuningu
# Różnica w punktach procentowych (p.p.) pokazuje realną poprawę

print("\n" + "="*60)
print("📊 PORÓWNANIE: MODEL PRZED vs PO TUNINGU")
print("="*60)

# Obliczamy różnice (w punktach procentowych)
accuracy_diff = (accuracy_after - accuracy_before) * 100
auc_diff = (auc_after - auc_before) * 100
recall_diff = (recall_after - recall_before) * 100
precision_diff = (precision_after - precision_before) * 100

# Wyświetlamy szczegółowe porównanie
print(f"\n1️⃣ ACCURACY (Dokładność ogólna):")
print(f"   Przed: {accuracy_before:.4f} ({accuracy_before*100:.2f}%)")
print(f"   Po:    {accuracy_after:.4f} ({accuracy_after*100:.2f}%)")
print(f"   Zmiana: {accuracy_diff:+.2f} punktów procentowych")

print(f"\n2️⃣ AUC (Zdolność rozróżniania klas):")
print(f"   Przed: {auc_before:.4f}")
print(f"   Po:    {auc_after:.4f}")
print(f"   Zmiana: {auc_diff:+.2f} punktów procentowych")

print(f"\n3️⃣ RECALL (Ile % odchodzących klientów wykrywamy):")
print(f"   Przed: {recall_before:.4f} ({recall_before*100:.2f}%)")
print(f"   Po:    {recall_after:.4f} ({recall_after*100:.2f}%)")
print(f"   Zmiana: {recall_diff:+.2f} punktów procentowych")

print(f"\n4️⃣ PRECISION (Ile % naszych alertów jest trafnych):")
print(f"   Przed: {precision_before:.4f} ({precision_before*100:.2f}%)")
print(f"   Po:    {precision_after:.4f} ({precision_after*100:.2f}%)")
print(f"   Zmiana: {precision_diff:+.2f} punktów procentowych")

print("\n" + "="*60)
============================================================
📊 PORÓWNANIE: MODEL PRZED vs PO TUNINGU
============================================================

1️⃣ ACCURACY (Dokładność ogólna):
   Przed: 0.7993 (79.93%)
   Po:    0.7941 (79.41%)
   Zmiana: -0.52 punktów procentowych

2️⃣ AUC (Zdolność rozróżniania klas):
   Przed: 0.8463
   Po:    0.8493
   Zmiana: +0.30 punktów procentowych

3️⃣ RECALL (Ile % odchodzących klientów wykrywamy):
   Przed: 0.7993 (79.93%)
   Po:    0.7941 (79.41%)
   Zmiana: -0.52 punktów procentowych

4️⃣ PRECISION (Ile % naszych alertów jest trafnych):
   Przed: 0.7903 (79.03%)
   Po:    0.7821 (78.21%)
   Zmiana: -0.82 punktów procentowych

============================================================

Krok 9: Analiza biznesowa - Czy tuning ma sens?¶

In [12]:
# Obliczamy czy poprawa jest istotna biznesowo
# Zakładamy bazę 10,000 klientów, z czego 27% chce odejść (2,700 klientów)

total_customers = 10000  # Całkowita liczba klientów
churn_rate = 0.27  # 27% klientów odchodzi (na podstawie naszych danych)
churning_customers = int(total_customers * churn_rate)  # Klienci faktycznie odchodzący

# Koszt retencji (próba zatrzymania klienta) - np. telefon + oferta specjalna
retention_cost = 50  # 50 zł za próbę zatrzymania

# Wartość klienta - ile tracimy gdy klient odejdzie
customer_value = 500  # 500 zł rocznie (średni przychód)

print("\n" + "="*60)
print("💰 ANALIZA BIZNESOWA - CZY TUNING SIĘ OPŁACA?")
print("="*60)

print(f"\n📊 Założenia:")
print(f"   - Baza klientów: {total_customers:,}")
print(f"   - Klienci odchodzący: {churning_customers:,} ({churn_rate*100:.0f}%)")
print(f"   - Koszt próby zatrzymania: {retention_cost} zł")
print(f"   - Wartość klienta rocznie: {customer_value} zł")

# Ile klientów wykrywamy PRZED tuningiem
detected_before = int(churning_customers * recall_before)
# Ile klientów wykrywamy PO tuningu
detected_after = int(churning_customers * recall_after)
# Ile więcej klientów wykrywamy dzięki tuningowi
additional_detected = detected_after - detected_before

print(f"\n🎯 Wykrywanie klientów:")
print(f"   - Przed tuningiem: {detected_before:,} klientów")
print(f"   - Po tuningu: {detected_after:,} klientów")
print(f"   - DODATKOWO wykrytych: {additional_detected:,} klientów")

# Zakładamy, że 30% wykrytych klientów uda się zatrzymać
retention_success_rate = 0.30

# Ile klientów zatrzymamy dodatkowo dzięki tuningowi
additional_retained = int(additional_detected * retention_success_rate)

# Dodatkowy koszt (więcej prób = więcej kosztów)
additional_cost = additional_detected * retention_cost

# Dodatkowy przychód (zatrzymani klienci = zachowany przychód)
additional_revenue = additional_retained * customer_value

# Zysk netto z tuningu
net_benefit = additional_revenue - additional_cost

print(f"\n💼 Skutki biznesowe (przy 30% skuteczności retencji):")
print(f"   - Dodatkowo zatrzymanych klientów: {additional_retained:,}")
print(f"   - Dodatkowy koszt retencji: {additional_cost:,} zł")
print(f"   - Dodatkowy przychód (zatrzymani): {additional_revenue:,} zł")
if net_benefit > 0:
    print(f"   - ZYSK NETTO Z TUNINGU: {net_benefit:,} zł rocznie")
else:
    print(f"   - STRATA NETTO Z TUNINGU: {net_benefit:,} zł rocznie")

print("\n" + "="*60)
============================================================
💰 ANALIZA BIZNESOWA - CZY TUNING SIĘ OPŁACA?
============================================================

📊 Założenia:
   - Baza klientów: 10,000
   - Klienci odchodzący: 2,700 (27%)
   - Koszt próby zatrzymania: 50 zł
   - Wartość klienta rocznie: 500 zł

🎯 Wykrywanie klientów:
   - Przed tuningiem: 2,158 klientów
   - Po tuningu: 2,144 klientów
   - DODATKOWO wykrytych: -14 klientów

💼 Skutki biznesowe (przy 30% skuteczności retencji):
   - Dodatkowo zatrzymanych klientów: -4
   - Dodatkowy koszt retencji: -700 zł
   - Dodatkowy przychód (zatrzymani): -2,000 zł
   - STRATA NETTO Z TUNINGU: -1,300 zł rocznie

============================================================

Krok 10: Wnioski końcowe dla biznesu¶

In [13]:
# Podsumowanie - czy tuning ma sens?
# Oceniamy na podstawie poprawy metryk i zysku netto

print("\n" + "="*60)
print("🎯 WNIOSKI - CZY TUNING MA SENS BIZNESOWY?")
print("="*60)

# Sprawdzamy czy AUC się poprawił o więcej niż 0.5%
if auc_diff > 0.5:
    print("\n✅ TUNING DAŁ REALNĄ POPRAWĘ TECHNICZNĄ!")
    print(f"   AUC wzrósł o {auc_diff:.2f} punktów procentowych")
    print(f"   To znacząca poprawa zdolności modelu do rozróżniania klientów")
elif auc_diff > 0:
    print("\n⚠️ TUNING DAŁ NIEWIELKĄ POPRAWĘ TECHNICZNĄ")
    print(f"   AUC wzrósł o {auc_diff:.2f} punktów procentowych")
    print(f"   Poprawa jest minimalna, model niewiele zyskał")
else:
    print("\n❌ TUNING NIE POPRAWIŁ MODELU")
    print(f"   AUC zmienił się o {auc_diff:.2f} punktów procentowych")
    print(f"   Model nie zyskał na tuningu")

# Sprawdzamy czy recall się poprawił (czy wykrywamy więcej klientów)
if recall_diff > 1.0:
    print("\n✅ WYKRYWAMY ZNACZNIE WIĘCEJ ODCHODZĄCYCH KLIENTÓW!")
    print(f"   Recall wzrósł o {recall_diff:.2f} punktów procentowych")
    print(f"   Wykrywamy {additional_detected:,} więcej klientów zagrożonych odejściem")
elif recall_diff > 0:
    print("\n✅ WYKRYWAMY TROCHĘ WIĘCEJ ODCHODZĄCYCH KLIENTÓW")
    print(f"   Recall wzrósł o {recall_diff:.2f} punktów procentowych")
    print(f"   Wykrywamy {additional_detected:,} więcej klientów zagrożonych odejściem")
else:
    print("\n⚠️ NIE WYKRYWAMY WIĘCEJ KLIENTÓW")
    print(f"   Recall zmienił się o {recall_diff:.2f} punktów procentowych")

# Analiza zysku netto
print("\n💰 ANALIZA FINANSOWA:")
if net_benefit > 10000:
    print(f"   ✅ TUNING MA DUŻY SENS BIZNESOWY!")
    print(f"   Roczny zysk: {net_benefit:,} zł")
    print(f"   ROI: {(net_benefit/additional_cost)*100:.0f}% (świetny zwrot z inwestycji!)")
elif net_benefit > 0:
    print(f"   ✅ TUNING MA SENS BIZNESOWY")
    print(f"   Roczny zysk: {net_benefit:,} zł")
    print(f"   ROI: {(net_benefit/additional_cost)*100:.0f}% (opłaca się!)")
else:
    print(f"   ❌ TUNING NIE MA SENSU BIZNESOWEGO")
    print(f"   Strata: {abs(net_benefit):,} zł rocznie")
    print(f"   Koszt retencji przewyższa korzyści")

# Ostateczna rekomendacja
print("\n🎯 REKOMENDACJA:")
if net_benefit > 5000 and auc_diff > 0.3:
    print("   🌟 ZDECYDOWANIE WDRAŻAJ MODEL PO TUNINGU!")
    print("   Tuning dał realną poprawę techniczną i biznesową")
    print("   Model będzie lepiej wykrywał klientów zagrożonych odejściem")
elif net_benefit > 0:
    print("   ✅ WARTO WDROŻYĆ MODEL PO TUNINGU")
    print("   Tuning dał niewielką, ale pozytywną poprawę")
    print("   Model będzie generował dodatkowy zysk")
else:
    print("   ⚠️ ZOSTAŃ PRZY MODELU PODSTAWOWYM")
    print("   Tuning nie dał poprawy lub okazał się nawet gorszy")
    print("   Model podstawowy jest wystarczający")

print("\n" + "="*60)
============================================================
🎯 WNIOSKI - CZY TUNING MA SENS BIZNESOWY?
============================================================

⚠️ TUNING DAŁ NIEWIELKĄ POPRAWĘ TECHNICZNĄ
   AUC wzrósł o 0.30 punktów procentowych
   Poprawa jest minimalna, model niewiele zyskał

⚠️ NIE WYKRYWAMY WIĘCEJ KLIENTÓW
   Recall zmienił się o -0.52 punktów procentowych

💰 ANALIZA FINANSOWA:
   ❌ TUNING NIE MA SENSU BIZNESOWEGO
   Strata: 1,300 zł rocznie
   Koszt retencji przewyższa korzyści

🎯 REKOMENDACJA:
   ⚠️ ZOSTAŃ PRZY MODELU PODSTAWOWYM
   Tuning nie dał poprawy lub okazał się nawet gorszy
   Model podstawowy jest wystarczający

============================================================

Krok 11: Zapisanie najlepszego modelu¶

In [15]:
# Zapisujemy model przed tuningiem lub po tuningu do pliku (w zależności od wybranej ostatecznej opcji)
# Dzięki temu możemy go później użyć bez ponownego trenowania

print("💾 Zapisywanie modelu...")

# Zapisujemy model do folderu 'models'. Wybieramy model przed tuningiem bo był wystarczający a nawet lepszy
save_model(model_before, 'models/churn_best_model')

print("✅ Model zapisany w folderze 'models/churn_model'!")
print("\n📝 Możesz go później wczytać używając: load_model('models/churn_model')")
💾 Zapisywanie modelu...
Transformation Pipeline and Model Successfully Saved
✅ Model zapisany w folderze 'models/churn_model'!

📝 Możesz go później wczytać używając: load_model('models/churn_model')

📚 Podsumowanie - Co to wszystko znaczy?¶

Co zrobiliśmy?¶

  1. Wytrenowaliśmy model - algorytm uczący się przewidywać odejścia klientów
  2. Zoptymalizowaliśmy go (tuning) - szukaliśmy najlepszych ustawień
  3. Porównaliśmy wyniki - przed i po tuningu
  4. Oceniliśmy wartość biznesową - czy to się opłaca finansowo

Kluczowe metryki wyjaśnione:¶

  • Accuracy = Ile % wszystkich przewidywań jest prawidłowych
  • AUC = Jak dobrze model rozróżnia klientów (0.5 = losowanie, 1.0 = perfekcja)
  • Recall = Ile % odchodzących klientów wykrywamy
  • Precision = Ile % naszych alertów "klient odejdzie" jest trafnych

Co to znaczy w praktyce?¶

  • Recall 80% = Na 100 klientów, którzy odejdą, wykryjemy 80
  • Precision 75% = Na 100 alertów, 75 będzie prawdziwych, 25 fałszywych

Czy tuning się opłaca?¶

Sprawdź wyniki powyżej! Model sam obliczy czy poprawa jest warta czasu i pieniędzy.