Spaces:
Running
Running
import folium | |
import geopandas as gpd | |
import plotly.express as px | |
import streamlit as st | |
from branca.colormap import LinearColormap | |
from groq import Groq | |
from streamlit_extras.add_vertical_space import add_vertical_space | |
from streamlit_extras.stylable_container import stylable_container | |
from streamlit_folium import folium_static | |
from streamlit_option_menu import option_menu | |
from data_cleaning import processar_arquivo | |
st.set_page_config( | |
page_title="Dashboard UBS Flamengo", | |
page_icon="📊", | |
layout="wide", | |
initial_sidebar_state="expanded", | |
) | |
# Inicializar variáveis dos DataFrames como None | |
DF_DATA = None | |
DF_HEAD = None | |
DF_IDADE = None | |
DF_GENERO = None | |
DF_COR = None | |
DF_DEFICIENCIA = None | |
DF_DOENCAS = None | |
DF_ESCOLA = None | |
DF_TRANSGEN = None | |
# Upload de arquivo CSV na barra lateral | |
uploaded_file = st.sidebar.file_uploader("Escolha um arquivo CSV", type="csv") | |
if uploaded_file is not None: | |
dataframes = processar_arquivo(uploaded_file) | |
DF_DATA = dataframes.get("Data") | |
DF_HEAD = dataframes.get("Head") | |
DF_IDADE = dataframes.get("Idade") | |
DF_GENERO = dataframes.get("genero") | |
DF_COR = dataframes.get("cor") | |
DF_DEFICIENCIA = dataframes.get("deficiencia") | |
DF_DOENCAS = dataframes.get("doencas") | |
DF_ESCOLA = dataframes.get("Escola") | |
DF_TRANSGEN = dataframes.get("transgen") | |
for df in [DF_IDADE, DF_GENERO, DF_COR, DF_DEFICIENCIA, DF_DOENCAS, DF_ESCOLA, DF_TRANSGEN]: | |
if df is not None: | |
df["Descrição"] = df["Descrição"].str.capitalize() | |
else: | |
st.sidebar.info("Adicione um arquivo .csv.") | |
def gerar_resumo_df(): | |
resumo = "" | |
if DF_DATA is not None: | |
resumo += "Resumo DF_DATA:\n" | |
resumo += DF_DATA.to_string(index=False) + "\n\n" | |
if DF_HEAD is not None: | |
resumo += "Resumo DF_HEAD:\n" | |
resumo += DF_HEAD.to_string(index=False) + "\n\n" | |
if DF_IDADE is not None: | |
resumo += "Resumo DF_IDADE:\n" | |
resumo += DF_IDADE.to_string(index=False) + "\n\n" | |
if DF_GENERO is not None: | |
resumo += f"Resumo DF_GENERO:\n{DF_GENERO.to_string(index=False)}\n\n" | |
if DF_COR is not None: | |
resumo += f"Resumo DF_COR:\n{DF_COR.to_string(index=False)}\n\n" | |
if DF_DEFICIENCIA is not None: | |
resumo += f"Resumo DF_DEFICIENCIA:\n{DF_DEFICIENCIA.to_string(index=False)}\n\n" | |
if DF_DOENCAS is not None: | |
resumo += f"Resumo DF_DOENCAS:\n{DF_DOENCAS.to_string(index=False)}\n\n" | |
if DF_ESCOLA is not None: | |
resumo += f"Resumo DF_ESCOLA:\n{DF_ESCOLA.to_string(index=False)}\n\n" | |
if DF_TRANSGEN is not None: | |
resumo += f"Resumo DF_TRANSGEN:\n{DF_TRANSGEN.to_string(index=False)}\n\n" | |
return resumo | |
# Mostrar o resumo no Streamlit | |
st.text(gerar_resumo_df()) | |
####################################### | |
############ BANNER ################## | |
####################################### | |
with stylable_container( | |
key="banner", | |
css_styles=""" | |
img { | |
width: 1800px; | |
height: 600px; | |
overflow: hidden; | |
position: relative; | |
object-fit: cover; | |
border-radius: 14px; /* Adiciona bordas arredondadas */ | |
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); | |
-webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); /* For Safari */ | |
} | |
""", | |
): | |
st.image("./banner.png") | |
add_vertical_space(5) | |
c1, c2 = st.columns([5, 5]) | |
with c1: | |
st.markdown( | |
""" | |
## Dashboard UBS Flamengo | |
###### :brain: _by Dr. Guilherme Apolinário_ | |
#### Bem-vindo! :wave: | |
""" | |
) | |
with c2: | |
with stylable_container( | |
key="graph_entry", | |
css_styles=""" | |
img { | |
width: 600px; | |
height: 160px; | |
overflow: hidden; | |
position: relative; | |
object-fit: cover; | |
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); /* For Safari */ | |
} | |
""", | |
): | |
st.image("./graph1.png") | |
st.divider() | |
########################################### | |
############### LATERAL ################## | |
########################################### | |
st.sidebar.markdown( | |
""" | |
### Informações: | |
- Análise de dados do relatório de cadastro individual. | |
- Iniciativa - Ubs Flamengo | |
- Acesso livre | |
### Links: | |
## | |
##### - [PEC SUS](https://sape.pecsus.com.br/) 📝 | |
### | |
##### - [Obsidian - Dr Guilherme](http://dr-guilhermeapolinario.com) 🌎 | |
##### - [GitHub - Dr Guilherme](http://dr-guilhermeapolinario.com) 🌎 | |
""" | |
) | |
st.markdown( | |
""" | |
### Intruções: | |
##### - Acesse o site :orange[PEC SUS], na aba esquerda selecione consolidado, :blue[*RELATÓRIO DE CADASTRO INDIVIDUAL*] | |
##### - Selecione a opção :orange[baixar arquivo .csv]. Após baixar o arquivo, selecione o arquivo .csv na aba ao lado, e pronto. | |
##### - Clique no botão de expansão abaixo para iniciar o processo. | |
##### - Utilize o chatbot com a inteligência artificial 🤖 Zé Flamengo para tirar suas dúvidas. | |
""" | |
) | |
st.divider() | |
st.markdown( | |
""" | |
### :world_map: **Conhecendo a área de abrangencia da UBS Flamengo: (IBGE 2022)** | |
""" | |
) | |
m_pop = st.checkbox(":man-woman-girl-boy: **População**") | |
if m_pop: | |
gdf = gpd.read_file("flamengo_ibge2022.geojson").to_crs(epsg=4326) | |
LATITUDE = -19.971591804 | |
LONGITUDE = -44.057912815 | |
colorscale = px.colors.sequential.Viridis | |
colormap = LinearColormap( | |
colors=colorscale, | |
vmin=gdf["POP"].min(), | |
vmax=gdf["POP"].max(), | |
caption="População residente UBS Flamengo - IBGE 2022", | |
) | |
m = folium.Map( | |
location=[LATITUDE, LONGITUDE], tiles="Cartodb Positron", zoom_start=15 | |
) | |
pop_total = gdf["POP"].sum() | |
gdf["POP_PERCENT"] = (gdf["POP"] / pop_total * 100).round(2) | |
folium.GeoJson( | |
gdf, | |
style_function=lambda feature: { | |
"fillColor": colormap(feature["properties"]["POP"]), | |
"color": "black", | |
"weight": 1, | |
"fillOpacity": 0.4, | |
}, | |
highlight_function=lambda feature: { | |
"fillColor": "#ffaf00", | |
"color": "green", | |
"weight": 3, | |
"fillOpacity": 0.6, | |
}, | |
tooltip=folium.features.GeoJsonTooltip( | |
fields=["CD_SETOR", "POP", "POP_PERCENT"], | |
aliases=[ | |
"Setor Censitário:", | |
"População do Setor:", | |
"Porcentagem do Total: %", | |
], | |
style=( | |
"background-color: white; color: #333333; font-family: calibri; font-size: 12px; padding: 10px;" | |
), | |
), | |
).add_to(m) | |
colormap.add_to(m) | |
STYLE_STATEMENT = "<style>.leaflet-control-layers { position: fixed; top: 10px; left: 50px; } </style>" | |
m.get_root().html.add_child(folium.Element(STYLE_STATEMENT)) | |
folium_static(m) | |
st.divider() | |
add_vertical_space(3) | |
st.markdown( | |
""" | |
## Dados de saúde Relatório de cadastro individual. | |
""" | |
) | |
def criar_grafico_personalizado(df, x_col, y_col, titulo): | |
fig = px.bar( | |
df, | |
x=x_col, | |
y=y_col, | |
title=titulo, | |
text=y_col, | |
color=x_col, | |
color_discrete_sequence=px.colors.qualitative.Bold | |
) | |
fig.update_traces(texttemplate='%{text:.2s}', textposition='outside') | |
fig.update_layout( | |
uniformtext_minsize=8, | |
uniformtext_mode='hide', | |
xaxis_title=x_col, | |
yaxis_title=y_col, | |
title_font_size=24, | |
title_font_family="Arial", | |
title_font_color="blue", | |
title_x=0.5, | |
paper_bgcolor='rgba(0,0,0,0)', | |
plot_bgcolor='rgba(0,0,0,0)', | |
xaxis_tickangle=-45 | |
) | |
return fig | |
if uploaded_file is not None: | |
with st.expander(" Adicione o arquivo .csv ao lado para visualização", expanded=True): | |
selected_tab = option_menu( | |
menu_title=None, | |
options=["Faixa Etária", "Gênero", "Cor", "Deficiência", "Doenças", "Escolaridade", "Identidade de Gênero"], | |
icons=["person", "gender-female", "person-plus", "person-wheelchair", "capsule-pill", "school", "transgender"], | |
menu_icon="cast", | |
default_index=0, | |
orientation="horizontal", | |
styles={ | |
"container": {"padding": "5px", "background-color": "#f9f9f9"}, | |
"icon": {"color": "orange", "font-size": "25px"}, | |
"nav-link": { | |
"font-size": "16px", | |
"text-align": "center", | |
"margin": "0px", | |
"--hover-color": "#eee", | |
}, | |
"nav-link-selected": {"background-color": "#02ab21"}, | |
}, | |
) | |
if selected_tab == "Faixa Etária" and DF_IDADE is not None: | |
st.subheader("Distribuição por Faixa Etária") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_IDADE, hide_index=True) | |
with col2: | |
DF_IDADE["Masculino"] = DF_IDADE["Masculino"].astype(int) * -1 | |
DF_IDADE["Feminino"] = DF_IDADE["Feminino"].astype(int) | |
fig_idade = px.bar( | |
DF_IDADE, | |
x=["Masculino", "Feminino"], | |
y="Descrição", | |
orientation="h", | |
title="Pirâmide Etária", | |
labels={"value": "População", "Descrição": "Faixa Etária"}, | |
color="Descrição", | |
color_discrete_sequence=px.colors.qualitative.Set3, | |
) | |
fig_idade.update_layout( | |
barmode="relative", xaxis_title="População", yaxis_title="Faixa Etária" | |
) | |
st.plotly_chart(fig_idade) | |
DF_IDADE["Masculino"] = DF_IDADE["Masculino"].abs() | |
elif selected_tab == "Gênero" and DF_GENERO is not None: | |
st.subheader("Distribuição por Gênero") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_GENERO, hide_index=True) | |
with col2: | |
fig_genero = px.pie( | |
DF_GENERO, | |
names="Descrição", | |
values="Valor", | |
title="Distribuição por Gênero", | |
color_discrete_sequence=px.colors.qualitative.Pastel | |
) | |
st.plotly_chart(fig_genero) | |
elif selected_tab == "Cor" and DF_COR is not None: | |
st.subheader("Distribuição por Cor") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_COR, hide_index=True) | |
with col2: | |
fig_cor = px.pie( | |
DF_COR, | |
names="Descrição", | |
values="Valor", | |
title="Distribuição por Cor", | |
color_discrete_sequence=px.colors.qualitative.Vivid | |
) | |
st.plotly_chart(fig_cor) | |
elif selected_tab == "Deficiência" and DF_DEFICIENCIA is not None: | |
st.subheader("Distribuição por Deficiência") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_DEFICIENCIA, hide_index=True) | |
with col2: | |
fig_deficiencia = criar_grafico_personalizado( | |
DF_DEFICIENCIA, | |
x_col="Descrição", | |
y_col="Valor", | |
titulo="Distribuição por Deficiência" | |
) | |
st.plotly_chart(fig_deficiencia) | |
elif selected_tab == "Doenças" and DF_DOENCAS is not None: | |
st.subheader("Distribuição por Doenças") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_DOENCAS, hide_index=True) | |
with col2: | |
fig_doencas = criar_grafico_personalizado( | |
DF_DOENCAS, | |
x_col="Descrição", | |
y_col="Valor", | |
titulo="Distribuição por Doenças" | |
) | |
st.plotly_chart(fig_doencas) | |
elif selected_tab == "Escolaridade" and DF_ESCOLA is not None: | |
st.subheader("Distribuição por Escolaridade") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_ESCOLA, hide_index=True) | |
with col2: | |
fig_escola = criar_grafico_personalizado( | |
DF_ESCOLA, | |
x_col="Descrição", | |
y_col="Valor", | |
titulo="Distribuição por Escolaridade" | |
) | |
st.plotly_chart(fig_escola) | |
elif selected_tab == "Identidade de Gênero" and DF_TRANSGEN is not None: | |
st.subheader("Distribuição por Identidade de Gênero") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.dataframe(DF_TRANSGEN, hide_index=True) | |
with col2: | |
fig_transgen = criar_grafico_personalizado( | |
DF_TRANSGEN, | |
x_col="Descrição", | |
y_col="Valor", | |
titulo="Distribuição por Identidade de Gênero" | |
) | |
st.plotly_chart(fig_transgen) | |
add_vertical_space() | |
st.info( | |
""" | |
📢 **CONVERSE COM IA SE TIVER DÚVIDAS NA ANÁLISE DOS GRÁFICOS**""" | |
) | |
add_vertical_space(1) | |
with stylable_container( | |
key="brain", | |
css_styles=""" | |
img { | |
width: 120px; | |
height: 100px; | |
overflow: hidden; | |
position: relative; | |
object-fit: cover; | |
border-radius: 14px; /* Adiciona bordas arredondadas */ | |
} | |
""", | |
): | |
st.image("./brain.png") | |
# Widget expander para interação da IA | |
with st.expander(" Converse com o 🤖 Zé Flamengo", expanded=True): | |
client = Groq( | |
api_key=st.secrets["GROQ_API_KEY"], | |
) | |
INPUT_KEY = "USER_CHAT_input" | |
USER_CHAT = st.text_input( | |
"Digite sua pergunta sobre saúde na microárea:", | |
placeholder="Digite sua pergunta aqui...", | |
) | |
if st.button("Enviar pergunta", key="send_button"): | |
if USER_CHAT is not None: # Verifica se há texto na entrada | |
try: | |
RESUMO_DF = gerar_resumo_df() | |
CHAT_COMPLETION = client.chat.completions.create( | |
messages=[ | |
{ | |
"role": "system", | |
"content": f""" | |
Seu nome é Zé Flamengo, você é um assistente virtual que analisa de dados médicos epidemiológicos. Você tem 20 anos de experiência | |
em análise de dados de saúde de microáreas de um psf. | |
Sua função é: | |
1. Analisar dados de uma unidade básica de saúde. | |
2. Os dados que irá inalisar, são dados do Relatório de cadastro individual, advindos do PEC SUS. | |
3. Suas respostas devem ser sempre em português. | |
4- Seja conciso e evite conversar sobre outros temas. | |
5- Sempre retome o tema da conversa. | |
Os dataframes que irá analisar são do Relatório de cadastro indiviual. ele foi transformados em texto aqui disponíveis: | |
{RESUMO_DF} | |
Use essas informações para responder às perguntas do usuário. | |
Regras: | |
1- Seja sempre cortês. | |
2- Responda somente assuntos referentes ao resumo. | |
3- Caso seja feita alguma pergunta a você diferente de resumos, responda: "Vamos voltar ao trabalho que interessa?" | |
4- Responda sempre em português. | |
5- Se não souber a resposta, responda: "Desculpe, mas não tennho esta informação." | |
6- Nas suas respostas não forneça os nomes dos df_, somente responda as perguntas. | |
""", | |
}, | |
{ | |
"role": "user", | |
"content": USER_CHAT, | |
}, | |
], | |
model="llama3-70b-8192", | |
temperature=0.2, | |
max_tokens=1500, | |
) | |
st.write(CHAT_COMPLETION.choices[0].message.content) | |
# Limpa o campo de entrada após enviar a pergunta | |
st.session_state[INPUT_KEY] = "" | |
except Exception as e: | |
st.error(f"Erro ao gerar a conclusão do chat: {e}") | |
else: | |
st.warning("Por favor, digite uma pergunta antes de enviar.") | |
# Adicione este código fora do expander para evitar a reexecução do script ao pressionar Enter | |
if "USER_CHAT_input" in st.session_state and st.session_state.USER_CHAT_input: | |
st.session_state.USER_CHAT_input = "" |