""" LIMPEZA E VISUALIZAÇÃO DE CSV Returns: DATAFRAMES """ 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 criar_dataframe, iniciar, limpa_rci1, limpa_rci2, separa_grupos, renomear_escola, limpar_dfs, 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) # Atribuir os DataFrames a variáveis específicas para uso posterior 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") # Aplicar capitalização à coluna "Descrição" em cada DataFrame 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(): """ Generates a summary of the dataframes. This function iterates over the local and global variables to check if any of them are named "DF_DATA", "DF_HEAD", "DF_IDADE", "DF_GENERO", "DF_COR", "DF_DEFICIENCIA", "DF_DOENCAS", "DF_ESCOLA", or "DF_TRANSGEN". If a dataframe is found, it generates a summary of the dataframe by printing its column names and the count of each unique value in the "Descrição" column. Returns: str: A string containing the summary of the dataframes. """ resumo = "" if "DF_HEAD" in locals() or "DF_HEAD" in globals(): resumo += "Resumo DF_HEAD:\n" resumo += DF_HEAD.to_string(index=False) + "\n\n" if "DF_IDADE" in locals() or "DF_IDADE" in globals(): resumo += "Resumo DF_IDADE:\n" resumo += DF_IDADE.to_string(index=False) + "\n\n" if "DF_GENERO" in locals() or "DF_GENERO" in globals(): resumo += f"Resumo DF_GENERO:\n{DF_GENERO.value_counts().to_string()}\n\n" if "DF_COR" in locals() or "DF_COR" in globals(): resumo += f"Resumo DF_COR:\n{DF_COR.value_counts().to_string()}\n\n" if "DF_DEFICIENCIA" in locals() or "DF_DEFICIENCIA" in globals(): resumo += f"Resumo DF_DEFICIENCIA:\n{DF_DEFICIENCIA.value_counts().to_string()}\n\n" if "DF_DOENCAS" in locals() or "DF_DOENCAS" in globals(): resumo += f"Resumo DF_DOENCAS:\n{DF_DOENCAS.value_counts().to_string()}\n\n" if "DF_ESCOLA" in locals() or "DF_ESCOLA" in globals(): resumo += f"Resumo DF_ESCOLA:\n{DF_ESCOLA.value_counts().to_string()}\n\n" if "DF_TRANSGEN" in locals() or "DF_TRANSGEN" in globals(): resumo += f"Resumo DF_TRANSGEN:\n{DF_TRANSGEN.value_counts().to_string()}\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") ####################################### ############ BANNER ################## ####################################### 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) 🌎 """ ) ########################################### ############### LATERAL ################## ########################################### 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 daUBS 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.swatches_continuous() 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.caption = "População residente UBS Flamengo - IBGE 2022" colormap.add_to(m) # Alterar o CSS da legenda para reposicioná-la no topo STYLE_STATEMENT = "" 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. """ ) if uploaded_file is not None: dataframes = processar_arquivo(uploaded_file) # Atribuir os DataFrames a variáveis específicas para uso posterior 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") # Aplicar capitalização à coluna "Descrição" em cada DataFrame 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 criar_grafico_personalizado(df, x_col, y_col, titulo): """ Generates a custom bar chart using Plotly Express. Args: df (pandas.DataFrame): The input dataframe. x_col (str): The column name to be used as the x-axis. y_col (str): The column name to be used as the y-axis. titulo (str): The title of the chart. Returns: plotly.graph_objects.Figure: The generated bar chart. """ fig = px.bar( df, x=x_col, y=y_col, title=titulo, text=y_col, # Adiciona os rótulos color=x_col, # Usa uma paleta de cores para a barra color_discrete_sequence=px.colors.qualitative.Bold # Define uma paleta de cores ) fig.update_traces(texttemplate='%{text:.2s}', textposition='outside') # Formatação dos rótulos 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 # Rotaciona os rótulos do eixo x para melhor legibilidade ) return fig 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"}, }, ) # Exibição dos DataFrames com base na aba selecionada 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: # Criar o gráfico de pirâmide etária DF_IDADE["Masculino"] = DF_IDADE["Masculino"].astype(int) * -1 # Valores negativos para o lado masculino 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 é Flávio, assistente virtual de dados médicos epidemiológicos. Você é um epidemiologista brasileiro com 20 anos de experiência em análise de dados de saúde de microáreas. Sua função é analisar a estrutura de saúde de microregiões através de dados coletados no SUS. Forneça suas respostas sempre em português, seja conciso e evite conversar sobre outros temas, sempre retornando ao tema da conversa. Aqui estão os resumos dos dataframes 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 = ""