#!/usr/bin/env python # coding: utf-8 import streamlit as st import numpy as np import pandas as pd import plotly.graph_objects as go from scipy.stats import binom # ========================= # Configuração de página + estilo # ========================= st.set_page_config( page_title="Análise de Distribuição Binomial – Overbooking", layout="wide", initial_sidebar_state="expanded" ) # Slider e detalhes em tema escuro (simples) st.markdown( """ """, unsafe_allow_html=True, ) # ========================= # Cabeçalho # ========================= st.markdown("

Análise de Distribuição Binomial

", unsafe_allow_html=True) st.markdown("

Simulação de Overbooking em Voos

", unsafe_allow_html=True) st.markdown("---") # ========================= # Funções utilitárias # ========================= def formatar_pct(x: float) -> str: return f"{x*100:.2f}%" def clamp_vendidas(): """Garante vendidas >= capacidade sem causar 'tremedeira'.""" cap = st.session_state.capacidade if st.session_state.vendidas < cap: st.session_state.vendidas = cap # ========================= # Parâmetros (no estilo do professor) # ========================= st.markdown("### Distribuição Binomial — Simulação de Overbooking") col1, col2, col3, col4 = st.columns(4) with col1: st.markdown("

Probabilidade de Comparecimento (p)

", unsafe_allow_html=True) st.slider("", min_value=0.50, max_value=0.99, value=0.88, step=0.01, key="p") with col2: st.markdown("

Capacidade do Avião

", unsafe_allow_html=True) st.slider("", min_value=60, max_value=300, value=120, step=1, key="capacidade", on_change=clamp_vendidas) with col3: st.markdown("

Passagens Vendidas

", unsafe_allow_html=True) # intervalo amplo e independente; clamp ajusta se ficar < capacidade st.slider("", min_value=60, max_value=380, value=130, step=1, key="vendidas", on_change=clamp_vendidas) with col4: st.markdown("

Nível de Risco Aceito (%)

", unsafe_allow_html=True) st.slider("", min_value=0.01, max_value=0.30, value=0.07, step=0.01, key="limite") # faixa da curva (capacidade até capacidade+max_extra) st.markdown("

Faixa da Curva (capacidade + ...)

", unsafe_allow_html=True) max_extra = st.slider("", min_value=10, max_value=120, value=40, step=5) # Valores finais p = float(st.session_state.p) capacidade = int(st.session_state.capacidade) vendidas = int(max(st.session_state.vendidas, capacidade)) limite = float(st.session_state.limite) # ========================= # Cálculos (SciPy Binomial) # ========================= # Risco pontual: P(X > capacidade) = 1 - CDF(capacidade) risco_pontual = 1.0 - binom.cdf(capacidade, vendidas, p) # Curva: de capacidade até capacidade+max_extra vendidas_range = np.arange(capacidade, capacidade + max_extra + 1) riscos = 1.0 - binom.cdf(capacidade, vendidas_range, p) # Tabela tabela = pd.DataFrame( {"Passagens vendidas": vendidas_range, "Risco de Overbooking": riscos} ) # Máximo de vendidas respeitando o limite ok = tabela[tabela["Risco de Overbooking"] <= limite] max_seguro = int(ok["Passagens vendidas"].max()) if not ok.empty else None # ========================= # Métricas # ========================= m1, m2, m3, m4 = st.columns(4) m1.metric("Risco atual (P>X_cap)", formatar_pct(risco_pontual)) m2.metric("Capacidade", capacidade) m3.metric("Vendidas (ponto atual)", vendidas) m4.metric("Máximo com risco ≤ limite", f"{max_seguro}" if max_seguro else "—") # ========================= # Gráfico (Plotly) # ========================= fig = go.Figure() fig.add_trace(go.Scatter( x=vendidas_range, y=riscos, mode="lines", line=dict(color="#003366", width=3), name="Risco de Overbooking" )) fig.add_hline(y=limite, line_dash="dash", line_color="red", line_width=1, name="Limite") fig.update_layout( title=f"Risco de Overbooking para mais de {capacidade} passageiros", xaxis_title="Passagens vendidas", yaxis_title=f"Probabilidade de mais de {capacidade} passageiros aparecerem", xaxis=dict(tickmode="linear"), yaxis=dict(range=[0, 1]), plot_bgcolor="white", width=900, height=420, showlegend=False, ) st.plotly_chart(fig, use_container_width=True) # ========================= # Tabela # ========================= st.write("### Tabela de Probabilidades") st.dataframe(tabela, use_container_width=True) if max_seguro is not None: st.info(f"▶ Máximo de passagens com risco ≤ {formatar_pct(limite)}: **{max_seguro}**.") else: st.warning(f"Nenhum valor de venda dentro do limite de {formatar_pct(limite)} na faixa analisada.")