Pegumenezes commited on
Commit
1ec8606
·
verified ·
1 Parent(s): 6b68a2a

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +285 -257
src/streamlit_app.py CHANGED
@@ -5,274 +5,302 @@ import plotly.express as px
5
  import plotly.graph_objects as go
6
 
7
  # --- Configuração da Página ---
8
- st.set_page_config(layout="wide", page_title="Dashboard de Reciclagem v0 Estruturado")
9
-
10
- # --- Dados (Originais do v0) ---
11
- meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']
12
- dados_2024 = {
13
- 'Mes': meses,
14
- 'Papel_Papelao': [8047, 11287, 8184, 10183, 5699, 5830, 7465, 5600, 2960, 5175, 9656, 3960],
15
- 'Plastico': [6353, 8771, 6993, 8050, 4880, 5296, 5937, 4747, 2446, 4109, 7667, 3367],
16
- 'Metal': [1061, 2025, 1121, 1832, 716, 936, 1553, 904, 361, 630, 1904, 569],
17
- 'Vidro': [5248, 6929, 6014, 5821, 3697, 3655, 4950, 3360, 1580, 3261, 6173, 2357]
18
- }
19
- df_2024 = pd.DataFrame(dados_2024)
20
-
21
- dados_anuais = {
22
- 'Ano': [2022, 2023, 2024],
23
- 'Papel_Papelao': [18780, 58718, 84046],
24
- 'Plastico': [5340, 1041, 8279],
25
- 'Metal': [1300, 1737, 19955],
26
- 'Vidro': [0, 725, 1709]
27
- }
28
- df_anuais = pd.DataFrame(dados_anuais)
29
-
30
- precos = {
31
- 'Papel_Papelao': 0.50,
32
- 'Plastico': 0.80,
33
- 'Metal': 2.00,
34
- 'Vidro': 0.30
35
- }
36
 
37
- # --- Estatísticas descritivas (Originais do v0) ---
38
- df_2024_numeric = df_2024.drop(columns='Mes')
39
- desc = df_2024_numeric.describe().T
40
- desc['Mediana'] = df_2024_numeric.median()
41
- desc = desc[['mean', 'Mediana', 'std', 'min', 'max']]
42
- desc.columns = ['Média', 'Mediana', 'Desvio Padrão', 'Mínimo', 'Máximo']
43
-
44
- # --- Simulação de faturamento (Originais do v0) ---
45
- estatisticas_simulacao = {} # Renomeado para evitar conflito com o df 'estatisticas' se existir
46
- for material_key in precos.keys(): # Evitar conflito com a variável 'material' do selectbox
47
- media = df_2024_numeric[material_key].mean()
48
- std = df_2024_numeric[material_key].std()
49
- estatisticas_simulacao[material_key] = {'media': media, 'std': std}
50
-
51
- def simular_faturamento_anual(estats, precos_dict, fator=1.0, n_sim=1000, seed=42):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  np.random.seed(seed)
53
- faturamentos = []
54
- for _ in range(n_sim):
55
- total = 0
56
- for mat_sim, stats_sim in estats.items():
57
- medias = stats_sim['media'] * fator
58
- stds = stats_sim['std']
59
- # Garantir que o desvio padrão não seja negativo ou zero se a média for zero
60
- quantidades = np.random.normal(medias, stds if stds > 0 else 1e-6, 12)
61
- quantidades = np.clip(quantidades, 0, None) # Quantidades não podem ser negativas
62
- total += quantidades.sum() * precos_dict[mat_sim]
63
- faturamentos.append(total)
64
- return np.array(faturamentos)
65
-
66
- cenarios_simulacao = { # Renomeado para evitar conflito
 
 
 
 
 
 
 
 
67
  'Base': 1.0,
68
- 'Otimista': 1.15,
69
- 'Pessimista': 0.85
70
  }
71
- # Cores para os gráficos de cenário
72
- cores_graf_cenarios = {'Base': '#1f77b4', 'Otimista': '#2ca02c', 'Pessimista': '#d62728'}
73
-
74
-
75
- # --- Interface Streamlit ---
76
- st.sidebar.title("🔩 Configurações e Navegação")
77
- secao_analise = st.sidebar.radio(
78
- "Selecione a Seção de Análise:",
79
- ("Visão Geral da Coleta", "Análise Estatística da Coleta", "Simulação de Faturamento"),
80
- captions = ["Tendências anuais e mensais", "Descritivas e distribuições", "Projeções e cenários"]
81
- )
82
- st.sidebar.markdown("---")
83
-
84
- # Filtros (relevância depende da seção)
85
- st.sidebar.subheader("Filtros Gerais")
86
- materiais_opcoes = ['Todos'] + list(precos.keys())
87
- material_filtro = st.sidebar.selectbox('Selecione o material:', materiais_opcoes)
88
-
89
- # O filtro de cenário só é relevante para a seção de faturamento
90
- cenario_filtro = 'Todos' # Default
91
- if secao_analise == "Simulação de Faturamento":
92
- cenarios_opcoes_filtro = ['Todos'] + list(cenarios_simulacao.keys())
93
- cenario_filtro = st.sidebar.selectbox('Selecione o cenário de faturamento:', cenarios_opcoes_filtro)
94
-
95
- st.sidebar.markdown("---")
96
- st.sidebar.info(f"Dashboard de Reciclagem (v0 Estruturado)\nDados de 2022-2024. Simulações baseadas em 2024.\nAtualizado em: {pd.Timestamp('now').strftime('%d/%m/%Y')}")
 
 
 
 
 
 
 
 
97
 
98
 
99
  # --- Conteúdo Principal ---
100
- st.title('♻️ Dashboard de Recicláveis e Faturamento')
101
- st.markdown(f"### Seção Atual: {secao_analise}")
102
- st.markdown("---")
103
-
104
-
105
- if secao_analise == "Visão Geral da Coleta":
106
- st.header("📈 Evolução da Coleta de Materiais")
107
- st.caption(f"Visualizando dados para: {material_filtro}")
108
-
109
- tab_anual, tab_mensal = st.tabs(["Evolução Anual", "Evolução Mensal (2024)"])
110
-
111
- with tab_anual:
112
- if material_filtro != 'Todos':
113
- st.subheader(f'Evolução Anual de {material_filtro}')
114
- if material_filtro in df_anuais.columns:
115
- fig_anual = px.bar(df_anuais, x=df_anuais.index, y=material_filtro,
116
- title=f'Coleta Anual de {material_filtro}',
117
- labels={'value': 'Quantidade (kg)', 'Ano': 'Ano', material_filtro: 'Quantidade (kg)'},
118
- template='plotly_white', text_auto='.2s')
119
- fig_anual.update_layout(xaxis_title='Ano', yaxis_title='Quantidade (kg)')
120
- st.plotly_chart(fig_anual, use_container_width=True)
121
- else:
122
- st.warning(f"Dados anuais não disponíveis para '{material_filtro}'.")
123
- else:
124
- st.subheader('Evolução Anual de Todos os Materiais')
125
- df_anuais_melt = df_anuais.reset_index().melt(id_vars='Ano', var_name='Material', value_name='Quantidade')
126
- fig_anual_todos = px.bar(df_anuais_melt, x='Ano', y='Quantidade', color='Material', barmode='group',
127
- title='Coleta Anual de Todos os Materiais',
128
- labels={'Quantidade': 'Quantidade (kg)', 'Ano': 'Ano'},
129
- template='plotly_white', text_auto='.2s')
130
- fig_anual_todos.update_layout(xaxis_title='Ano', yaxis_title='Quantidade (kg)')
131
- st.plotly_chart(fig_anual_todos, use_container_width=True)
132
-
133
- with tab_mensal:
134
- if material_filtro != 'Todos':
135
- st.subheader(f'Evolução Mensal de {material_filtro} em 2024')
136
- if material_filtro in df_2024.columns: # df_2024 agora tem 'Mes' como index
137
- fig_mensal = px.line(df_2024, x=df_2024.index, y=material_filtro, markers=True,
138
- title=f'Coleta Mensal de {material_filtro} (2024)',
139
- labels={'index': 'Mês', material_filtro: 'Quantidade (kg)'}, template='plotly_white')
140
- fig_mensal.update_layout(xaxis_title='Mês', yaxis_title='Quantidade (kg)')
141
- st.plotly_chart(fig_mensal, use_container_width=True)
142
- else:
143
- st.warning(f"Dados mensais de 2024 não disponíveis para '{material_filtro}'.")
144
- else:
145
- st.subheader('Evolução Mensal de Todos os Materiais em 2024')
146
- df_2024_melt = df_2024_numeric.reset_index().melt(id_vars='Mes', var_name='Material', value_name='Quantidade')
147
- fig_mensal_todos = px.line(df_2024_melt, x='Mes', y='Quantidade', color='Material', markers=True,
148
- title='Coleta Mensal de Todos os Materiais (2024)',
149
- labels={'Mes': 'Mês', 'Quantidade': 'Quantidade (kg)'}, template='plotly_white')
150
- fig_mensal_todos.update_layout(xaxis_title='Mês', yaxis_title='Quantidade (kg)')
151
- st.plotly_chart(fig_mensal_todos, use_container_width=True)
152
-
153
-
154
- elif secao_analise == "Análise Estatística da Coleta":
155
- st.header("📊 Análise Estatística da Coleta (Baseado em Dados Mensais de 2024)")
156
-
157
- tab_desc, tab_box = st.tabs(["Estatísticas Descritivas", "Boxplot dos Materiais"])
158
 
159
- with tab_desc:
160
- st.subheader('Tabela de Estatísticas Descritivas')
161
- st.caption("Quantidades médias, medianas, desvios, mínimos e máximos mensais por material em 2024.")
162
- st.dataframe(desc.style.format('{:,.2f}'), use_container_width=True)
163
-
164
- with tab_box:
165
- st.subheader('Distribuição das Quantidades Mensais por Material (Boxplot)')
166
- st.caption(f"Visualizando para: {material_filtro}")
167
- df_melt_box = df_2024_numeric.reset_index().melt(id_vars='Mes', var_name='Material', value_name='Quantidade')
168
-
169
- if material_filtro != 'Todos':
170
- df_melt_box_filtrado = df_melt_box[df_melt_box['Material'] == material_filtro]
171
- else:
172
- df_melt_box_filtrado = df_melt_box
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
- if not df_melt_box_filtrado.empty:
175
- fig_boxplot = px.box(df_melt_box_filtrado, x='Material', y='Quantidade',
176
- color='Material' if material_filtro == 'Todos' else None,
177
- title='Distribuição das Quantidades Mensais (2024)',
178
- labels={'Quantidade': 'Quantidade (kg)'}, template='plotly_white')
179
- fig_boxplot.update_layout(xaxis_title='Material', yaxis_title='Quantidade (kg)')
180
- st.plotly_chart(fig_boxplot, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  else:
182
- st.warning(f"Não dados para exibir no boxplot para '{material_filtro}'.")
183
-
184
-
185
- elif secao_analise == "Simulação de Faturamento":
186
- st.header("💰 Simulação de Faturamento Anual")
187
- st.caption(f"Analisando Material: {material_filtro}, Cenário: {cenario_filtro}")
188
-
189
- # Funções de plotagem e tabela de resumo da simulação (originais do v0, adaptadas)
190
- def criar_tabela_resumo_simulacao(simulacoes_dict):
191
- dados = []
192
- # Iterar sobre as chaves dos cenários de simulação para manter a ordem
193
- for nome_cenario_sim in cenarios_simulacao.keys():
194
- if nome_cenario_sim in simulacoes_dict:
195
- media_val = simulacoes_dict[nome_cenario_sim].mean()
196
- std_val = simulacoes_dict[nome_cenario_sim].std()
197
- dados.append([nome_cenario_sim, f"R$ {media_val:,.2f}", f"R$ {std_val:,.2f}"])
198
- df_res = pd.DataFrame(dados, columns=['Cenário', 'Faturamento Anual Médio', 'Desvio Padrão'])
199
- return df_res.set_index('Cenário')
200
-
201
- def criar_grafico_cenarios_simulacao(simulacoes_dict, titulo_grafico):
202
- fig = go.Figure()
203
- # Iterar sobre as chaves dos cenários de simulação para manter a ordem e cores
204
- for nome_cenario_sim in cenarios_simulacao.keys():
205
- if nome_cenario_sim in simulacoes_dict:
206
- fig.add_trace(go.Histogram(
207
- x=simulacoes_dict[nome_cenario_sim],
208
- name=nome_cenario_sim,
209
- histnorm='probability density',
210
- opacity=0.6,
211
- nbinsx=40, # Ajustado para melhor visualização
212
- marker_color=cores_graf_cenarios[nome_cenario_sim],
213
- showlegend=True
214
- ))
215
- media_val = simulacoes_dict[nome_cenario_sim].mean()
216
- fig.add_vline(x=media_val, line_dash='dash', line_color=cores_graf_cenarios[nome_cenario_sim],
217
- annotation_text=f'Média {nome_cenario_sim}: R${media_val:,.0f}',
218
- annotation_position='top right' if nome_cenario_sim != 'Pessimista' else 'top left',
219
- annotation_font_size=10)
220
- fig.update_layout(
221
- barmode='overlay',
222
- title_text=titulo_grafico,
223
- xaxis_title_text='Faturamento Total Anual (R$)',
224
- yaxis_title_text='Densidade de Probabilidade',
225
- legend_title_text='Cenário',
226
- template='plotly_white',
227
- title_font_size=16
228
  )
229
- return fig
230
 
231
- # Lógica de simulação e exibição (adaptada do v0)
232
- if material_filtro != 'Todos':
233
- estats_filtradas = {material_filtro: estatisticas_simulacao[material_filtro]}
234
- precos_filtrados = {material_filtro: precos[material_filtro]}
235
- titulo_base_grafico = f"Simulação para {material_filtro}"
236
- else:
237
- estats_filtradas = estatisticas_simulacao
238
- precos_filtrados = precos
239
- titulo_base_grafico = "Simulação para Todos os Materiais"
240
-
241
- if cenario_filtro != 'Todos':
242
- # Simulação para um cenário específico
243
- fator_unico = cenarios_simulacao[cenario_filtro]
244
- dados_simulados_unicos = simular_faturamento_anual(estats_filtradas, precos_filtrados, fator=fator_unico)
245
-
246
- st.subheader(f"Distribuição para Cenário: {cenario_filtro} ({titulo_base_grafico.split('para ')[1]})")
247
- fig_sim_unica = px.histogram(dados_simulados_unicos, nbins=50, histnorm='probability density',
248
- title=f'Faturamento Anual Simulado - {titulo_base_grafico} ({cenario_filtro})',
249
- labels={'value': 'Faturamento Anual (R$)'}, template='plotly_white')
250
- fig_sim_unica.update_traces(marker_color=cores_graf_cenarios.get(cenario_filtro, '#808080'))
251
- media_sim_unica = dados_simulados_unicos.mean()
252
- fig_sim_unica.add_vline(x=media_sim_unica, line_dash='dash', line_color='black',
253
- annotation_text=f'Média: R$ {media_sim_unica:,.0f}', annotation_position='top right')
254
- st.plotly_chart(fig_sim_unica, use_container_width=True)
255
- st.write(f"**Faturamento Anual Médio Simulado:** R$ {media_sim_unica:,.2f}")
256
- st.write(f"**Desvio Padrão:** R$ {dados_simulados_unicos.std():,.2f}")
257
-
258
- else: # cenario_filtro == 'Todos'
259
- # Simulações para todos os cenários (Base, Otimista, Pessimista)
260
- simulacoes_completas = {
261
- nome_cen: simular_faturamento_anual(estats_filtradas, precos_filtrados, fator=fator_cen)
262
- for nome_cen, fator_cen in cenarios_simulacao.items()
263
- }
264
- st.subheader(f"Comparativo de Cenários ({titulo_base_grafico.split('para ')[1]})")
265
-
266
- tab_graf_sim, tab_tab_sim = st.tabs(["Distribuição dos Cenários", "Resumo Estatístico"])
267
 
268
- with tab_graf_sim:
269
- fig_todos_cenarios = criar_grafico_cenarios_simulacao(simulacoes_completas,
270
- f"Comparativo de Cenários de Faturamento ({titulo_base_grafico.split('para ')[1]})")
271
- st.plotly_chart(fig_todos_cenarios, use_container_width=True)
272
-
273
- with tab_tab_sim:
274
- df_resumo_todos_cenarios = criar_tabela_resumo_simulacao(simulacoes_completas)
275
- st.dataframe(df_resumo_todos_cenarios, use_container_width=True)
276
 
277
- else:
278
- st.error("Seção de análise não reconhecida.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  import plotly.graph_objects as go
6
 
7
  # --- Configuração da Página ---
8
+ st.set_page_config(
9
+ layout="wide",
10
+ page_title="Dashboard Estratégico de Reciclagem",
11
+ page_icon="♻️"
12
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
+ # --- Carregamento e Preparação dos Dados ---
15
+ @st.cache_data
16
+ def carregar_dados():
17
+ meses = ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez']
18
+ dados_2024 = {
19
+ 'Mes': meses,
20
+ 'Papel_Papelao': [8047, 11287, 8184, 10183, 5699, 5830, 7465, 5600, 2960, 5175, 9656, 3960],
21
+ 'Plastico': [6353, 8771, 6993, 8050, 4880, 5296, 5937, 4747, 2446, 4109, 7667, 3367],
22
+ 'Metal': [1061, 2025, 1121, 1832, 716, 936, 1553, 904, 361, 630, 1904, 569],
23
+ 'Vidro': [5248, 6929, 6014, 5821, 3697, 3655, 4950, 3360, 1580, 3261, 6173, 2357]
24
+ }
25
+ df_2024 = pd.DataFrame(dados_2024)
26
+
27
+ dados_anuais = {
28
+ 'Ano': [2022, 2023, 2024],
29
+ 'Papel_Papelao': [18780, 58718, 84046],
30
+ 'Plastico': [5340, 1041, 8279],
31
+ 'Metal': [1300, 1737, 19955],
32
+ 'Vidro': [0, 725, 1709]
33
+ }
34
+ df_anuais = pd.DataFrame(dados_anuais).set_index('Ano')
35
+
36
+ precos = {
37
+ 'Papel_Papelao': 0.50,
38
+ 'Plastico': 0.80,
39
+ 'Metal': 2.00,
40
+ 'Vidro': 0.30
41
+ }
42
+ # DataFrame numérico para simulação e estatísticas
43
+ df_2024_numeric = df_2024.drop(columns='Mes')
44
+ return df_2024, df_anuais, precos, df_2024_numeric
45
+
46
+ df_2024_original, df_anuais, precos, df_2024_numeric = carregar_dados()
47
+
48
+ # --- Simulação de Faturamento com Bootstrapping ---
49
+ @st.cache_data
50
+ def simular_faturamento_bootstrap(df_dados_mensais_numeric, precos_dict, fator_cenario=1.0, n_simulacoes=2000, seed=42):
51
  np.random.seed(seed)
52
+ faturamentos_simulados_ano = []
53
+
54
+ if df_dados_mensais_numeric.empty:
55
+ return np.array([0] * n_simulacoes)
56
+
57
+ for _ in range(n_simulacoes):
58
+ faturamento_anual_iteracao = 0
59
+ # Amostra 12 meses COM REPOSIÇÃO dos dados históricos mensais
60
+ # Cada "linha" amostrada representa um mês com as quantidades de todos os materiais
61
+ df_amostrado_para_ano = df_dados_mensais_numeric.sample(n=12, replace=True, random_state=np.random.randint(0, 100000))
62
+
63
+ for material, preco_unitario in precos_dict.items():
64
+ if material in df_amostrado_para_ano.columns:
65
+ quantidade_total_material_ano = df_amostrado_para_ano[material].sum()
66
+ # Aplica o fator de cenário às quantidades anuais amostradas
67
+ quantidade_ajustada_cenario = quantidade_total_material_ano * fator_cenario
68
+ faturamento_anual_iteracao += quantidade_ajustada_cenario * preco_unitario
69
+ faturamentos_simulados_ano.append(faturamento_anual_iteracao)
70
+ return np.array(faturamentos_simulados_ano)
71
+
72
+ cenarios_sim = {
73
+ 'Pessimista': 0.85, # Redução de 15% na coleta
74
  'Base': 1.0,
75
+ 'Otimista': 1.15 # Aumento de 15% na coleta
 
76
  }
77
+ cores_cenarios_sim = {'Base': '#1f77b4', 'Otimista': '#2ca02c', 'Pessimista': '#d62728'}
78
+
79
+ # Realizar simulações para os cenários
80
+ @st.cache_data
81
+ def calcular_todas_simulacoes(df_numeric_data, precos_data):
82
+ simulacoes_completas = {}
83
+ for nome_cen, fator in cenarios_sim.items():
84
+ # Passar uma cópia ou garantir que os objetos sejam "hashable" para o cache
85
+ simulacoes_completas[nome_cen] = simular_faturamento_bootstrap(df_numeric_data.copy(), dict(precos_data), fator_cenario=fator)
86
+ return simulacoes_completas
87
+
88
+ simulacoes_faturamento = calcular_todas_simulacoes(df_2024_numeric, precos)
89
+
90
+
91
+ # --- Funções Auxiliares para Gráficos e Tabelas ---
92
+ def formatar_brl(valor):
93
+ return f"R$ {valor:,.2f}"
94
+
95
+ # --- Sidebar para Navegação ---
96
+ with st.sidebar:
97
+ st.markdown("<h1 style='text-align: center; font-size: 26px;'>♻️ Análise Estratégica</h1>", unsafe_allow_html=True)
98
+ st.markdown("---")
99
+ pagina_selecionada = st.radio(
100
+ "Menu de Navegação:",
101
+ ["🎯 Visão Geral", "💰 Análise de Faturamento", "📦 Análise da Coleta"],
102
+ captions=["KPIs e Resumos Globais", "Detalhes e Cenários de Receita", "Volumes e Tendências de Materiais"]
103
+ )
104
+ st.markdown("---")
105
+ if pagina_selecionada != "🎯 Visão Geral":
106
+ st.subheader("Filtros Detalhados")
107
+ materiais_disponiveis = ['Todos'] + list(precos.keys())
108
+ material_filtro_detalhe = st.selectbox("Selecionar Material:", materiais_disponiveis, key="mat_detalhe")
109
+ st.markdown("---")
110
+ st.info(f"Última atualização: {pd.Timestamp.now(tz='America/Sao_Paulo').strftime('%d/%m/%Y %H:%M')}")
111
 
112
 
113
  # --- Conteúdo Principal ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
+ if pagina_selecionada == "🎯 Visão Geral":
116
+ st.title("🎯 Visão Geral Estratégica")
117
+ st.markdown("Principais indicadores de desempenho e projeções de faturamento.")
118
+ st.markdown("---")
119
+
120
+ # KPIs
121
+ kpi1, kpi2, kpi3, kpi4 = st.columns(4)
122
+ sim_base = simulacoes_faturamento['Base']
123
+ media_faturamento_base = np.mean(sim_base)
124
+ p05_faturamento_base = np.percentile(sim_base, 5)
125
+ p95_faturamento_base = np.percentile(sim_base, 95)
126
+
127
+ ultimo_ano_real = df_anuais.index.max()
128
+ coleta_total_ultimo_ano = df_anuais.loc[ultimo_ano_real].sum()
129
+
130
+ # Material mais rentável (baseado na média de 2024 e preços)
131
+ coleta_media_2024_material = df_2024_numeric.mean() # Média mensal
132
+ faturamento_medio_mensal_material = coleta_media_2024_material * pd.Series(precos)
133
+ material_mais_rentavel = faturamento_medio_mensal_material.idxmax()
134
+ rentabilidade_max = faturamento_medio_mensal_material.max() * 12 # Anualizado
135
+
136
+ with kpi1:
137
+ st.metric(label="Faturamento Anual Médio Simulado (Base)", value=formatar_brl(media_faturamento_base),
138
+ help="Média das simulações de faturamento para o cenário base.")
139
+ with kpi2:
140
+ st.metric(label="Intervalo de Confiança 90% (Base)", value=f"{formatar_brl(p05_faturamento_base)} - {formatar_brl(p95_faturamento_base)}",
141
+ help="Intervalo provável (90% de confiança) para o faturamento anual no cenário base.")
142
+ with kpi3:
143
+ st.metric(label=f"Coleta Total em {ultimo_ano_real}", value=f"{coleta_total_ultimo_ano:,.0f} kg",
144
+ help="Soma de todos os materiais coletados no último ano com dados reais.")
145
+ with kpi4:
146
+ st.metric(label="Material Mais Rentável (Est. Anual)", value=f"{material_mais_rentavel}",
147
+ help=f"Estimativa anual: {formatar_brl(rentabilidade_max)}")
148
+ st.markdown("---")
149
+
150
+
151
+ # Gráficos da Visão Geral
152
+ col_g1, col_g2 = st.columns([6, 4]) # Ajustar proporções das colunas
153
+
154
+ with col_g1:
155
+ st.subheader("Distribuição do Faturamento Anual Simulado")
156
+ fig_dist_faturamento = go.Figure()
157
+ for nome_cen, data_sim in simulacoes_faturamento.items():
158
+ fig_dist_faturamento.add_trace(go.Histogram(
159
+ x=data_sim, name=nome_cen, histnorm='probability density',
160
+ opacity=0.7, marker_color=cores_cenarios_sim[nome_cen]
161
+ ))
162
+ fig_dist_faturamento.update_layout(
163
+ barmode='overlay', title_text="Comparativo de Cenários de Faturamento",
164
+ xaxis_title="Faturamento Anual Simulado (R$)", yaxis_title="Densidade",
165
+ legend_title_text='Cenário', template='plotly_white', height=400,
166
+ margin=dict(t=50, b=10)
167
+ )
168
+ st.plotly_chart(fig_dist_faturamento, use_container_width=True)
169
+
170
+ with col_g2:
171
+ st.subheader("Composição do Faturamento (Base)")
172
+ # Calcular faturamento médio por material no cenário base
173
+ faturamento_material_base_lista = []
174
+ for material_nome in precos.keys():
175
+ sim_material_base = simular_faturamento_bootstrap(df_2024_numeric[[material_nome]], {material_nome: precos[material_nome]}, fator_cenario=1.0)
176
+ faturamento_material_base_lista.append({'Material': material_nome, 'Faturamento Médio': np.mean(sim_material_base)})
177
 
178
+ df_faturamento_material_base = pd.DataFrame(faturamento_material_base_lista)
179
+
180
+ fig_comp_faturamento = px.pie(df_faturamento_material_base, values='Faturamento Médio', names='Material',
181
+ title="Estimativa da Composição do Faturamento (Cenário Base)",
182
+ hole=0.4, template='plotly_white', height=400)
183
+ fig_comp_faturamento.update_layout(margin=dict(t=50, b=10))
184
+ st.plotly_chart(fig_comp_faturamento, use_container_width=True)
185
+
186
+ st.subheader("Evolução da Coleta Total Anual (Histórico)")
187
+ df_coleta_total_anual = df_anuais.sum(axis=1).reset_index()
188
+ df_coleta_total_anual.columns = ['Ano', 'Coleta Total (kg)']
189
+ fig_evol_coleta = px.bar(df_coleta_total_anual, x='Ano', y='Coleta Total (kg)',
190
+ text_auto='.2s', template='plotly_white',
191
+ title="Volume Total de Materiais Coletados por Ano")
192
+ fig_evol_coleta.update_traces(textposition='outside')
193
+ fig_evol_coleta.update_layout(height=350, yaxis_title="Coleta Total (kg)")
194
+ st.plotly_chart(fig_evol_coleta, use_container_width=True)
195
+
196
+
197
+ elif pagina_selecionada == "💰 Análise de Faturamento":
198
+ st.title("💰 Análise Detalhada de Faturamento")
199
+ st.markdown(f"Explorando as simulações de faturamento. Filtro de Material: **{material_filtro_detalhe}**")
200
+ st.markdown("---")
201
+
202
+ simulacoes_faturamento_detalhe = {}
203
+ if material_filtro_detalhe == 'Todos':
204
+ simulacoes_faturamento_detalhe = simulacoes_faturamento
205
+ titulo_sufixo = " (Todos Materiais)"
206
+ else:
207
+ if material_filtro_detalhe in precos:
208
+ df_material_unico_numeric = df_2024_numeric[[material_filtro_detalhe]]
209
+ precos_material_unico = {material_filtro_detalhe: precos[material_filtro_detalhe]}
210
+ for nome_cen, fator in cenarios_sim.items():
211
+ simulacoes_faturamento_detalhe[nome_cen] = simular_faturamento_bootstrap(
212
+ df_material_unico_numeric, precos_material_unico, fator_cenario=fator
213
+ )
214
+ titulo_sufixo = f" ({material_filtro_detalhe})"
215
  else:
216
+ st.warning(f"Material '{material_filtro_detalhe}' não encontrado para simulação detalhada.")
217
+ titulo_sufixo = ""
218
+
219
+
220
+ if simulacoes_faturamento_detalhe:
221
+ # Tabela de Resumo
222
+ st.subheader(f"Resumo Estatístico das Simulações{titulo_sufixo}")
223
+ dados_resumo_lista = []
224
+ for nome_cen, data_sim in simulacoes_faturamento_detalhe.items():
225
+ dados_resumo_lista.append({
226
+ 'Cenário': nome_cen,
227
+ 'Faturamento Médio': formatar_brl(np.mean(data_sim)),
228
+ 'Mediana': formatar_brl(np.median(data_sim)),
229
+ 'Desvio Padrão': formatar_brl(np.std(data_sim)),
230
+ 'Mínimo (P5)': formatar_brl(np.percentile(data_sim, 5)),
231
+ 'Máximo (P95)': formatar_brl(np.percentile(data_sim, 95))
232
+ })
233
+ df_resumo_sim = pd.DataFrame(dados_resumo_lista).set_index('Cenário')
234
+ st.dataframe(df_resumo_sim, use_container_width=True)
235
+ st.markdown("---")
236
+
237
+ # Gráfico de Distribuição
238
+ st.subheader(f"Distribuição do Faturamento Simulado{titulo_sufixo}")
239
+ fig_dist_detalhe = go.Figure()
240
+ for nome_cen, data_sim in simulacoes_faturamento_detalhe.items():
241
+ fig_dist_detalhe.add_trace(go.Histogram(
242
+ x=data_sim, name=nome_cen, histnorm='probability density',
243
+ opacity=0.7, marker_color=cores_cenarios_sim[nome_cen]
244
+ ))
245
+ fig_dist_detalhe.update_layout(
246
+ barmode='overlay', title_text=f"Comparativo de Cenários{titulo_sufixo}",
247
+ xaxis_title="Faturamento Anual Simulado (R$)", yaxis_title="Densidade",
248
+ legend_title_text='Cenário', template='plotly_white', height=450
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  )
250
+ st.plotly_chart(fig_dist_detalhe, use_container_width=True)
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
 
253
+ elif pagina_selecionada == "📦 Análise da Coleta":
254
+ st.title("📦 Análise Detalhada da Coleta")
255
+ st.markdown(f"Analisando dados de coleta. Filtro de Material: **{material_filtro_detalhe}**")
256
+ st.markdown("---")
 
 
 
 
257
 
258
+ df_coleta_analise = df_2024_original # Contém a coluna 'Mes'
259
+
260
+ # Evolução Mensal
261
+ st.subheader("Evolução Mensal da Coleta (2024)")
262
+ if material_filtro_detalhe == 'Todos':
263
+ df_melt_mensal = df_coleta_analise.melt(id_vars='Mes', value_vars=precos.keys(), var_name='Material', value_name='Quantidade')
264
+ fig_evol_mensal = px.line(df_melt_mensal, x='Mes', y='Quantidade', color='Material', markers=True,
265
+ title="Coleta Mensal de Todos os Materiais", template='plotly_white')
266
+ else:
267
+ if material_filtro_detalhe in df_coleta_analise.columns:
268
+ fig_evol_mensal = px.line(df_coleta_analise, x='Mes', y=material_filtro_detalhe, markers=True,
269
+ title=f"Coleta Mensal de {material_filtro_detalhe}", template='plotly_white',
270
+ labels={material_filtro_detalhe: 'Quantidade (kg)'})
271
+ else:
272
+ st.warning(f"Dados mensais para '{material_filtro_detalhe}' não encontrados.")
273
+ fig_evol_mensal = go.Figure()
274
+ fig_evol_mensal.update_layout(yaxis_title="Quantidade (kg)", height=400)
275
+ st.plotly_chart(fig_evol_mensal, use_container_width=True)
276
+ st.markdown("---")
277
+
278
+ # Boxplot da Coleta Mensal
279
+ st.subheader("Distribuição da Coleta Mensal por Material (2024)")
280
+ df_melt_box_coleta = df_coleta_analise.melt(id_vars='Mes', value_vars=precos.keys(), var_name='Material', value_name='Quantidade')
281
+
282
+ df_plot_box = df_melt_box_coleta
283
+ if material_filtro_detalhe != 'Todos':
284
+ df_plot_box = df_melt_box_coleta[df_melt_box_coleta['Material'] == material_filtro_detalhe]
285
+
286
+ if not df_plot_box.empty:
287
+ fig_boxplot_coleta = px.box(df_plot_box, x='Material', y='Quantidade',
288
+ color='Material' if material_filtro_detalhe == 'Todos' else None,
289
+ title="Boxplot da Coleta Mensal", template='plotly_white')
290
+ fig_boxplot_coleta.update_layout(yaxis_title="Quantidade (kg)", height=450)
291
+ st.plotly_chart(fig_boxplot_coleta, use_container_width=True)
292
+ else:
293
+ st.info("Selecione um material para ver o boxplot ou 'Todos' para comparação.")
294
+ st.markdown("---")
295
+
296
+ # Estatísticas Descritivas da Coleta
297
+ st.subheader("Estatísticas Descritivas da Coleta Mensal (2024)")
298
+ desc_coleta = df_2024_numeric.describe().T[['mean', 'std', 'min', '50%', 'max']]
299
+ desc_coleta.columns = ['Média Mensal', 'Desvio Padrão', 'Mínimo', 'Mediana (50%)', 'Máximo']
300
+ if material_filtro_detalhe == 'Todos':
301
+ st.dataframe(desc_coleta.style.format("{:,.0f} kg"), use_container_width=True)
302
+ else:
303
+ if material_filtro_detalhe in desc_coleta.index:
304
+ st.dataframe(desc_coleta.loc[[material_filtro_detalhe]].style.format("{:,.0f} kg"), use_container_width=True)
305
+ else:
306
+ st.warning(f"Estatísticas para '{material_filtro_detalhe}' não disponíveis.")