drguilhermeapolinario commited on
Commit
0f2a4a4
·
verified ·
1 Parent(s): 8bae19a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +534 -534
app.py CHANGED
@@ -1,534 +1,534 @@
1
- """_summary_
2
- LIMPEZA E VISUALIZAÇÃO DE CSV
3
- Returns:
4
- DATAFRAMES
5
- """
6
-
7
- import folium
8
- import geopandas as gpd
9
- import plotly.express as px
10
- import streamlit as st
11
- from branca.colormap import LinearColormap
12
- from groq import Groq
13
- from streamlit_extras.add_vertical_space import add_vertical_space
14
- from streamlit_extras.stylable_container import stylable_container
15
- from streamlit_folium import folium_static
16
- from streamlit_option_menu import option_menu
17
-
18
- from data_cleaning import criar_dataframe, iniciar, limpa_rci, separa_grupos
19
-
20
- st.set_page_config(
21
- page_title="Dashboard UBS Flamengo",
22
- page_icon="📊",
23
- layout="wide",
24
- initial_sidebar_state="expanded",
25
- )
26
-
27
-
28
- def processar_arquivo(file):
29
- """
30
- A function that processes the uploaded file by writing it to a temporary CSV file,
31
- then performs various data cleaning operations on the CSV data to create and return dataframes.
32
- Returns:
33
- - dataframes: the processed dataframes generated from the uploaded file
34
- """
35
- with open("temp.csv", "wb") as f:
36
- f.write(file.getbuffer())
37
- csv_st = iniciar("temp.csv")
38
- clean_txt = limpa_rci(csv_st)
39
- grupos_encontrados = separa_grupos(clean_txt)
40
- dfs = criar_dataframe(grupos_encontrados)
41
-
42
- return dfs
43
-
44
-
45
- # Inicializar variáveis dos DataFrames como None
46
- DF_IDADE = None
47
- DF_GENERO = None
48
- DF_COR = None
49
- DF_DEFICIENCIA = None
50
- DF_DOENCAS = None
51
-
52
- # Upload de arquivo CSV na barra lateral
53
- uploaded_file = st.sidebar.file_uploader("Escolha um arquivo CSV", type="csv")
54
-
55
- if uploaded_file is not None:
56
- dataframes = processar_arquivo(uploaded_file)
57
-
58
- # Atribuir os DataFrames a variáveis específicas para uso posterior
59
- DF_IDADE = dataframes.get("Idade")
60
- DF_GENERO = dataframes.get("genero")
61
- DF_COR = dataframes.get("cor")
62
- DF_DEFICIENCIA = dataframes.get("deficiencia")
63
- DF_DOENCAS = dataframes.get("doencas")
64
-
65
- # Aplicar capitalização à coluna "Descrição" em cada DataFrame
66
- if DF_IDADE is not None:
67
- DF_IDADE["Descrição"] = DF_IDADE["Descrição"].str.capitalize()
68
-
69
- if DF_GENERO is not None:
70
- DF_GENERO["Descrição"] = DF_GENERO["Descrição"].str.capitalize()
71
-
72
- if DF_COR is not None:
73
- DF_COR["Descrição"] = DF_COR["Descrição"].str.capitalize()
74
-
75
- if DF_DEFICIENCIA is not None:
76
- DF_DEFICIENCIA["Descrição"] = DF_DEFICIENCIA["Descrição"].str.capitalize()
77
-
78
- if DF_DOENCAS is not None:
79
- DF_DOENCAS["Descrição"] = DF_DOENCAS["Descrição"].str.capitalize()
80
- else:
81
- st.sidebar.info("Adicione um arquivo .csv.")
82
-
83
-
84
- def gerar_resumo_df():
85
- """
86
- Generates a summary of the dataframes.
87
-
88
- This function iterates over the local and global variables to check if any of them
89
- are named "DF_IDADE", "DF_GENERO", "DF_COR", "DF_DEFICIENCIA", or "DF_DOENCAS".
90
- If a dataframe is found, it generates a summary of the dataframe by printing its
91
- column names and the count of each unique value in the "Descrição" column.
92
-
93
- Returns:
94
- str: A string containing the summary of the dataframes.
95
- """
96
- resumo = ""
97
-
98
- if "DF_IDADE" in locals() or "DF_IDADE" in globals():
99
- # Assumindo que DF_IDADE tem colunas 'Faixa_Etaria', 'Masculino', 'Feminino'
100
- resumo += "Resumo DF_IDADE:\n"
101
- resumo += DF_IDADE.to_string(index=False) + "\n\n"
102
-
103
- if "DF_GENERO" in locals() or "DF_GENERO" in globals():
104
- resumo += f"Resumo DF_GENERO:\n{DF_GENERO.value_counts().to_string()}\n\n"
105
-
106
- if "DF_COR" in locals() or "DF_COR" in globals():
107
- resumo += f"Resumo DF_COR:\n{DF_COR.value_counts().to_string()}\n\n"
108
-
109
- if "DF_DEFICIENCIA" in locals() or "DF_DEFICIENCIA" in globals():
110
- resumo += (
111
- f"Resumo DF_DEFICIENCIA:\n{DF_DEFICIENCIA.value_counts().to_string()}\n\n"
112
- )
113
-
114
- if "DF_DOENCAS" in locals() or "DF_DOENCAS" in globals():
115
- resumo += f"Resumo DF_DOENCAS:\n{DF_DOENCAS.value_counts().to_string()}\n\n"
116
-
117
- return resumo
118
-
119
-
120
- #######################################
121
- #######################################
122
- ############ BANNER ##################
123
- #######################################
124
- #######################################
125
-
126
- with stylable_container(
127
- key="banner",
128
- css_styles="""
129
- img {
130
- width: 1800px;
131
- height: 600px;
132
- overflow: hidden;
133
- position: relative;
134
- object-fit: cover;
135
- border-radius: 14px; /* Adiciona bordas arredondadas */
136
- mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
137
- -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); /* For Safari */
138
- }
139
- """,
140
- ):
141
- st.image("./banner.png")
142
-
143
-
144
- #######################################
145
- ############ BANNER ##################
146
- #######################################
147
-
148
- add_vertical_space(5)
149
-
150
- c1, c2 = st.columns([5, 5])
151
- with c1:
152
- st.markdown(
153
- """
154
- # Dashboard UBS Flamengo
155
-
156
- :brain: _by Dr. Guilherme Apolinário_
157
-
158
- ### Bem-vindo! :wave:
159
- """
160
- )
161
-
162
- with c2:
163
- with stylable_container(
164
- key="graph_entry",
165
- css_styles="""
166
- img {
167
- width: 600px;
168
- height: 160px;
169
- overflow: hidden;
170
- position: relative;
171
- object-fit: cover;
172
- mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
173
- -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); /* For Safari */
174
- }
175
- """,
176
- ):
177
- st.image("./graph1.png")
178
- st.divider()
179
-
180
-
181
- ###########################################
182
- ###########################################
183
- ###########################################
184
- ############### LATERAL ##################
185
- ###########################################
186
- ###########################################
187
- ###########################################
188
-
189
- st.sidebar.markdown(
190
- """
191
- ### Informações:
192
- - Análise de dados do relatório de cadastro individual.
193
- - Iniciativa - Ubs Flamengo
194
- - Acesso livre
195
-
196
- ### Links:
197
- ##
198
- ##### - [PEC SUS](https://sape.pecsus.com.br/) 📝
199
- ###
200
- ##### - [Obsidian - Dr Guilherme](http://dr-guilhermeapolinario.com) 🌎
201
- ##### - [GitHub - Dr Guilherme](http://dr-guilhermeapolinario.com) 🌎
202
-
203
- """
204
- )
205
-
206
- ###########################################
207
- ############### LATERAL ##################
208
- ###########################################
209
-
210
- st.markdown(
211
- """
212
- ### Intruções:
213
- ##### - Acesse o site :orange[PEC SUS], na aba esquerda selecione consolidado, :blue[*RELATÓRIO DE CADASTRO INDIVIDUAL*]
214
- ##### - Selecione a opção :orange[baixar arquivo .csv]. Após baixar o arquivo, selecione o arquivo .csv na aba ao lado, e pronto.
215
- ##### - Clique no botão de expansão abaixo para iniciar o processo.
216
- ##### - Utilize o chatbot com a inteligência artificial 🤖 Zé Flamengo para tirar suas dúvidas.
217
- """
218
- )
219
- st.divider()
220
-
221
-
222
- st.markdown(
223
- """
224
- ### :world_map: **Conhecendo a área de abrangencia daUBS Flamengo: (IBGE 2022)**
225
-
226
- """
227
- )
228
-
229
- m_pop = st.checkbox(":man-woman-girl-boy: **População**")
230
- if m_pop:
231
- gdf = gpd.read_file("flamengo_ibge2022.geojson").to_crs(epsg=4326)
232
- LATITUDE = -19.971591804
233
- LONGITUDE = -44.057912815
234
- colorscale = px.colors.sequential.swatches_continuous()
235
- colorscale = px.colors.sequential.Viridis
236
- colormap = LinearColormap(
237
- colors=colorscale,
238
- vmin=gdf["POP"].min(),
239
- vmax=gdf["POP"].max(),
240
- caption="População residente UBS Flamengo - IBGE 2022",
241
- )
242
-
243
- m = folium.Map(
244
- location=[LATITUDE, LONGITUDE], tiles="Cartodb Positron", zoom_start=15
245
- )
246
-
247
- pop_total = gdf["POP"].sum()
248
- gdf["POP_PERCENT"] = (gdf["POP"] / pop_total * 100).round(2)
249
-
250
- folium.GeoJson(
251
- gdf,
252
- style_function=lambda feature: {
253
- "fillColor": colormap(feature["properties"]["POP"]),
254
- "color": "black",
255
- "weight": 1,
256
- "fillOpacity": 0.4,
257
- },
258
- highlight_function=lambda feature: {
259
- "fillColor": "#ffaf00",
260
- "color": "green",
261
- "weight": 3,
262
- "fillOpacity": 0.6,
263
- },
264
- tooltip=folium.features.GeoJsonTooltip(
265
- fields=["CD_SETOR", "POP", "POP_PERCENT"],
266
- aliases=[
267
- "Setor Censitário:",
268
- "População do Setor:",
269
- "Porcentagem do Total: %",
270
- ],
271
- style=(
272
- "background-color: white; color: #333333; font-family: calibri; font-size: 12px; padding: 10px;"
273
- ),
274
- ),
275
- ).add_to(m)
276
-
277
- colormap.caption = "População residente UBS Flamengo - IBGE 2022"
278
- colormap.add_to(m)
279
- # Alterar o CSS da legenda para reposicioná-la no topo
280
- STYLE_STATEMENT = "<style>.leaflet-control-layers { position: fixed; top: 10px; left: 50px; } </style>"
281
- m.get_root().html.add_child(folium.Element(STYLE_STATEMENT))
282
- folium_static(m)
283
-
284
- st.divider()
285
- add_vertical_space(3)
286
-
287
- st.markdown(
288
- """
289
- ## Dados de saúde Relatório de cadastro individual.
290
- """
291
- )
292
-
293
-
294
- # Função para criar gráficos de barras personalizados
295
- def criar_grafico_personalizado(df, x_col, y_col, titulo):
296
- """
297
- Generates a custom bar chart using Plotly Express.
298
-
299
- Args:
300
- df (pandas.DataFrame): The input dataframe.
301
- x_col (str): The column name to be used as the x-axis.
302
- y_col (str): The column name to be used as the y-axis.
303
- titulo (str): The title of the chart.
304
-
305
- Returns:
306
- plotly.graph_objects.Figure: The generated bar chart.
307
-
308
- """
309
- fig = px.bar(
310
- df,
311
- x=x_col,
312
- y=y_col,
313
- title=titulo,
314
- text=y_col, # Adiciona os rótulos
315
- color=x_col, # Usa uma paleta de cores para a barra
316
- color_discrete_sequence=px.colors.qualitative.Bold # Define uma paleta de cores
317
- )
318
- fig.update_traces(texttemplate='%{text:.2s}', textposition='outside') # Formatação dos rótulos
319
- fig.update_layout(
320
- uniformtext_minsize=8,
321
- uniformtext_mode='hide',
322
- xaxis_title=x_col,
323
- yaxis_title=y_col,
324
- title_font_size=24,
325
- title_font_family="Arial",
326
- title_font_color="blue",
327
- title_x=0.5,
328
- paper_bgcolor='rgba(0,0,0,0)',
329
- plot_bgcolor='rgba(0,0,0,0)',
330
- xaxis_tickangle=-45 # Rotaciona os rótulos do eixo x para melhor legibilidade
331
- )
332
- return fig
333
-
334
- with st.expander(" Adicione o arquivo .csv ao lado para visuzalização", expanded=True):
335
- selected_tab = option_menu(
336
- menu_title=None,
337
- options=["Faixa Etária", "Gênero", "Cor", "Deficiência", "Doenças"],
338
- icons=["person", "gender-female", "person-plus", "person-wheelchair", "capsule-pill"],
339
- menu_icon="cast",
340
- default_index=0,
341
- orientation="horizontal",
342
- styles={
343
- "container": {"padding": "5px", "background-color": "#f9f9f9"},
344
- "icon": {"color": "orange", "font-size": "25px"},
345
- "nav-link": {
346
- "font-size": "16px",
347
- "text-align": "center",
348
- "margin": "0px",
349
- "--hover-color": "#eee",
350
- },
351
- "nav-link-selected": {"background-color": "#02ab21"},
352
- },
353
- )
354
-
355
- # Exibição dos DataFrames com base na aba selecionada
356
- if selected_tab == "Faixa Etária" and DF_IDADE is not None:
357
- st.subheader("Distribuição por Faixa Etária")
358
-
359
-
360
-
361
- col1, col2 = st.columns(2)
362
- with col1:
363
- st.dataframe(DF_IDADE, hide_index=True)
364
- with col2:
365
- # Criar o gráfico de pirâmide etária
366
- # Preparar os dados para o gráfico de pirâmide etária
367
- DF_IDADE["Masculino"] = (
368
- DF_IDADE["Masculino"].astype(int) * -1
369
- ) # Valores negativos para o lado masculino
370
- DF_IDADE["Feminino"] = DF_IDADE["Feminino"].astype(int)
371
- fig_idade = px.bar(
372
- DF_IDADE,
373
- x=["Masculino", "Feminino"],
374
- y="Descrição",
375
- orientation="h",
376
- title="Pirâmide Etária",
377
- labels={"value": "População", "Descrição": "Faixa Etária"},
378
- color="Descrição",
379
- color_discrete_sequence=px.colors.qualitative.Set3,
380
- )
381
- fig_idade.update_layout(
382
- barmode="relative", xaxis_title="População", yaxis_title="Faixa Etária"
383
- )
384
- st.plotly_chart(fig_idade)
385
- DF_IDADE["Masculino"] = DF_IDADE["Masculino"].abs()
386
-
387
- elif selected_tab == "Gênero" and DF_GENERO is not None:
388
- st.subheader("Distribuição por Gênero")
389
- col1, col2 = st.columns(2)
390
- with col1:
391
- st.dataframe(DF_GENERO, hide_index=True)
392
- with col2:
393
- fig_genero = px.pie(
394
- DF_GENERO,
395
- names="Descrição",
396
- values="Valor",
397
- title="Distribuição por Gênero",
398
- color_discrete_sequence=px.colors.qualitative.Pastel
399
- )
400
- st.plotly_chart(fig_genero)
401
-
402
- elif selected_tab == "Cor" and DF_COR is not None:
403
- st.subheader("Distribuição por Cor")
404
- col1, col2 = st.columns(2)
405
- with col1:
406
- st.dataframe(DF_COR, hide_index=True)
407
- with col2:
408
- fig_cor = px.pie(
409
- DF_COR,
410
- names="Descrição",
411
- values="Valor",
412
- title="Distribuição por Cor",
413
- color_discrete_sequence=px.colors.qualitative.Vivid
414
- )
415
- st.plotly_chart(fig_cor)
416
-
417
- elif selected_tab == "Deficiência" and DF_DEFICIENCIA is not None:
418
- st.subheader("Distribuição por Deficiência")
419
- col1, col2 = st.columns(2)
420
- with col1:
421
- st.dataframe(DF_DEFICIENCIA, hide_index=True)
422
- with col2:
423
- fig_deficiencia = criar_grafico_personalizado(
424
- DF_DEFICIENCIA,
425
- x_col="Descrição",
426
- y_col="Valor",
427
- titulo="Distribuição por Deficiência"
428
- )
429
- st.plotly_chart(fig_deficiencia)
430
-
431
- elif selected_tab == "Doenças" and DF_DOENCAS is not None:
432
- st.subheader("Distribuição por Doenças")
433
- col1, col2 = st.columns(2)
434
- with col1:
435
- st.dataframe(DF_DOENCAS, hide_index=True)
436
- with col2:
437
- fig_doencas = criar_grafico_personalizado(
438
- DF_DOENCAS,
439
- x_col="Descrição",
440
- y_col="Valor",
441
- titulo="Distribuição por Doenças"
442
- )
443
- st.plotly_chart(fig_doencas)
444
- add_vertical_space(4)
445
-
446
- st.info(
447
- """
448
- 📢 **CONVERSE COM IA SE TIVER DÚVIDAS NA ANÁLISE DOS GRÁFICOS**"""
449
- )
450
-
451
- add_vertical_space(6)
452
-
453
-
454
- with stylable_container(
455
- key="brain",
456
- css_styles="""
457
- img {
458
- width: 120px;
459
- height: 100px;
460
- overflow: hidden;
461
- position: relative;
462
- object-fit: cover;
463
- border-radius: 14px; /* Adiciona bordas arredondadas */
464
- }
465
- """,
466
- ):
467
- st.image("./brain.png")
468
- # Widget expander para interação da IA
469
- with st.expander(" Converse com o 🤖 Zé Flamengo", expanded=True):
470
- client = Groq(
471
- api_key=st.secrets["GROQ_API_KEY"],
472
- )
473
-
474
- INPUT_KEY = "USER_CHAT_input"
475
-
476
- USER_CHAT = st.text_input(
477
- "Digite sua pergunta sobre saúde na microárea:",
478
- placeholder="Digite sua pergunta aqui...",
479
- )
480
-
481
- if st.button("Enviar pergunta", key="send_button"):
482
- if USER_CHAT is not None: # Verifica se há texto na entrada
483
- try:
484
- RESUMO_DF = gerar_resumo_df()
485
- CHAT_COMPLETION = client.chat.completions.create(
486
- messages=[
487
- {
488
- "role": "system",
489
- "content": f"""
490
- Seu nome é Flávio, assistente virtual de dados médicos epidemiológicos.
491
- Você é um epidemiologista brasileiro com 20 anos de experiência
492
- em análise de dados de saúde de microáreas. Sua função é analisar
493
- a estrutura de saúde de microregiões através de dados coletados
494
- no SUS. Forneça suas respostas sempre em português, seja conciso e
495
- evite conversar sobre outros temas, sempre retornando ao tema da
496
- conversa.
497
- Aqui estão os resumos dos dataframes disponíveis:
498
-
499
- {RESUMO_DF}
500
-
501
- Use essas informações para responder às perguntas do usuário.
502
- Regras:
503
- 1- Seja sempre cortês.
504
- 2- Responda somente assuntos referentes ao resumo.
505
- 3- Caso seja feita alguma pergunta a você diferente de resumos, responda: "Vamos voltar ao trabalho que interessa?"
506
- 4- Responda sempre em português.
507
- 5- Se não souber a resposta, responda: "Desculpe, mas não tennho esta informação."
508
- 6- Nas suas respostas não forneça os nomes dos df_, somente responda as perguntas.
509
-
510
- """,
511
- },
512
- {
513
- "role": "user",
514
- "content": USER_CHAT,
515
- },
516
- ],
517
- model="llama3-70b-8192",
518
- temperature=0.2,
519
- max_tokens=1500,
520
- )
521
- st.write(CHAT_COMPLETION.choices[0].message.content)
522
-
523
- # Limpa o campo de entrada após enviar a pergunta
524
- st.session_state[INPUT_KEY] = ""
525
- except Exception as e:
526
- st.error(f"Erro ao gerar a conclusão do chat: {e}")
527
- else:
528
- st.warning("Por favor, digite uma pergunta antes de enviar.")
529
-
530
-
531
- # Adicione este código fora do expander para evitar a reexecução do script ao pressionar Enter
532
- if "USER_CHAT_input" in st.session_state and st.session_state.USER_CHAT_input:
533
- st.session_state.USER_CHAT_input = ""
534
-
 
1
+ """_summary_
2
+ LIMPEZA E VISUALIZAÇÃO DE CSV
3
+ Returns:
4
+ DATAFRAMES
5
+ """
6
+
7
+ import folium
8
+ import geopandas as gpd
9
+ import plotly.express as px
10
+ import streamlit as st
11
+ from branca.colormap import LinearColormap
12
+ from groq import Groq
13
+ from streamlit_extras.add_vertical_space import add_vertical_space
14
+ from streamlit_extras.stylable_container import stylable_container
15
+ from streamlit_folium import folium_static
16
+ from streamlit_option_menu import option_menu
17
+
18
+ from data_cleaning import criar_dataframe, iniciar, limpa_rci, separa_grupos
19
+
20
+ st.set_page_config(
21
+ page_title="Dashboard UBS Flamengo",
22
+ page_icon="📊",
23
+ layout="wide",
24
+ initial_sidebar_state="expanded",
25
+ )
26
+
27
+
28
+ def processar_arquivo(file):
29
+ """
30
+ A function that processes the uploaded file by writing it to a temporary CSV file,
31
+ then performs various data cleaning operations on the CSV data to create and return dataframes.
32
+ Returns:
33
+ - dataframes: the processed dataframes generated from the uploaded file
34
+ """
35
+ with open("temp.csv", "wb") as f:
36
+ f.write(file.getbuffer())
37
+ csv_st = iniciar("temp.csv")
38
+ clean_txt = limpa_rci(csv_st)
39
+ grupos_encontrados = separa_grupos(clean_txt)
40
+ dfs = criar_dataframe(grupos_encontrados)
41
+
42
+ return dfs
43
+
44
+
45
+ # Inicializar variáveis dos DataFrames como None
46
+ DF_IDADE = None
47
+ DF_GENERO = None
48
+ DF_COR = None
49
+ DF_DEFICIENCIA = None
50
+ DF_DOENCAS = None
51
+
52
+ # Upload de arquivo CSV na barra lateral
53
+ uploaded_file = st.sidebar.file_uploader("Escolha um arquivo CSV", type="csv")
54
+
55
+ if uploaded_file is not None:
56
+ dataframes = processar_arquivo(uploaded_file)
57
+
58
+ # Atribuir os DataFrames a variáveis específicas para uso posterior
59
+ DF_IDADE = dataframes.get("Idade")
60
+ DF_GENERO = dataframes.get("genero")
61
+ DF_COR = dataframes.get("cor")
62
+ DF_DEFICIENCIA = dataframes.get("deficiencia")
63
+ DF_DOENCAS = dataframes.get("doencas")
64
+
65
+ # Aplicar capitalização à coluna "Descrição" em cada DataFrame
66
+ if DF_IDADE is not None:
67
+ DF_IDADE["Descrição"] = DF_IDADE["Descrição"].str.capitalize()
68
+
69
+ if DF_GENERO is not None:
70
+ DF_GENERO["Descrição"] = DF_GENERO["Descrição"].str.capitalize()
71
+
72
+ if DF_COR is not None:
73
+ DF_COR["Descrição"] = DF_COR["Descrição"].str.capitalize()
74
+
75
+ if DF_DEFICIENCIA is not None:
76
+ DF_DEFICIENCIA["Descrição"] = DF_DEFICIENCIA["Descrição"].str.capitalize()
77
+
78
+ if DF_DOENCAS is not None:
79
+ DF_DOENCAS["Descrição"] = DF_DOENCAS["Descrição"].str.capitalize()
80
+ else:
81
+ st.sidebar.info("Adicione um arquivo .csv.")
82
+
83
+
84
+ def gerar_resumo_df():
85
+ """
86
+ Generates a summary of the dataframes.
87
+
88
+ This function iterates over the local and global variables to check if any of them
89
+ are named "DF_IDADE", "DF_GENERO", "DF_COR", "DF_DEFICIENCIA", or "DF_DOENCAS".
90
+ If a dataframe is found, it generates a summary of the dataframe by printing its
91
+ column names and the count of each unique value in the "Descrição" column.
92
+
93
+ Returns:
94
+ str: A string containing the summary of the dataframes.
95
+ """
96
+ resumo = ""
97
+
98
+ if "DF_IDADE" in locals() or "DF_IDADE" in globals():
99
+ # Assumindo que DF_IDADE tem colunas 'Faixa_Etaria', 'Masculino', 'Feminino'
100
+ resumo += "Resumo DF_IDADE:\n"
101
+ resumo += DF_IDADE.to_string(index=False) + "\n\n"
102
+
103
+ if "DF_GENERO" in locals() or "DF_GENERO" in globals():
104
+ resumo += f"Resumo DF_GENERO:\n{DF_GENERO.value_counts().to_string()}\n\n"
105
+
106
+ if "DF_COR" in locals() or "DF_COR" in globals():
107
+ resumo += f"Resumo DF_COR:\n{DF_COR.value_counts().to_string()}\n\n"
108
+
109
+ if "DF_DEFICIENCIA" in locals() or "DF_DEFICIENCIA" in globals():
110
+ resumo += (
111
+ f"Resumo DF_DEFICIENCIA:\n{DF_DEFICIENCIA.value_counts().to_string()}\n\n"
112
+ )
113
+
114
+ if "DF_DOENCAS" in locals() or "DF_DOENCAS" in globals():
115
+ resumo += f"Resumo DF_DOENCAS:\n{DF_DOENCAS.value_counts().to_string()}\n\n"
116
+
117
+ return resumo
118
+
119
+
120
+ #######################################
121
+ #######################################
122
+ ############ BANNER ##################
123
+ #######################################
124
+ #######################################
125
+
126
+ with stylable_container(
127
+ key="banner",
128
+ css_styles="""
129
+ img {
130
+ width: 1800px;
131
+ height: 600px;
132
+ overflow: hidden;
133
+ position: relative;
134
+ object-fit: cover;
135
+ border-radius: 14px; /* Adiciona bordas arredondadas */
136
+ mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
137
+ -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); /* For Safari */
138
+ }
139
+ """,
140
+ ):
141
+ st.image("./banner.png")
142
+
143
+
144
+ #######################################
145
+ ############ BANNER ##################
146
+ #######################################
147
+
148
+ add_vertical_space(5)
149
+
150
+ c1, c2 = st.columns([5, 5])
151
+ with c1:
152
+ st.markdown(
153
+ """
154
+ ## Dashboard UBS Flamengo
155
+
156
+ ###### :brain: _by Dr. Guilherme Apolinário_
157
+
158
+ #### Bem-vindo! :wave:
159
+ """
160
+ )
161
+
162
+ with c2:
163
+ with stylable_container(
164
+ key="graph_entry",
165
+ css_styles="""
166
+ img {
167
+ width: 600px;
168
+ height: 160px;
169
+ overflow: hidden;
170
+ position: relative;
171
+ object-fit: cover;
172
+ mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0));
173
+ -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); /* For Safari */
174
+ }
175
+ """,
176
+ ):
177
+ st.image("./graph1.png")
178
+ st.divider()
179
+
180
+
181
+ ###########################################
182
+ ###########################################
183
+ ###########################################
184
+ ############### LATERAL ##################
185
+ ###########################################
186
+ ###########################################
187
+ ###########################################
188
+
189
+ st.sidebar.markdown(
190
+ """
191
+ ### Informações:
192
+ - Análise de dados do relatório de cadastro individual.
193
+ - Iniciativa - Ubs Flamengo
194
+ - Acesso livre
195
+
196
+ ### Links:
197
+ ##
198
+ ##### - [PEC SUS](https://sape.pecsus.com.br/) 📝
199
+ ###
200
+ ##### - [Obsidian - Dr Guilherme](http://dr-guilhermeapolinario.com) 🌎
201
+ ##### - [GitHub - Dr Guilherme](http://dr-guilhermeapolinario.com) 🌎
202
+
203
+ """
204
+ )
205
+
206
+ ###########################################
207
+ ############### LATERAL ##################
208
+ ###########################################
209
+
210
+ st.markdown(
211
+ """
212
+ ### Intruções:
213
+ ##### - Acesse o site :orange[PEC SUS], na aba esquerda selecione consolidado, :blue[*RELATÓRIO DE CADASTRO INDIVIDUAL*]
214
+ ##### - Selecione a opção :orange[baixar arquivo .csv]. Após baixar o arquivo, selecione o arquivo .csv na aba ao lado, e pronto.
215
+ ##### - Clique no botão de expansão abaixo para iniciar o processo.
216
+ ##### - Utilize o chatbot com a inteligência artificial 🤖 Zé Flamengo para tirar suas dúvidas.
217
+ """
218
+ )
219
+ st.divider()
220
+
221
+
222
+ st.markdown(
223
+ """
224
+ ### :world_map: **Conhecendo a área de abrangencia daUBS Flamengo: (IBGE 2022)**
225
+
226
+ """
227
+ )
228
+
229
+ m_pop = st.checkbox(":man-woman-girl-boy: **População**")
230
+ if m_pop:
231
+ gdf = gpd.read_file("flamengo_ibge2022.geojson").to_crs(epsg=4326)
232
+ LATITUDE = -19.971591804
233
+ LONGITUDE = -44.057912815
234
+ colorscale = px.colors.sequential.swatches_continuous()
235
+ colorscale = px.colors.sequential.Viridis
236
+ colormap = LinearColormap(
237
+ colors=colorscale,
238
+ vmin=gdf["POP"].min(),
239
+ vmax=gdf["POP"].max(),
240
+ caption="População residente UBS Flamengo - IBGE 2022",
241
+ )
242
+
243
+ m = folium.Map(
244
+ location=[LATITUDE, LONGITUDE], tiles="Cartodb Positron", zoom_start=15
245
+ )
246
+
247
+ pop_total = gdf["POP"].sum()
248
+ gdf["POP_PERCENT"] = (gdf["POP"] / pop_total * 100).round(2)
249
+
250
+ folium.GeoJson(
251
+ gdf,
252
+ style_function=lambda feature: {
253
+ "fillColor": colormap(feature["properties"]["POP"]),
254
+ "color": "black",
255
+ "weight": 1,
256
+ "fillOpacity": 0.4,
257
+ },
258
+ highlight_function=lambda feature: {
259
+ "fillColor": "#ffaf00",
260
+ "color": "green",
261
+ "weight": 3,
262
+ "fillOpacity": 0.6,
263
+ },
264
+ tooltip=folium.features.GeoJsonTooltip(
265
+ fields=["CD_SETOR", "POP", "POP_PERCENT"],
266
+ aliases=[
267
+ "Setor Censitário:",
268
+ "População do Setor:",
269
+ "Porcentagem do Total: %",
270
+ ],
271
+ style=(
272
+ "background-color: white; color: #333333; font-family: calibri; font-size: 12px; padding: 10px;"
273
+ ),
274
+ ),
275
+ ).add_to(m)
276
+
277
+ colormap.caption = "População residente UBS Flamengo - IBGE 2022"
278
+ colormap.add_to(m)
279
+ # Alterar o CSS da legenda para reposicioná-la no topo
280
+ STYLE_STATEMENT = "<style>.leaflet-control-layers { position: fixed; top: 10px; left: 50px; } </style>"
281
+ m.get_root().html.add_child(folium.Element(STYLE_STATEMENT))
282
+ folium_static(m)
283
+
284
+ st.divider()
285
+ add_vertical_space(3)
286
+
287
+ st.markdown(
288
+ """
289
+ ## Dados de saúde Relatório de cadastro individual.
290
+ """
291
+ )
292
+
293
+
294
+ # Função para criar gráficos de barras personalizados
295
+ def criar_grafico_personalizado(df, x_col, y_col, titulo):
296
+ """
297
+ Generates a custom bar chart using Plotly Express.
298
+
299
+ Args:
300
+ df (pandas.DataFrame): The input dataframe.
301
+ x_col (str): The column name to be used as the x-axis.
302
+ y_col (str): The column name to be used as the y-axis.
303
+ titulo (str): The title of the chart.
304
+
305
+ Returns:
306
+ plotly.graph_objects.Figure: The generated bar chart.
307
+
308
+ """
309
+ fig = px.bar(
310
+ df,
311
+ x=x_col,
312
+ y=y_col,
313
+ title=titulo,
314
+ text=y_col, # Adiciona os rótulos
315
+ color=x_col, # Usa uma paleta de cores para a barra
316
+ color_discrete_sequence=px.colors.qualitative.Bold # Define uma paleta de cores
317
+ )
318
+ fig.update_traces(texttemplate='%{text:.2s}', textposition='outside') # Formatação dos rótulos
319
+ fig.update_layout(
320
+ uniformtext_minsize=8,
321
+ uniformtext_mode='hide',
322
+ xaxis_title=x_col,
323
+ yaxis_title=y_col,
324
+ title_font_size=24,
325
+ title_font_family="Arial",
326
+ title_font_color="blue",
327
+ title_x=0.5,
328
+ paper_bgcolor='rgba(0,0,0,0)',
329
+ plot_bgcolor='rgba(0,0,0,0)',
330
+ xaxis_tickangle=-45 # Rotaciona os rótulos do eixo x para melhor legibilidade
331
+ )
332
+ return fig
333
+
334
+ with st.expander(" Adicione o arquivo .csv ao lado para visuzalização", expanded=True):
335
+ selected_tab = option_menu(
336
+ menu_title=None,
337
+ options=["Faixa Etária", "Gênero", "Cor", "Deficiência", "Doenças"],
338
+ icons=["person", "gender-female", "person-plus", "person-wheelchair", "capsule-pill"],
339
+ menu_icon="cast",
340
+ default_index=0,
341
+ orientation="horizontal",
342
+ styles={
343
+ "container": {"padding": "5px", "background-color": "#f9f9f9"},
344
+ "icon": {"color": "orange", "font-size": "25px"},
345
+ "nav-link": {
346
+ "font-size": "16px",
347
+ "text-align": "center",
348
+ "margin": "0px",
349
+ "--hover-color": "#eee",
350
+ },
351
+ "nav-link-selected": {"background-color": "#02ab21"},
352
+ },
353
+ )
354
+
355
+ # Exibição dos DataFrames com base na aba selecionada
356
+ if selected_tab == "Faixa Etária" and DF_IDADE is not None:
357
+ st.subheader("Distribuição por Faixa Etária")
358
+
359
+
360
+
361
+ col1, col2 = st.columns(2)
362
+ with col1:
363
+ st.dataframe(DF_IDADE, hide_index=True)
364
+ with col2:
365
+ # Criar o gráfico de pirâmide etária
366
+ # Preparar os dados para o gráfico de pirâmide etária
367
+ DF_IDADE["Masculino"] = (
368
+ DF_IDADE["Masculino"].astype(int) * -1
369
+ ) # Valores negativos para o lado masculino
370
+ DF_IDADE["Feminino"] = DF_IDADE["Feminino"].astype(int)
371
+ fig_idade = px.bar(
372
+ DF_IDADE,
373
+ x=["Masculino", "Feminino"],
374
+ y="Descrição",
375
+ orientation="h",
376
+ title="Pirâmide Etária",
377
+ labels={"value": "População", "Descrição": "Faixa Etária"},
378
+ color="Descrição",
379
+ color_discrete_sequence=px.colors.qualitative.Set3,
380
+ )
381
+ fig_idade.update_layout(
382
+ barmode="relative", xaxis_title="População", yaxis_title="Faixa Etária"
383
+ )
384
+ st.plotly_chart(fig_idade)
385
+ DF_IDADE["Masculino"] = DF_IDADE["Masculino"].abs()
386
+
387
+ elif selected_tab == "Gênero" and DF_GENERO is not None:
388
+ st.subheader("Distribuição por Gênero")
389
+ col1, col2 = st.columns(2)
390
+ with col1:
391
+ st.dataframe(DF_GENERO, hide_index=True)
392
+ with col2:
393
+ fig_genero = px.pie(
394
+ DF_GENERO,
395
+ names="Descrição",
396
+ values="Valor",
397
+ title="Distribuição por Gênero",
398
+ color_discrete_sequence=px.colors.qualitative.Pastel
399
+ )
400
+ st.plotly_chart(fig_genero)
401
+
402
+ elif selected_tab == "Cor" and DF_COR is not None:
403
+ st.subheader("Distribuição por Cor")
404
+ col1, col2 = st.columns(2)
405
+ with col1:
406
+ st.dataframe(DF_COR, hide_index=True)
407
+ with col2:
408
+ fig_cor = px.pie(
409
+ DF_COR,
410
+ names="Descrição",
411
+ values="Valor",
412
+ title="Distribuição por Cor",
413
+ color_discrete_sequence=px.colors.qualitative.Vivid
414
+ )
415
+ st.plotly_chart(fig_cor)
416
+
417
+ elif selected_tab == "Deficiência" and DF_DEFICIENCIA is not None:
418
+ st.subheader("Distribuição por Deficiência")
419
+ col1, col2 = st.columns(2)
420
+ with col1:
421
+ st.dataframe(DF_DEFICIENCIA, hide_index=True)
422
+ with col2:
423
+ fig_deficiencia = criar_grafico_personalizado(
424
+ DF_DEFICIENCIA,
425
+ x_col="Descrição",
426
+ y_col="Valor",
427
+ titulo="Distribuição por Deficiência"
428
+ )
429
+ st.plotly_chart(fig_deficiencia)
430
+
431
+ elif selected_tab == "Doenças" and DF_DOENCAS is not None:
432
+ st.subheader("Distribuição por Doenças")
433
+ col1, col2 = st.columns(2)
434
+ with col1:
435
+ st.dataframe(DF_DOENCAS, hide_index=True)
436
+ with col2:
437
+ fig_doencas = criar_grafico_personalizado(
438
+ DF_DOENCAS,
439
+ x_col="Descrição",
440
+ y_col="Valor",
441
+ titulo="Distribuição por Doenças"
442
+ )
443
+ st.plotly_chart(fig_doencas)
444
+ add_vertical_space()
445
+
446
+ st.info(
447
+ """
448
+ 📢 **CONVERSE COM IA SE TIVER DÚVIDAS NA ANÁLISE DOS GRÁFICOS**"""
449
+ )
450
+
451
+ add_vertical_space(1)
452
+
453
+
454
+ with stylable_container(
455
+ key="brain",
456
+ css_styles="""
457
+ img {
458
+ width: 120px;
459
+ height: 100px;
460
+ overflow: hidden;
461
+ position: relative;
462
+ object-fit: cover;
463
+ border-radius: 14px; /* Adiciona bordas arredondadas */
464
+ }
465
+ """,
466
+ ):
467
+ st.image("./brain.png")
468
+ # Widget expander para interação da IA
469
+ with st.expander(" Converse com o 🤖 Zé Flamengo", expanded=True):
470
+ client = Groq(
471
+ api_key=st.secrets["GROQ_API_KEY"],
472
+ )
473
+
474
+ INPUT_KEY = "USER_CHAT_input"
475
+
476
+ USER_CHAT = st.text_input(
477
+ "Digite sua pergunta sobre saúde na microárea:",
478
+ placeholder="Digite sua pergunta aqui...",
479
+ )
480
+
481
+ if st.button("Enviar pergunta", key="send_button"):
482
+ if USER_CHAT is not None: # Verifica se há texto na entrada
483
+ try:
484
+ RESUMO_DF = gerar_resumo_df()
485
+ CHAT_COMPLETION = client.chat.completions.create(
486
+ messages=[
487
+ {
488
+ "role": "system",
489
+ "content": f"""
490
+ Seu nome é Flávio, assistente virtual de dados médicos epidemiológicos.
491
+ Você é um epidemiologista brasileiro com 20 anos de experiência
492
+ em análise de dados de saúde de microáreas. Sua função é analisar
493
+ a estrutura de saúde de microregiões através de dados coletados
494
+ no SUS. Forneça suas respostas sempre em português, seja conciso e
495
+ evite conversar sobre outros temas, sempre retornando ao tema da
496
+ conversa.
497
+ Aqui estão os resumos dos dataframes disponíveis:
498
+
499
+ {RESUMO_DF}
500
+
501
+ Use essas informações para responder às perguntas do usuário.
502
+ Regras:
503
+ 1- Seja sempre cortês.
504
+ 2- Responda somente assuntos referentes ao resumo.
505
+ 3- Caso seja feita alguma pergunta a você diferente de resumos, responda: "Vamos voltar ao trabalho que interessa?"
506
+ 4- Responda sempre em português.
507
+ 5- Se não souber a resposta, responda: "Desculpe, mas não tennho esta informação."
508
+ 6- Nas suas respostas não forneça os nomes dos df_, somente responda as perguntas.
509
+
510
+ """,
511
+ },
512
+ {
513
+ "role": "user",
514
+ "content": USER_CHAT,
515
+ },
516
+ ],
517
+ model="llama3-70b-8192",
518
+ temperature=0.2,
519
+ max_tokens=1500,
520
+ )
521
+ st.write(CHAT_COMPLETION.choices[0].message.content)
522
+
523
+ # Limpa o campo de entrada após enviar a pergunta
524
+ st.session_state[INPUT_KEY] = ""
525
+ except Exception as e:
526
+ st.error(f"Erro ao gerar a conclusão do chat: {e}")
527
+ else:
528
+ st.warning("Por favor, digite uma pergunta antes de enviar.")
529
+
530
+
531
+ # Adicione este código fora do expander para evitar a reexecução do script ao pressionar Enter
532
+ if "USER_CHAT_input" in st.session_state and st.session_state.USER_CHAT_input:
533
+ st.session_state.USER_CHAT_input = ""
534
+