# -*- coding: utf-8 -*-
"""Regresja Logistyczna_wzor.ipynb

Automatically generated by Colab.

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

# =========================================================
# WZORCOWY PROJEKT ML (Regresja Logistyczna) – DIABETES BRFSS 2015
# =========================================================
# Cel:
# 1) Wczytać dane (CSV)
# 2) Przygotować X i y
# 3) Podzielić dane na train i test
# 4) Zbalansować tylko TRAIN (SMOTE)
# 5) Zeskalować dane (StandardScaler)
# 6) Wytrenować LogisticRegression
# 7) Sprawdzić metryki i zrobić wykresy (CM, ROC, PR)
# 8) Zapisać model do pliku i wczytać go z powrotem
# 9) Zapytać użytkownika o parametry i zdiagnozować cukrzycę
# =========================================================


# ---------------------------
# 0) INSTALACJE (tylko w Colabie, jeśli brakuje pakietu)
# ---------------------------

# Jeśli w Colabie nie masz imbalanced-learn, odkomentuj tę linię.  # komentarz
# !pip -q install imbalanced-learn  # instalacja biblioteki do SMOTE (balansowanie)


# ---------------------------
# 1) IMPORTY
# ---------------------------

import pandas as pd  # praca z danymi tabelarycznymi (DataFrame)
import numpy as np  # obliczenia numeryczne
import matplotlib.pyplot as plt  # wykresy
import seaborn as sns  # wykresy statystyczne
from pathlib import Path  # ścieżki do plików

from sklearn.model_selection import train_test_split  # podział danych na train/test
from sklearn.preprocessing import StandardScaler  # skalowanie cech
from sklearn.linear_model import LogisticRegression  # model regresji logistycznej

from sklearn.metrics import accuracy_score  # accuracy
from sklearn.metrics import precision_score  # precision
from sklearn.metrics import recall_score  # recall
from sklearn.metrics import f1_score  # F1
from sklearn.metrics import confusion_matrix  # macierz pomyłek
from sklearn.metrics import classification_report  # raport klasyfikacji
from sklearn.metrics import roc_curve  # punkty krzywej ROC
from sklearn.metrics import roc_auc_score  # AUC
from sklearn.metrics import precision_recall_curve  # krzywa precision-recall
from sklearn.metrics import average_precision_score  # AP (pole pod PR)

from imblearn.over_sampling import SMOTE  # SMOTE do balansowania klas

import pickle  # zapis/wczytanie modelu jako plik binarny

sns.set_theme(style="whitegrid")  # ładny styl wykresów


# ---------------------------
# 2) WCZYTANIE DANYCH
# ---------------------------

FILE_PATH = Path("diabetes_binary_health_indicators_BRFSS2015 (3) (1) (1).csv")  # ścieżka do pliku CSV
df = pd.read_csv(FILE_PATH)  # wczytaj dane z CSV do DataFrame

print("Wymiary danych (wiersze, kolumny):", df.shape)  # szybka informacja o rozmiarze danych
print("Pierwsze 3 wiersze danych:")  # opis
display(df.head(3))  # podgląd danych


# ---------------------------
# 3) MINIMALNA KONTROLA JAKOŚCI (bez powtarzania pełnej EDA)
# ---------------------------

"""
W EDA sprawdzaliśmy dane dokładniej.
Tutaj robimy tylko krótką kontrolę, żeby modelowanie było bezpieczne:
- czy są braki danych?
- czy target ma tylko 0/1?
- czy typy są liczbowe?
"""

print("\nBraki danych w całym zbiorze (suma):", df.isna().sum().sum())  # suma braków w całym zbiorze
print("Unikalne wartości w Diabetes_binary:", df["Diabetes_binary"].unique())  # sprawdź klasy 0/1


# ---------------------------
# 4) WYBÓR ZMIENNEJ DOCELOWEJ I CECH
# ---------------------------

target_col = "Diabetes_binary"  # nazwa kolumny, którą chcemy przewidywać (0/1)

X = df.drop(columns=[target_col])  # X = cechy (wszystko oprócz targetu)
y = df[target_col]  # y = target (0/1)

feature_names = list(X.columns)  # zapamiętaj nazwy cech (ważne do eksportu i późniejszego użycia)

print("\nLiczba cech (kolumn wejściowych):", X.shape[1])  # ile cech mamy w modelu
print("Nazwy cech:", feature_names)  # pokaż nazwy cech


# ---------------------------
# 5) PODZIAŁ NA TRAIN / TEST
# ---------------------------

"""
To jest kluczowa zasada:
- model uczy się tylko na TRAIN
- ocena jakości jest na TEST (danych niewidzianych)

Używamy stratify=y, aby proporcje klas były podobne w train i test.
"""

X_train, X_test, y_train, y_test = train_test_split(  # wykonaj podział
    X,  # cechy
    y,  # target
    test_size=0.2,  # 20% danych na test
    random_state=42,  # powtarzalność
    stratify=y  # zachowaj proporcje klas
)

print("\nRozmiary zbiorów:")  # opis
print("X_train:", X_train.shape, "X_test:", X_test.shape)  # pokaż wymiary
print("y_train:", y_train.shape, "y_test:", y_test.shape)  # pokaż wymiary


# ---------------------------
# 6) BALANSOWANIE KLAS (SMOTE) – TYLKO NA TRAIN
# ---------------------------

"""
Dane są niezbalansowane (zwykle więcej osób bez cukrzycy niż z cukrzycą).
SMOTE tworzy „syntetyczne” przykłady klasy mniejszościowej.
UWAGA: SMOTE robimy TYLKO na TRAIN, bo inaczej „przeciekamy” informacją do testu.
"""

smote = SMOTE(random_state=42)  # utwórz obiekt SMOTE
X_train_res, y_train_res = smote.fit_resample(X_train, y_train)  # zbalansuj train

print("\nRozkład klas przed SMOTE (train):")  # opis
print(y_train.value_counts())  # ile było klas w train

print("\nRozkład klas po SMOTE (train):")  # opis
print(pd.Series(y_train_res).value_counts())  # ile jest klas po SMOTE


# ---------------------------
# 7) SKALOWANIE DANYCH (StandardScaler)
# ---------------------------

"""
Regresja logistyczna działa lepiej, gdy cechy mają podobną skalę.
StandardScaler robi:
- odejmuje średnią
- dzieli przez odchylenie standardowe

BARDZO WAŻNE:
- scaler FIT na TRAIN (po SMOTE)
- scaler TRANSFORM na TRAIN i TEST
"""

scaler = StandardScaler()  # utwórz scaler

X_train_scaled = scaler.fit_transform(X_train_res)  # dopasuj scaler do train i przeskaluj train
X_test_scaled = scaler.transform(X_test)  # przeskaluj test tym samym scalerem (bez fit!)

print("\nWymiary po skalowaniu:")  # opis
print("X_train_scaled:", X_train_scaled.shape)  # wymiary train
print("X_test_scaled:", X_test_scaled.shape)  # wymiary test


# ---------------------------
# 8) TRENING MODELU: REGRESJA LOGISTYCZNA
# ---------------------------

"""
Regresja logistyczna:
- uczy się zależności między cechami a klasą 0/1
- zwraca też prawdopodobieństwo (predict_proba)

Ustawiamy max_iter=1000, aby model miał czas „dojść do rozwiązania”.
"""

model = LogisticRegression(max_iter=1000, random_state=42)  # utwórz model
model.fit(X_train_scaled, y_train_res)  # naucz model na zbalansowanym train


# ---------------------------
# 9) PREDYKCJA (TEST) + PRAWDOPODOBIEŃSTWA
# ---------------------------

y_pred = model.predict(X_test_scaled)  # przewidywana klasa (0/1)
y_proba = model.predict_proba(X_test_scaled)[:, 1]  # prawdopodobieństwo klasy 1 (cukrzyca)


# ---------------------------
# 10) METRYKI JAKOŚCI MODELU
# ---------------------------

"""
W medycznych przykładach szczególnie ważny bywa recall (czułość):
- ile prawdziwych przypadków cukrzycy wykryliśmy?

Ale pokazujemy pełen pakiet metryk:
- accuracy, precision, recall, f1
- confusion matrix
- classification report
"""

acc = accuracy_score(y_test, y_pred)  # accuracy
prec = precision_score(y_test, y_pred, zero_division=0)  # precision
rec = recall_score(y_test, y_pred, zero_division=0)  # recall
f1 = f1_score(y_test, y_pred, zero_division=0)  # f1

print("\n=== METRYKI (na zbiorze testowym) ===")  # nagłówek
print(f"Accuracy : {acc:.4f}")  # accuracy
print(f"Precision: {prec:.4f}")  # precision
print(f"Recall   : {rec:.4f}")  # recall
print(f"F1-score : {f1:.4f}")  # f1

print("\n=== RAPORT KLASYFIKACJI ===")  # nagłówek
print(classification_report(y_test, y_pred, digits=4))  # raport klasyfikacji


# ---------------------------
# 11) MACIERZ POMYŁEK + HEATMAPA
# ---------------------------

cm = confusion_matrix(y_test, y_pred)  # policz macierz pomyłek

plt.figure(figsize=(7, 5))  # rozmiar wykresu
sns.heatmap(  # heatmapa
    cm,  # macierz
    annot=True,  # pokaż liczby
    fmt="d",  # format liczb (integer)
    cmap="Blues",  # kolory
    xticklabels=["brak cukrzycy (0)", "cukrzyca (1)"],  # etykiety osi X
    yticklabels=["brak cukrzycy (0)", "cukrzyca (1)"]  # etykiety osi Y
)
plt.title("Macierz pomyłek (Confusion Matrix)")  # tytuł
plt.xlabel("Klasa przewidziana")  # oś X
plt.ylabel("Klasa rzeczywista")  # oś Y
plt.show()  # pokaż wykres


# ---------------------------
# 12) KRZYWA ROC + AUC
# ---------------------------

auc = roc_auc_score(y_test, y_proba)  # policz AUC
fpr, tpr, thresholds = roc_curve(y_test, y_proba)  # punkty ROC

plt.figure(figsize=(7, 5))  # rozmiar
plt.plot(fpr, tpr, lw=2, label=f"ROC (AUC = {auc:.4f})")  # krzywa ROC
plt.plot([0, 1], [0, 1], linestyle="--", lw=2, label="Losowy klasyfikator")  # przekątna
plt.title("Krzywa ROC")  # tytuł
plt.xlabel("False Positive Rate (FPR)")  # oś X
plt.ylabel("True Positive Rate (TPR)")  # oś Y
plt.legend()  # legenda
plt.show()  # pokaż


# ---------------------------
# 13) KRZYWA PRECISION–RECALL + AVERAGE PRECISION (AP)
# ---------------------------

precision, recall, pr_thresholds = precision_recall_curve(y_test, y_proba)  # policz PR curve
ap = average_precision_score(y_test, y_proba)  # pole pod krzywą PR

plt.figure(figsize=(7, 5))  # rozmiar
plt.plot(recall, precision, lw=2, label=f"PR curve (AP = {ap:.4f})")  # wykres PR
plt.title("Krzywa Precision–Recall")  # tytuł
plt.xlabel("Recall")  # oś X
plt.ylabel("Precision")  # oś Y
plt.legend()  # legenda
plt.show()  # pokaż


# ---------------------------
# 14) ROZKŁAD PRAWDOPODOBIEŃSTW (czy model rozróżnia klasy?)
# ---------------------------

plt.figure(figsize=(8, 5))  # rozmiar
sns.histplot(y_proba[y_test == 0], bins=40, kde=True, label="Brak cukrzycy (0)")  # rozkład proba dla klasy 0
sns.histplot(y_proba[y_test == 1], bins=40, kde=True, label="Cukrzyca (1)")  # rozkład proba dla klasy 1
plt.title("Rozkład przewidywanego prawdopodobieństwa klasy 1")  # tytuł
plt.xlabel("P(y=1) = prawdopodobieństwo cukrzycy")  # oś X
plt.ylabel("Liczba przypadków")  # oś Y
plt.legend()  # legenda
plt.show()  # pokaż


# ---------------------------
# 15) ZAPIS MODELU DO PLIKU (eksport)
# ---------------------------

"""
W praktyce nie zapisujemy tylko samego modelu.
Musimy zapisać też:
- scaler (bo bez niego nowe dane będą w innej skali)
- listę cech i ich kolejność (żeby użytkownik podawał dane w tej samej kolejności)
"""

artifact = {  # tworzymy „paczkę” do zapisu
    "model": model,  # wytrenowany model
    "scaler": scaler,  # dopasowany scaler
    "feature_names": feature_names  # kolejność cech
}

MODEL_FILE = "logreg_diabetes_model.pkl"  # nazwa pliku binarnego

with open(MODEL_FILE, "wb") as f:  # otwórz plik do zapisu binarnego
    pickle.dump(artifact, f)  # zapisz paczkę do pliku

print("\nModel zapisany do pliku:", MODEL_FILE)  # potwierdzenie


# ---------------------------
# 16) WCZYTANIE MODELU (import) – test, czy wszystko działa
# ---------------------------

with open(MODEL_FILE, "rb") as f:  # otwórz plik binarny do odczytu
    loaded_artifact = pickle.load(f)  # wczytaj paczkę

loaded_model = loaded_artifact["model"]  # wyciągnij model
loaded_scaler = loaded_artifact["scaler"]  # wyciągnij scaler
loaded_feature_names = loaded_artifact["feature_names"]  # wyciągnij listę cech

# Krótki test: predykcja na pierwszych 5 rekordach testu  # komentarz
test_scaled_again = loaded_scaler.transform(X_test)  # przeskaluj test tak jak wcześniej
test_pred_again = loaded_model.predict(test_scaled_again[:5])  # przewidź klasy dla 5 pierwszych

print("\nTest po imporcie (pierwsze 5 predykcji):", test_pred_again)  # pokaż wynik

# ---------------------------
# 17) „DIAGNOZA” – zapytaj użytkownika o cechy i przewidź cukrzycę
# ---------------------------

"""
To jest prosty „interfejs tekstowy”.
Użytkownik wpisuje wartości cech (tak jak w danych), a my:
1) tworzymy wiersz danych w odpowiedniej kolejności
2) skalujemy go scalerem
3) liczymy predykcję i prawdopodobieństwo

WAŻNE: To jest przykład edukacyjny, nie narzędzie medyczne.
"""

def ask_float(name, hint):  # funkcja pomocnicza do wczytywania liczb
    value = float(input(f"{name} ({hint}): "))  # pobierz wartość od użytkownika
    return value  # zwróć wartość

print("\n=== WPROWADŹ DANE PACJENTA (jak w zbiorze) ===")  # nagłówek

user_data = {}  # słownik na dane od użytkownika

# Każda linia poniżej pyta o jedną cechę.  # komentarz
user_data["HighBP"] = ask_float("HighBP", "0 lub 1 (wysokie ciśnienie)")  # HighBP
user_data["HighChol"] = ask_float("HighChol", "0 lub 1 (wysoki cholesterol)")  # HighChol
user_data["CholCheck"] = ask_float("CholCheck", "0 lub 1 (badanie cholesterolu)")  # CholCheck
user_data["BMI"] = ask_float("BMI", "np. 18–50 (wskaźnik BMI)")  # BMI
user_data["Smoker"] = ask_float("Smoker", "0 lub 1 (czy palił >=100 papierosów)")  # Smoker
user_data["Stroke"] = ask_float("Stroke", "0 lub 1 (czy miał udar)")  # Stroke
user_data["HeartDiseaseorAttack"] = ask_float("HeartDiseaseorAttack", "0 lub 1 (choroba serca/zawał)")  # HeartDiseaseorAttack
user_data["PhysActivity"] = ask_float("PhysActivity", "0 lub 1 (aktywność fizyczna)")  # PhysActivity
user_data["Fruits"] = ask_float("Fruits", "0 lub 1 (czy je owoce)")  # Fruits
user_data["Veggies"] = ask_float("Veggies", "0 lub 1 (czy je warzywa)")  # Veggies
user_data["HvyAlcoholConsump"] = ask_float("HvyAlcoholConsump", "0 lub 1 (duże spożycie alkoholu)")  # HvyAlcoholConsump
user_data["AnyHealthcare"] = ask_float("AnyHealthcare", "0 lub 1 (ubezpieczenie/opieka)")  # AnyHealthcare
user_data["NoDocbcCost"] = ask_float("NoDocbcCost", "0 lub 1 (czy koszt blokował wizytę)")  # NoDocbcCost
user_data["GenHlth"] = ask_float("GenHlth", "1–5 (ogólny stan zdrowia)")  # GenHlth
user_data["MentHlth"] = ask_float("MentHlth", "0–30 (dni gorszego zdrowia psych.)")  # MentHlth
user_data["PhysHlth"] = ask_float("PhysHlth", "0–30 (dni gorszego zdrowia fiz.)")  # PhysHlth
user_data["DiffWalk"] = ask_float("DiffWalk", "0 lub 1 (trudność w chodzeniu)")  # DiffWalk
user_data["Sex"] = ask_float("Sex", "0 lub 1 (płeć jak w danych)")  # Sex
user_data["Age"] = ask_float("Age", "1–13 (przedziały wieku jak w danych)")  # Age
user_data["Education"] = ask_float("Education", "1–6 (poziom edukacji)")  # Education
user_data["Income"] = ask_float("Income", "1–8 (przedziały dochodu)")  # Income

# Zbuduj DataFrame z jednym wierszem, w poprawnej kolejności cech.  # komentarz
user_df = pd.DataFrame([user_data], columns=loaded_feature_names)  # jedna obserwacja jako DataFrame

# Skaluj dane tak jak trenowaliśmy model.  # komentarz
user_scaled = loaded_scaler.transform(user_df)  # przeskaluj dane użytkownika

# Przewidź klasę i prawdopodobieństwo.  # komentarz
user_pred = loaded_model.predict(user_scaled)[0]  # przewidywana klasa (0/1)
user_prob = loaded_model.predict_proba(user_scaled)[0, 1]  # prawdopodobieństwo klasy 1

print("\n=== WYNIK MODELU ===")  # nagłówek
print("Predykcja (0=brak cukrzycy, 1=cukrzyca):", int(user_pred))  # pokaż klasę
print(f"Prawdopodobieństwo cukrzycy (klasa 1): {user_prob:.4f}")  # pokaż prawdopodobieństwo

# Dodatkowy komunikat dla czytelności laika.  # komentarz
if user_pred == 1:  # jeśli model przewiduje cukrzycę
    print("Model sugeruje: WYSOKIE RYZYKO (klasa 1).")  # komunikat
else:  # jeśli model przewiduje brak cukrzycy
    print("Model sugeruje: NISKIE RYZYKO (klasa 0).")  # komunikat

# =========================================================
# 18) ZMIANA PROGU DECYZYJNEGO (threshold)
# =========================================================

"""
Domyślnie klasyfikator przewiduje klasę 1, gdy prawdopodobieństwo >= 0.50.
W praktyce (zwłaszcza w medycynie) często chcemy:
- zwiększyć recall (wykryć więcej chorych),
kosztem precision (więcej fałszywych alarmów).

Dlatego uczymy się wybierać próg (threshold) świadomie.
"""

# ---------------------------
# 18.1) Obliczamy precision i recall dla różnych progów
# ---------------------------

precisions, recalls, thr = precision_recall_curve(y_test, y_proba)  # policz PR dla wielu progów

thr_safe = np.append(thr, 1.0)  # dodaj próg 1.0, żeby długości tablic pasowały (thr jest o 1 krótsze)

# ---------------------------
# 18.2) Wykres: precision i recall w zależności od progu
# ---------------------------

plt.figure(figsize=(8, 5))  # rozmiar wykresu
plt.plot(thr_safe, precisions, label="Precision")  # precision vs threshold
plt.plot(thr_safe, recalls, label="Recall")  # recall vs threshold
plt.title("Precision i Recall w zależności od progu (threshold)")  # tytuł
plt.xlabel("Threshold (próg decyzji)")  # oś X
plt.ylabel("Wartość metryki")  # oś Y
plt.legend()  # legenda
plt.show()  # pokaż wykres

# ---------------------------
# 18.3) Prosty wybór progu: maksymalizacja F1
# ---------------------------

"""
Wybór progu można robić na wiele sposobów.
Najprostszy i bardzo edukacyjny wariant:
- wybieramy threshold, który daje najwyższy F1 na zbiorze testowym.

Uwaga dydaktyczna:
W idealnym świecie próg wybieramy na walidacji, a test zostawiamy „na koniec”.
Tutaj robimy to prosto, żeby zrozumieć ideę.
"""

f1_scores = (2 * precisions * recalls) / (precisions + recalls + 1e-12)  # oblicz F1 dla każdego punktu (bez dzielenia przez zero)
best_idx = np.argmax(f1_scores)  # indeks najlepszego F1
best_threshold = thr_safe[best_idx]  # próg odpowiadający najlepszemu F1

print("\n=== WYBÓR PROGU (na podstawie najlepszego F1) ===")  # nagłówek
print("Najlepszy threshold:", round(float(best_threshold), 4))  # pokaż próg
print("F1 dla tego progu:", round(float(f1_scores[best_idx]), 4))  # pokaż F1

# ---------------------------
# 18.4) Predykcja z nowym progiem + metryki
# ---------------------------

y_pred_thr = (y_proba >= best_threshold).astype(int)  # zamień prawdopodobieństwa na klasy używając nowego progu

acc_thr = accuracy_score(y_test, y_pred_thr)  # accuracy dla progu
prec_thr = precision_score(y_test, y_pred_thr, zero_division=0)  # precision dla progu
rec_thr = recall_score(y_test, y_pred_thr, zero_division=0)  # recall dla progu
f1_thr = f1_score(y_test, y_pred_thr, zero_division=0)  # f1 dla progu

print("\n=== METRYKI DLA NOWEGO PROGU ===")  # nagłówek
print(f"Accuracy : {acc_thr:.4f}")  # accuracy
print(f"Precision: {prec_thr:.4f}")  # precision
print(f"Recall   : {rec_thr:.4f}")  # recall
print(f"F1-score : {f1_thr:.4f}")  # f1

print("\n=== RAPORT KLASYFIKACJI DLA NOWEGO PROGU ===")  # nagłówek
print(classification_report(y_test, y_pred_thr, digits=4))  # raport

# ---------------------------
# 18.5) Confusion Matrix dla nowego progu
# ---------------------------

cm_thr = confusion_matrix(y_test, y_pred_thr)  # macierz pomyłek dla progu

plt.figure(figsize=(7, 5))  # rozmiar
sns.heatmap(  # heatmapa
    cm_thr,  # macierz
    annot=True,  # liczby
    fmt="d",  # int
    cmap="Greens",  # kolor
    xticklabels=["brak cukrzycy (0)", "cukrzyca (1)"],  # etykiety X
    yticklabels=["brak cukrzycy (0)", "cukrzyca (1)"]  # etykiety Y
)
plt.title("Macierz pomyłek (nowy threshold)")  # tytuł
plt.xlabel("Klasa przewidziana")  # oś X
plt.ylabel("Klasa rzeczywista")  # oś Y
plt.show()  # pokaż wykres

# =========================================================
# 19) NAJWAŻNIEJSZE CECHY (feature importance) – Logistic Regression
# =========================================================

"""
Regresja logistyczna ma współczynniki (coef_).
Każdy współczynnik mówi:
- czy dana cecha zwiększa (współczynnik dodatni),
- czy zmniejsza (współczynnik ujemny)
prawdopodobieństwo klasy 1 (cukrzyca).

Ponieważ użyliśmy StandardScaler, współczynniki są bardziej porównywalne między cechami.
"""

coefs = model.coef_[0]  # współczynniki dla klasy 1 (dla problemu binarnego jest to 1 wiersz)

coef_df = pd.DataFrame({  # zbuduj tabelę współczynników
    "feature": feature_names,  # nazwa cechy
    "coef": coefs  # współczynnik
}).sort_values("coef", ascending=False)  # posortuj od największego wpływu dodatniego

print("\n=== TOP cechy zwiększające ryzyko (największe dodatnie współczynniki) ===")  # nagłówek
display(coef_df.head(10))  # pokaż top 10

print("\n=== TOP cechy zmniejszające ryzyko (najbardziej ujemne współczynniki) ===")  # nagłówek
display(coef_df.tail(10))  # pokaż top 10 od końca

# ---------------------------
# 19.1) Wykres: 10 największych + 10 najmniejszych współczynników
# ---------------------------

top_pos = coef_df.head(10)  # 10 największych dodatnich
top_neg = coef_df.tail(10)  # 10 najbardziej ujemnych

coef_plot_df = pd.concat([top_neg, top_pos], axis=0)  # połącz w jedną tabelę do wykresu

plt.figure(figsize=(10, 6))  # rozmiar
sns.barplot(data=coef_plot_df, x="coef", y="feature")  # wykres słupkowy współczynników
plt.title("Najważniejsze cechy wg regresji logistycznej (współczynniki)")  # tytuł
plt.xlabel("Współczynnik (coef)")  # oś X
plt.ylabel("Cecha")  # oś Y
plt.show()  # pokaż wykres