Spaces:
Running
Running
""" | |
This module contains the code for the 'rci' view of the application. | |
""" | |
import plotly.express as px | |
import streamlit as st | |
from groq import Groq | |
from streamlit_extras.add_vertical_space import add_vertical_space | |
from streamlit_extras.stylable_container import stylable_container | |
from streamlit_option_menu import option_menu | |
from data_cleaning import processar_arquivo | |
################################# | |
############ BANNER ############# | |
################################# | |
with stylable_container( | |
key="banner", | |
css_styles=""" | |
img { | |
width: 1800px; | |
height: 400px; | |
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("25.jpg") | |
st.title("Análise de microárea") | |
################################# | |
############ BANNER ############# | |
################################# | |
def clear_chat_history(): | |
""" | |
Clears the chat history and resets the initial analysis in the session state. | |
This function clears the chat history and resets the initial analysis in the session state. | |
It sets the value of "groq_chat_history" in the session state to an empty list and the value of "initial_analysis" to an empty string. | |
""" | |
st.session_state["groq_chat_history"] = [] | |
st.session_state["initial_analysis"] = "" | |
################################# | |
############ DATA ############### | |
################################# | |
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.") | |
if st.sidebar.button("Limpar", type="primary"): | |
clear_chat_history() | |
def gerar_resumo_df(): | |
""" | |
Generates a summary of the dataframes. | |
This function creates a summary of the dataframes by concatenating the | |
string representation of each dataframe. | |
The summary includes the name of the dataframe and its content. | |
Returns: | |
str: The summary of the dataframes. | |
""" | |
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 | |
################################# | |
############ DATA ############### | |
################################# | |
########################################### | |
############### LATERAL ################## | |
########################################### | |
st.sidebar.markdown( | |
""" | |
### Informações: | |
- Análise - 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( | |
""" | |
#### Instruçõ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.write("-----") | |
st.markdown( | |
""" | |
#### Dados de saúde Relatório de cadastro individual. | |
""" | |
) | |
########################################### | |
############### LATERAL ################## | |
########################################### | |
########################################### | |
############### EXPANDERCOM GRÁFICOS ###### | |
########################################### | |
def criar_grafico_personalizado(df, x_col, y_col, titulo): | |
""" | |
A function to create a customized bar chart based on the input data, x | |
and y columns, and title. | |
Parameters: | |
df (DataFrame): The input DataFrame containing the data. | |
x_col (str): The column name for the x-axis data. | |
y_col (str): The column name for the y-axis data. | |
titulo (str): The title of the chart. | |
Returns: | |
fig: The customized bar chart figure. | |
""" | |
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="white", | |
title_x=0.2, | |
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: | |
c1, c2 = st.columns(2) | |
with c1: | |
if DF_DATA is not None: | |
st.dataframe(DF_DATA, hide_index=True) | |
else: | |
st.write("Data não disponível") | |
with c2: | |
if DF_HEAD is not None: | |
st.dataframe(DF_HEAD, hide_index=True) | |
else: | |
st.write("Informações não disponíveis") | |
if uploaded_file is not None: | |
with st.expander("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", | |
"stars", | |
"gender-trans", | |
], | |
menu_icon="cast", | |
default_index=0, | |
orientation="horizontal", | |
styles={ | |
"container": { | |
"padding": "0!important", | |
"background-color": "#262730", | |
}, | |
"icon": {"color": "#4FCBFC", "font-size": "18px"}, | |
"nav-link": { | |
"font-size": "14px", | |
"text-align": "center", | |
"margin": "0px", | |
"padding": "10px", | |
"--hover-color": "#363940", | |
"color": "#FFFFFF", | |
}, | |
"nav-link-selected": {"background-color": "#0083B8"}, | |
"separator": {"border-color": "#4B4B4B"}, | |
}, | |
) | |
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) | |
########################################### | |
############### EXPANDERCOM GRÁFICOS ###### | |
########################################### | |
add_vertical_space(5) | |
########################################### | |
############### FOTO DO BOT ############### | |
########################################### | |
with stylable_container( | |
key="bot", | |
css_styles=""" | |
img { | |
width: 120px; | |
height: 100px; | |
overflow: hidden; | |
position: relative; | |
object-fit: cover; | |
border-radius: 14px; /* Adiciona bordas arredondadas */ | |
} | |
""", | |
): | |
st.image("b.png") | |
########################################### | |
############### FOTO DO BOT ############### | |
########################################### | |
########################################### | |
############### CHATBOT RCI ############### | |
########################################### | |
# Configuração inicial do Groq client | |
client = Groq(api_key=st.secrets["GROQ_API_KEY"]) | |
# Função para gerar a análise inicial | |
def generate_initial_analysis(resumo_rci): | |
""" | |
Generates an initial analysis report based on the provided resumo_rci. | |
Parameters: | |
resumo_rci (str): The resumo_rci to be analyzed. | |
Returns: | |
str: The generated initial analysis report. | |
Raises: | |
Exception: If there is an error generating the initial analysis. | |
Examples: | |
>>> generate_initial_analysis("Resumo do RCI") | |
"Cabeçalho com informações gerais (data, cidadãos ativos, saída, mudança de território, | |
óbito)\n\nAnálise de Faixa Etária (Crianças de 0 a 2 anos, 0 a 4 anos, mulheres na faixa etária | |
de preventivo 25 a 64 anos, mulehres na faixa etária de mamomagrafia 50a 69 anos)\n\nComparação | |
de Sexo\n\nAnálise de Raça / Cor\n\nAnálise de | |
Escolaridade\n\nOrientação Sexual\n\nDeficiências\n\nSituações de Saúde Gerais\n\n\nFormate seu | |
relatório usando negrito para títulos de seções e subtítulos. Use listas com marcadores | |
quando apropriado para melhorar a legibilidade. Apresente seu relatório final dentro de tags | |
<relatorio></relatorio>." | |
""" | |
try: | |
initial_analysis = client.chat.completions.create( | |
messages=[ | |
{ | |
"role": "system", | |
"content": "Você é um assistente de análise de dados de saúde. Sua tarefa é analisar os dados fornecidos e criar um relatório detalhado seguindo o modelo especificado. O relatório deve ser escrito em português.", | |
}, | |
{ | |
"role": "user", | |
"content": f""" | |
Aqui estão os dados para análise: | |
<resumo_rci> | |
{resumo_rci} | |
</resumo_rci> | |
Analise cuidadosamente os dados fornecidos e crie um relatório | |
seguindo o modelo apresentado. O relatório deve incluir as seguintes | |
seções: | |
1. Cabeçalho com informações gerais (data, cidadãos ativos, saída, | |
mudança de território, óbito) | |
2. Análise de Faixa Etária (Crianças de 0 a 2 anos, 0 a 4 anos, mulheres | |
na faixa etária de preventivo 25 a 64 anos, mulehres na faixa etária de | |
mamomagrafia 50a 69 anos) | |
3. Comparação de Sexo | |
4. Análise de Raça / Cor | |
5. Análise de Escolaridade | |
6. Orientação Sexual | |
7. Deficiências | |
8. Situações de Saúde Gerais | |
Para cada seção: | |
- Calcule os totais e percentuais relevantes | |
- Faça comparações quando apropriado (por exemplo, entre masculino | |
e feminino) | |
- Destaque as 3 informações mais significativas | |
Formate seu relatório usando negrito para títulos de seções e subtítulos. | |
Use o título de Análise parcial, use listas com marcadores quando apropriado | |
para melhorar a legibilidade. | |
""", | |
}, | |
], | |
model="llama3-70b-8192", | |
temperature=0.2, | |
max_tokens=1500, | |
) | |
return initial_analysis.choices[0].message.content | |
except Exception as e: | |
st.error(f"Erro ao gerar a análise inicial: {e}") | |
return "Não foi possível gerar a análise inicial." | |
# Carregar arquivo | |
# uploaded_file = st.file_uploader("Escolha um arquivo CSV", type="csv") | |
if uploaded_file is not None: | |
if ( | |
"current_file" not in st.session_state | |
or st.session_state.current_file != uploaded_file.name | |
): | |
st.session_state.current_file = uploaded_file.name | |
st.session_state.initial_analysis = None # Reset da análise anterior | |
st.session_state.resumo_rci = None # Reset do resumo | |
# Botão para gerar o resumo | |
if st.button("Gerar Resumo"): | |
with st.spinner("Gerando resumo..."): | |
st.session_state.resumo_rci = ( | |
gerar_resumo_df() | |
) # Gera o resumo do DataFrame | |
st.session_state.initial_analysis = generate_initial_analysis( | |
st.session_state.resumo_rci | |
) | |
st.success("Resumo gerado com sucesso!") | |
col1, col2 = st.columns(2) | |
with col1: | |
with st.expander("Resumo Inicial", expanded=True): | |
if "initial_analysis" in st.session_state: | |
st.write("**Panorama Geral dos Dados:**") | |
st.text_area( | |
"Análise Inicial", | |
value=st.session_state.initial_analysis, | |
height=300, | |
disabled=False, | |
) | |
else: | |
st.write("**Clique em 'Gerar Resumo' para analisar os dados.**") | |
with col2: | |
with st.expander("Converse com o 🤖 Zé Flamengo", expanded=True): | |
# Inicialização do histórico do chat | |
if "groq_chat_history" not in st.session_state: | |
st.session_state.groq_chat_history = [] | |
# Exibição do histórico do chat | |
for message in st.session_state.groq_chat_history: | |
with st.chat_message(message["role"]): | |
st.markdown(message["content"]) | |
# Input do usuário e processamento da resposta | |
if user_message := st.chat_input( | |
"Digite sua pergunta sobre saúde na microárea:" | |
): | |
# Adiciona a mensagem do usuário ao histórico | |
st.session_state.groq_chat_history.append( | |
{"role": "user", "content": user_message} | |
) | |
try: | |
# Usar o resumo já gerado | |
if "resumo_rci" not in st.session_state: | |
st.warning("Por favor, gere o resumo antes de fazer perguntas.") | |
else: | |
resumo_rci = st.session_state.resumo_rci | |
# Preparação do contexto para a API | |
context = f""" | |
Seu nome é Zé Flamengo, você é um assistente virtual especializado em análise 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 (Programa Saúde da Família). | |
Sua função é: | |
1. Analisar dados de uma unidade básica de saúde. | |
2. Os dados que irá analisar são provenientes 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. | |
6. Realize sempre os cálculos novamente para garantir que os resultados fornecidos sejam precisos e atualizados. | |
Os dataframes que irá analisar são do Relatório de cadastro indiviual. | |
ele foi transformados em texto aqui disponíveis: | |
{resumo_rci} | |
As faixas etárias recomendadas para exames preventivos são: | |
- **Papanicolau (Preventivo):** Mulheres entre 25 e 64 anos, com frequência anual nos primeiros dois exames | |
consecutivos com resultados normais, depois a cada três anos. | |
- **Mamografia:** Mulheres entre 50 e 69 anos, com frequência bienal. | |
Informações sobre o Relatório de Cadastro Individual: | |
- **Objetivo:** Coletar dados sociodemográficos e de saúde dos indivíduos cadastrados em uma unidade básica de saúde. | |
- **Principais Indicadores:** Idade, sexo, condições de saúde (doenças crônicas, gestantes, etc.), status de | |
vacinação, hábitos de vida (tabagismo, alcoolismo, atividade física), entre outros. | |
Exemplos de perguntas esperadas: | |
- Qual a porcentagem de mulheres na faixa etária de preventivo? | |
- Qual a porcentagem de mulheres na faixa etária de mamografia? | |
- Quantas mulheres estão nas faixas etárias de preventivo e mamografia? | |
- Qual a relação masculino/feminino? | |
- Qual a porcentagem das doenças em relação à população total? | |
- 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 tenho esta informação." | |
6. Nas suas respostas, não forneça os nomes dos dataframes, apenas responda às perguntas. | |
7. Destaque os principais achados e tendências nos dados sempre que possível. | |
8. Se aplicável, sugira possíveis ações ou recomendações baseadas nos dados analisados. | |
""" | |
# Chamada à API Groq | |
chat_rci = client.chat.completions.create( | |
messages=[ | |
{"role": "system", "content": context}, | |
*st.session_state.groq_chat_history, | |
], | |
model="llama3-70b-8192", | |
temperature=0.3, | |
max_tokens=2500, | |
) | |
# Processamento da resposta | |
if chat_rci.choices and len(chat_rci.choices) > 0: | |
response_message = chat_rci.choices[0].message.content | |
else: | |
response_message = ( | |
"Desculpe, não foi possível gerar uma resposta." | |
) | |
# Adição da resposta ao histórico | |
st.session_state.groq_chat_history.append( | |
{"role": "assistant", "content": response_message} | |
) | |
# Exibição da resposta | |
with st.chat_message("assistant"): | |
st.markdown(response_message) | |
except Exception as e: | |
st.error(f"Erro ao gerar a resposta: {e}") | |
# Botão para limpar o histórico do chat | |
if st.button("Limpar histórico do chat"): | |
st.session_state.groq_chat_history = [] | |
st.rerun() | |