File size: 5,069 Bytes
69485c9
 
326e2bb
69485c9
9fd4111
69485c9
 
 
 
 
 
 
 
 
 
 
9fd4111
69485c9
 
 
 
 
 
 
 
 
 
 
9fd4111
69485c9
 
 
 
 
 
9fd4111
69485c9
 
 
9fd4111
 
 
8c415e0
69485c9
8c415e0
 
 
 
69485c9
 
 
 
9fd4111
69485c9
8c415e0
9fd4111
69485c9
 
 
9fd4111
69485c9
 
 
 
9fd4111
69485c9
 
 
 
 
 
 
 
9fd4111
69485c9
 
 
9fd4111
69485c9
 
8c415e0
69485c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c415e0
69485c9
 
 
9fd4111
69485c9
 
 
 
8c415e0
69485c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/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(
    """
    <style>
    .stSlider > div > div > div > div > div > div {background-color: #C0392B !important;}
    .big-title {text-align:center; color:#0d47a1;}
    .sub-title {text-align:center; color:#0d47a1;}
    </style>
    """,
    unsafe_allow_html=True,
)

# =========================
# Cabeçalho
# =========================
st.markdown("<h1 class='big-title'>Análise de Distribuição Binomial</h1>", unsafe_allow_html=True)
st.markdown("<h3 class='sub-title'>Simulação de Overbooking em Voos</h3>", 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("<h4 style='color:#0d47a1;'>Probabilidade de Comparecimento (p)</h4>", 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("<h4 style='color:#0d47a1;'>Capacidade do Avião</h4>", 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("<h4 style='color:#0d47a1;'>Passagens Vendidas</h4>", 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("<h4 style='color:#0d47a1;'>Nível de Risco Aceito (%)</h4>", 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("<h4 style='color:#0d47a1;'>Faixa da Curva (capacidade + ...)</h4>", 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.")