aedi / dashboard.py
vsmoreira's picture
Ajustes no dashboard
5d7954f
import streamlit as st
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# --- Funções da Simulação (reutilizadas do notebook) ---
def simular_viabilidade_curso(num_inscritos_sorteados, prob_confirmacao, prob_comparecimento, vagas_minimas_necessarias, num_simulacoes):
"""
Executa a Simulação de Monte Carlo para um dado número de inscritos sorteados.
Retorna a probabilidade do curso ser confirmado.
"""
# Etapa 1: Simular quantos dos sorteados confirmam a vaga
alunos_confirmados = np.random.binomial(n=num_inscritos_sorteados, p=prob_confirmacao, size=num_simulacoes)
# Etapa 2: Dentre os confirmados, simular quantos comparecem
alunos_comparecem = np.random.binomial(n=alunos_confirmados, p=prob_comparecimento, size=num_simulacoes)
# Verificar em quantas simulações o curso foi confirmado
sucessos = np.sum(alunos_comparecem >= vagas_minimas_necessarias)
# Calcular a probabilidade
prob_sucesso = sucessos / num_simulacoes
return prob_sucesso
# --- Interface do Streamlit ---
st.title('Dashboard de Simulação de Monte Carlo para Viabilidade de Cursos')
st.markdown("""
Este dashboard permite analisar a probabilidade de um curso ser confirmado com base em diversos parâmetros.
Utilize os controles na barra lateral para ajustar os cenários e observar o impacto nos resultados.
""")
# --- Barra Lateral com os Parâmetros de Entrada ---
st.sidebar.title('Parâmetros de Entrada')
st.sidebar.header('Parâmetros do Curso')
total_vagas = st.sidebar.slider('Total de Vagas', min_value=10, max_value=200, value=50, step=5)
min_ocupacao_perc = st.sidebar.slider('Ocupação Mínima para Confirmação (%)', min_value=50, max_value=100, value=90, step=5)
min_ocupacao_perc = min_ocupacao_perc / 100.0
vagas_minimas_necessarias = int(total_vagas * min_ocupacao_perc)
st.sidebar.header('Probabilidades do Cenário')
prob_confirmacao = st.sidebar.slider('Probabilidade de Confirmação de Vaga (%)', min_value=50, max_value=100, value=85, step=1)
prob_confirmacao = prob_confirmacao / 100.0
prob_comparecimento = st.sidebar.slider('Probabilidade de Comparecimento (%)', min_value=50, max_value=100, value=95, step=1)
prob_comparecimento = prob_comparecimento / 100.0
st.sidebar.header('Parâmetros da Simulação')
num_simulacoes = st.sidebar.select_slider('Número de Simulações (rodadas)', options=[1000, 5000, 10000, 20000], value=10000)
min_inscritos_teste = st.sidebar.number_input('Mínimo de Inscritos a Testar', value=int(total_vagas * 0.1), min_value=1)
max_inscritos_teste = st.sidebar.number_input('Máximo de Inscritos a Testar', value=total_vagas * 2, min_value=int(min_inscritos_teste)+10)
# --- Execução da Simulação com base nos inputs ---
if st.sidebar.button('Executar Simulação'):
with st.spinner('Realizando simulações... Por favor, aguarde.'):
lista_de_inscritos = range(min_inscritos_teste, max_inscritos_teste + 1)
resultados_simulacao = []
for num_convocados in lista_de_inscritos:
probabilidade = simular_viabilidade_curso(num_convocados, prob_confirmacao, prob_comparecimento, vagas_minimas_necessarias, num_simulacoes)
resultados_simulacao.append({'num_convocados': num_convocados, 'prob_confirmacao_curso': probabilidade})
df_resultados = pd.DataFrame(resultados_simulacao)
st.success('Simulação concluída!')
# --- Exibição dos Resultados ---
st.header('Resultados da Simulação')
# Análise para encontrar os limiares
try:
convocados_min_req = df_resultados[df_resultados['prob_confirmacao_curso'] >= min_ocupacao_perc].iloc[0]
convocados_99 = df_resultados[df_resultados['prob_confirmacao_curso'] >= 0.99].iloc[0]
except IndexError:
st.warning("Não foi possível atingir os limiares de probabilidade na faixa de testes. Tente aumentar o 'Máximo de Inscritos a Testar'.")
convocados_min_req = None
convocados_99 = None
col1, col2 = st.columns(2)
if convocados_min_req is not None:
col1.metric(f"Mínimo para {int(min_ocupacao_perc*100)}% de chance", f"{int(convocados_min_req['num_convocados'])} convocados")
if convocados_99 is not None:
col2.metric("Mínimo para 99% de chance", f"{int(convocados_99['num_convocados'])} convocados")
# Gráfico de Distribuição Binomial
st.subheader('Análise da Distribuição de Alunos')
st.markdown("Distribuição das probabilidades de sucesso para o número de convocados dentro do total de vagas disponíveis")
num_conv_dist = total_vagas
confirmados_dist = np.random.binomial(n=num_conv_dist, p=prob_confirmacao, size=num_simulacoes)
comparecem_dist = np.random.binomial(n=confirmados_dist, p=prob_comparecimento, size=num_simulacoes)
valor_esperado = num_conv_dist * prob_confirmacao * prob_comparecimento
fig2, ax2 = plt.subplots(figsize=(14, 7))
sns.histplot(comparecem_dist, kde=True, stat="probability", discrete=True, ax=ax2)
ax2.axvline(valor_esperado, color='red', linestyle='--', linewidth=2, label=f'Valor Esperado: {valor_esperado:.2f}')
ax2.axvline(vagas_minimas_necessarias, color='purple', linestyle=':', linewidth=2, label=f'Mínimo Necessário: {vagas_minimas_necessarias}')
ax2.set_title(f'Distribuição Simulada de Comparecimento para {num_conv_dist} Convocados', fontsize=16)
ax2.set_xlabel(f'Número de Alunos que Comparecem', fontsize=12)
ax2.set_ylabel('Probabilidade (Frequência Relativa)', fontsize=12)
ax2.legend()
st.pyplot(fig2)
# Gráfico Principal
st.subheader('Probabilidade de Confirmação vs. Número de Convocados')
fig1, ax1 = plt.subplots(figsize=(14, 7))
sns.set_style("whitegrid")
ax1.plot(df_resultados['num_convocados'], df_resultados['prob_confirmacao_curso'], marker='o', linestyle='-', label='Probabilidade de Confirmação do Curso')
ax1.axhline(y=min_ocupacao_perc, color='g', linestyle='--', label=f"Limiar de {int(min_ocupacao_perc*100)}%")
ax1.axhline(y=0.99, color='r', linestyle='--', label='Limiar de 99%')
if convocados_min_req is not None:
ax1.axvline(x=convocados_min_req['num_convocados'], color='g', linestyle='--', alpha=0.7)
ax1.text(convocados_min_req['num_convocados'] + 0.5, 0.5, f"{int(convocados_min_req['num_convocados'])} convocados\npara {int(min_ocupacao_perc*100)}%", color='g')
if convocados_99 is not None:
ax1.axvline(x=convocados_99['num_convocados'], color='r', linestyle='--', alpha=0.7)
ax1.text(convocados_99['num_convocados'] + 0.5, 0.65, f"{int(convocados_99['num_convocados'])} convocados\npara 99%", color='r')
ax1.set_title('Probabilidade de Confirmação do Curso vs. Número de Convocados', fontsize=16)
ax1.set_xlabel('Número de Pessoas Convocadas (Sorteados)', fontsize=12)
ax1.set_ylabel('Probabilidade de Confirmação do Curso', fontsize=12)
ax1.legend()
ax1.grid(True)
ax1.set_ylim(0, 1.05)
ax1.set_xlim(min(lista_de_inscritos), max(lista_de_inscritos))
st.pyplot(fig1)
# Tabela de Resultados
st.subheader('Tabela de Resultados Detalhados')
st.dataframe(df_resultados.style.format({'prob_confirmacao_curso': '{:.2%}'}))
else:
st.info('Ajuste os parâmetros na barra lateral e clique em "Executar Simulação" para começar.')