import gradio as gr import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from io import BytesIO import tempfile, os # Configurar estilo sns.set_style("whitegrid") plt.rcParams['figure.dpi'] = 100 # Dados históricos DADOS_HISTORICOS = np.array([11, 17, 7, 4, 5, 8, 6, 10, 11, 15, 11, 4, 8]) class MonteCarloSimulator: def __init__(self, dados_historicos): self.dados = dados_historicos self.media = np.mean(dados_historicos) self.desvio = np.std(dados_historicos) def simular(self, n_simulacoes=1000, semanas_futuras=15, meta=100): np.random.seed(42) resultados = [] simulacoes_completas = [] for sim in range(n_simulacoes): entregas_semanais = [] total = 0 semanas_necessarias = 0 for semana in range(semanas_futuras): entregas = max(0, int(np.random.normal(self.media, self.desvio))) entregas_semanais.append(entregas) total += entregas if total >= meta and semanas_necessarias == 0: semanas_necessarias = semana + 1 if semanas_necessarias == 0: semanas_necessarias = semanas_futuras + 1 resultados.append({ 'simulacao': sim + 1, 'semanas_necessarias': semanas_necessarias, 'total_entregas': total, 'atingiu_meta': total >= meta }) simulacoes_completas.append(entregas_semanais) return pd.DataFrame(resultados), np.array(simulacoes_completas) def criar_visualizacoes(df_resultados, simulacoes_completas, meta, n_simulacoes): df_atingiu = df_resultados[df_resultados['atingiu_meta']] if len(df_atingiu) == 0: fig, ax = plt.subplots(figsize=(10, 6)) ax.text(0.5, 0.5, 'Nenhuma simulação atingiu a meta.\nTente aumentar o horizonte de semanas.', ha='center', va='center', fontsize=14, color='red') ax.axis('off') return fig fig = plt.figure(figsize=(18, 12)) gs = fig.add_gridspec(3, 3, hspace=0.35, wspace=0.3) # 1. Distribuição de semanas necessárias ax1 = fig.add_subplot(gs[0, :2]) semanas_filtradas = df_atingiu['semanas_necessarias'] ax1.hist(semanas_filtradas, bins=range(1, int(semanas_filtradas.max()) + 2), edgecolor='black', alpha=0.7, color='steelblue') ax1.axvline(x=semanas_filtradas.mean(), color='r', linestyle='--', linewidth=2, label=f'Média: {semanas_filtradas.mean():.1f}') ax1.axvline(x=semanas_filtradas.median(), color='g', linestyle='--', linewidth=2, label=f'Mediana: {semanas_filtradas.median():.0f}') ax1.set_xlabel('Semanas Necessárias', fontsize=11) ax1.set_ylabel('Frequência', fontsize=11) ax1.set_title('Distribuição de Semanas para Atingir a Meta', fontsize=13, fontweight='bold') ax1.legend(fontsize=9) ax1.grid(True, alpha=0.3) # 2. Probabilidade acumulada ax2 = fig.add_subplot(gs[0, 2]) semanas_sorted = np.sort(semanas_filtradas) prob_acum = np.arange(1, len(semanas_sorted) + 1) / len(semanas_sorted) * 100 ax2.plot(semanas_sorted, prob_acum, linewidth=2, color='darkgreen') ax2.set_xlabel('Semanas', fontsize=11) ax2.set_ylabel('Probabilidade (%)', fontsize=11) ax2.set_title('Prob. Acumulada', fontsize=13, fontweight='bold') ax2.grid(True, alpha=0.3) ax2.axhline(y=50, color='r', linestyle='--', alpha=0.5, label='50%') ax2.axhline(y=90, color='orange', linestyle='--', alpha=0.5, label='90%') ax2.legend(fontsize=8) # 3. Boxplot ax3 = fig.add_subplot(gs[1, 0]) ax3.boxplot(semanas_filtradas, vert=True) ax3.set_ylabel('Semanas', fontsize=11) ax3.set_title('Boxplot', fontsize=13, fontweight='bold') ax3.grid(True, alpha=0.3) # 4. Taxa de sucesso ax4 = fig.add_subplot(gs[1, 1]) taxa_sucesso = df_resultados['atingiu_meta'].sum() / len(df_resultados) * 100 taxa_falha = 100 - taxa_sucesso colors = ['#2ecc71', '#e74c3c'] ax4.pie([taxa_sucesso, taxa_falha], labels=['Atingiu', 'Não Atingiu'], autopct='%1.1f%%', startangle=90, colors=colors, textprops={'fontsize': 10}) ax4.set_title('Taxa de Sucesso', fontsize=13, fontweight='bold') # 5. Total de entregas ax5 = fig.add_subplot(gs[1, 2]) ax5.hist(df_resultados['total_entregas'], bins=30, edgecolor='black', alpha=0.7, color='coral') ax5.axvline(x=meta, color='r', linestyle='--', linewidth=2, label=f'Meta: {meta}') ax5.set_xlabel('Total de Entregas', fontsize=11) ax5.set_ylabel('Frequência', fontsize=11) ax5.set_title('Distribuição Total', fontsize=13, fontweight='bold') ax5.legend(fontsize=9) ax5.grid(True, alpha=0.3) # 6. Evolução semanal ax6 = fig.add_subplot(gs[2, :]) n_mostrar = min(100, n_simulacoes) for i in range(n_mostrar): entregas_acum = np.cumsum(simulacoes_completas[i]) ax6.plot(range(1, len(entregas_acum) + 1), entregas_acum, alpha=0.08, color='blue') media_acumulada = np.cumsum(np.mean(simulacoes_completas, axis=0)) ax6.plot(range(1, len(media_acumulada) + 1), media_acumulada, color='red', linewidth=3, label='Média', zorder=100) ax6.axhline(y=meta, color='green', linestyle='--', linewidth=2, label=f'Meta: {meta}') ax6.set_xlabel('Semana', fontsize=11) ax6.set_ylabel('Entregas Acumuladas', fontsize=11) ax6.set_title(f'Evolução das Entregas ({n_mostrar} simulações)', fontsize=13, fontweight='bold') ax6.legend(fontsize=9) ax6.grid(True, alpha=0.3) plt.suptitle(f'Simulação de Monte Carlo - {n_simulacoes} Simulações', fontsize=15, fontweight='bold', y=0.995) return fig def gerar_estatisticas(df_resultados, meta): df_atingiu = df_resultados[df_resultados['atingiu_meta']] texto = f""" ## 📊 Resultados da Simulação ### 🎯 Meta: {meta} tarefas ### ✅ Taxa de Sucesso - **Simulações que atingiram a meta:** {df_resultados['atingiu_meta'].sum()} ({df_resultados['atingiu_meta'].sum()/len(df_resultados)*100:.1f}%) - **Simulações que NÃO atingiram:** {(~df_resultados['atingiu_meta']).sum()} ({(~df_resultados['atingiu_meta']).sum()/len(df_resultados)*100:.1f}%) """ if len(df_atingiu) > 0: p10 = np.percentile(df_atingiu['semanas_necessarias'], 10) p50 = np.percentile(df_atingiu['semanas_necessarias'], 50) p90 = np.percentile(df_atingiu['semanas_necessarias'], 90) texto += f""" ### 📈 Estatísticas de Semanas Necessárias - **Média:** {df_atingiu['semanas_necessarias'].mean():.2f} semanas - **Mediana:** {df_atingiu['semanas_necessarias'].median():.0f} semanas - **Mínimo:** {df_atingiu['semanas_necessarias'].min():.0f} semanas - **Máximo:** {df_atingiu['semanas_necessarias'].max():.0f} semanas - **Desvio Padrão:** {df_atingiu['semanas_necessarias'].std():.2f} ### 🎲 Percentis (Probabilidade) - **10%** das simulações: até **{p10:.0f} semanas** - **50%** das simulações: até **{p50:.0f} semanas** - **90%** das simulações: até **{p90:.0f} semanas** ### 💡 Interpretação Com base em {len(df_resultados)} simulações, há **{df_resultados['atingiu_meta'].sum()/len(df_resultados)*100:.1f}%** de chance de atingir a meta de {meta} tarefas. Em metade das simulações bem-sucedidas, a meta foi atingida em até **{p50:.0f} semanas**. """ else: texto += "\n\n⚠️ **Nenhuma simulação atingiu a meta no horizonte definido.**\n\nSugestão: Aumente o número de semanas ou revise a meta." return texto def simular_monte_carlo(n_simulacoes, semanas_futuras, meta): # Validações if n_simulacoes < 10 or n_simulacoes > 1000: return None, "❌ Número de simulações deve estar entre 10 e 1000", None if semanas_futuras < 5 or semanas_futuras > 52: return None, "❌ Número de semanas deve estar entre 5 e 52", None if meta < 10 or meta > 500: return None, "❌ Meta deve estar entre 10 e 500 tarefas", None try: # Executar simulação simulador = MonteCarloSimulator(DADOS_HISTORICOS) df_resultados, simulacoes_completas = simulador.simular( n_simulacoes=int(n_simulacoes), semanas_futuras=int(semanas_futuras), meta=int(meta) ) # Criar visualizações fig = criar_visualizacoes(df_resultados, simulacoes_completas, meta, n_simulacoes) # Gerar estatísticas stats_text = gerar_estatisticas(df_resultados, meta) # Criar CSV temporário temp_dir = tempfile.mkdtemp() csv_path = os.path.join(temp_dir, "monte_carlo_resultados.csv") df_resultados.to_csv(csv_path, index=False, encoding='utf-8') return fig, stats_text, csv_path except Exception as e: return None, f"❌ Erro na simulação: {str(e)}", None # Interface Gradio with gr.Blocks(title="Simulação de Monte Carlo - Entregas", theme=gr.themes.Soft()) as app: gr.Markdown(""" # 🎲 Simulação de Monte Carlo - Previsão de Entregas Esta aplicação simula diferentes cenários para prever **em quantas semanas** uma meta de entregas será atingida, baseando-se em dados históricos de performance. """) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### ⚙️ Parâmetros da Simulação") n_simulacoes = gr.Slider(minimum=10, maximum=1000, value=500, step=10, label="Número de Simulações") semanas_futuras = gr.Slider(minimum=5, maximum=52, value=15, step=1, label="Horizonte (Semanas)") meta = gr.Slider(minimum=10, maximum=500, value=100, step=5, label="Meta de Entregas") btn_simular = gr.Button("🚀 Executar Simulação", variant="primary", size="lg") with gr.Column(scale=2): gr.Markdown("### 📈 Resultados") stats_output = gr.Markdown(label="Estatísticas") plot_output = gr.Plot(label="Visualizações") csv_output = gr.File(label="📥 Download Resultados (CSV)") # Conectar botão à função btn_simular.click( fn=simular_monte_carlo, inputs=[n_simulacoes, semanas_futuras, meta], outputs=[plot_output, stats_output, csv_output] ) # Executar aplicação if __name__ == "__main__": app.launch()