# app.py - Dashboard Interativo de Cancelamento de Reservas Hoteleiras import streamlit as st import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import joblib from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, roc_curve, confusion_matrix) from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import SVC from sklearn.model_selection import cross_val_score from sklearn.preprocessing import StandardScaler import plotly.graph_objects as go import plotly.express as px import time import warnings warnings.filterwarnings('ignore') # Configuração da página st.set_page_config( page_title="Dashboard - Cancelamento de Reservas", page_icon="🏨", layout="wide", initial_sidebar_state="expanded" ) # CSS customizado st.markdown(""" """, unsafe_allow_html=True) class HotelBookingDashboard: def __init__(self): self.models = {} self.results = {} self.X_train = None self.X_test = None self.y_train = None self.y_test = None def load_data(self): """Carrega dados pré-processados ou cria dados de demonstração""" try: # Tentar carregar dados salvos saved_data = joblib.load('modelos_treinados.pkl') self.models = saved_data['models'] self.X_train = saved_data['X_train'] self.X_test = saved_data['X_test'] self.y_train = saved_data['y_train'] self.y_test = saved_data['y_test'] return True except: # Criar dados de demonstração st.info("📝 Criando dados de demonstração...") return self._create_demo_data() def _create_demo_data(self): """Cria dados de demonstração realísticos""" np.random.seed(42) n_samples = 2000 # Features baseadas no dataset real de hotéis features = { 'lead_time': np.random.gamma(2, 50, n_samples), 'adr': np.random.normal(100, 30, n_samples), 'adults': np.random.poisson(2, n_samples), 'children': np.random.poisson(0.3, n_samples), 'previous_cancellations': np.random.poisson(0.1, n_samples), 'is_repeated_guest': np.random.binomial(1, 0.1, n_samples), 'required_car_parking_spaces': np.random.binomial(1, 0.2, n_samples), 'total_of_special_requests': np.random.poisson(0.5, n_samples), 'booking_changes': np.random.poisson(0.3, n_samples), } X = pd.DataFrame(features) # Criar target com relação realística cancellation_prob = 1 / (1 + np.exp(-( X['lead_time'] * 0.01 + X['adr'] * 0.005 - X['is_repeated_guest'] * 0.8 - X['required_car_parking_spaces'] * 0.3 + X['total_of_special_requests'] * -0.4 + np.random.normal(0, 0.5, n_samples) ))) y = (cancellation_prob > 0.5).astype(int) # Split dos dados from sklearn.model_selection import train_test_split self.X_train, self.X_test, self.y_train, self.y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y ) # Normalizar scaler = StandardScaler() self.X_train = scaler.fit_transform(self.X_train) self.X_test = scaler.transform(self.X_test) # Treinar modelos de demonstração self._train_demo_models() return True def _train_demo_models(self): """Treina modelos de demonstração""" # Regressão Logística lr = LogisticRegression(random_state=42, max_iter=1000) lr.fit(self.X_train, self.y_train) self.models['RL_Padrao'] = lr # KNN knn = KNeighborsClassifier(n_neighbors=5) knn.fit(self.X_train, self.y_train) self.models['KNN_Padrao'] = knn # SVM svm = SVC(probability=True, random_state=42) svm.fit(self.X_train, self.y_train) self.models['SVM_Padrao'] = svm # Avaliar modelos demo for name, model in self.models.items(): metrics, _, _ = self.evaluate_model(model, name, 0) self.results[name] = metrics def train_logistic_regression(self, C=1.0, penalty='l2', solver='lbfgs'): """Treina Regressão Logística""" model = LogisticRegression(C=C, penalty=penalty, solver=solver, max_iter=1000, random_state=42) start_time = time.time() model.fit(self.X_train, self.y_train) training_time = time.time() - start_time return model, training_time def train_knn(self, n_neighbors=5, metric='euclidean', weights='uniform'): """Treina KNN""" model = KNeighborsClassifier(n_neighbors=n_neighbors, metric=metric, weights=weights) start_time = time.time() model.fit(self.X_train, self.y_train) training_time = time.time() - start_time return model, training_time def train_svm(self, C=1.0, kernel='rbf', gamma='scale'): """Treina SVM""" model = SVC(C=C, kernel=kernel, gamma=gamma, probability=True, random_state=42) start_time = time.time() model.fit(self.X_train, self.y_train) training_time = time.time() - start_time return model, training_time def evaluate_model(self, model, model_name, training_time): """Avalia modelo e retorna métricas""" y_pred = model.predict(self.X_test) y_proba = model.predict_proba(self.X_test)[:, 1] metrics = { 'Acurácia': accuracy_score(self.y_test, y_pred), 'Precisão': precision_score(self.y_test, y_pred, zero_division=0), 'Recall': recall_score(self.y_test, y_pred, zero_division=0), 'F1-Score': f1_score(self.y_test, y_pred, zero_division=0), 'AUC-ROC': roc_auc_score(self.y_test, y_proba), 'Tempo Treino (s)': training_time } # Curva ROC fpr, tpr, _ = roc_curve(self.y_test, y_proba) roc_data = {'fpr': fpr, 'tpr': tpr, 'auc': metrics['AUC-ROC']} # Matriz de confusão cm = confusion_matrix(self.y_test, y_pred) return metrics, roc_data, cm def plot_roc_comparison(self, current_roc, current_model_name): """Plota comparação de curvas ROC""" fig = go.Figure() # Curva do modelo atual fig.add_trace(go.Scatter( x=current_roc['fpr'], y=current_roc['tpr'], mode='lines', name=f'{current_model_name} (AUC = {current_roc["auc"]:.3f})', line=dict(width=3, color='red') )) # Curvas dos outros modelos colors = ['blue', 'green', 'orange', 'purple'] for i, (model_name, model) in enumerate(self.models.items()): if model_name != current_model_name: try: y_proba = model.predict_proba(self.X_test)[:, 1] fpr, tpr, _ = roc_curve(self.y_test, y_proba) auc = roc_auc_score(self.y_test, y_proba) fig.add_trace(go.Scatter( x=fpr, y=tpr, mode='lines', name=f'{model_name} (AUC = {auc:.3f})', line=dict(width=2, color=colors[i % len(colors)], dash='dash') )) except: continue # Linha de referência fig.add_trace(go.Scatter( x=[0, 1], y=[0, 1], mode='lines', name='Classificador Aleatório', line=dict(dash='dash', color='grey') )) fig.update_layout( title='Comparação das Curvas ROC', xaxis_title='Taxa de Falsos Positivos', yaxis_title='Taxa de Verdadeiros Positivos', width=600, height=500 ) return fig def main(): # Inicializar dashboard dashboard = HotelBookingDashboard() # Carregar dados if not dashboard.load_data(): st.error("❌ Erro ao carregar dados") return # Header st.markdown('