Spaces:
Sleeping
Sleeping
| 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() | |