|
|
import streamlit as st |
|
|
import numpy as np |
|
|
import pandas as pd |
|
|
import matplotlib.pyplot as plt |
|
|
import seaborn as sns |
|
|
|
|
|
|
|
|
|
|
|
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. |
|
|
""" |
|
|
|
|
|
alunos_confirmados = np.random.binomial(n=num_inscritos_sorteados, p=prob_confirmacao, size=num_simulacoes) |
|
|
|
|
|
|
|
|
alunos_comparecem = np.random.binomial(n=alunos_confirmados, p=prob_comparecimento, size=num_simulacoes) |
|
|
|
|
|
|
|
|
sucessos = np.sum(alunos_comparecem >= vagas_minimas_necessarias) |
|
|
|
|
|
|
|
|
prob_sucesso = sucessos / num_simulacoes |
|
|
return prob_sucesso |
|
|
|
|
|
|
|
|
|
|
|
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. |
|
|
""") |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
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!') |
|
|
|
|
|
|
|
|
|
|
|
st.header('Resultados da Simulação') |
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.') |