import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
from typing import Dict, Optional, List
# Definindo as 5 categorias CIF fixas e a ordem desejada
FIXED_ICF_COMPONENT_LABELS: List[str] = [
'Funções Corporais (b)',
'Atividades e Participação (d)',
'Ambiente (e)',
'Estruturas Corporais (s)',
'Outros'
]
# Mapeamento fixo de cores para cada categoria
ICF_COMPONENT_COLOR_MAP: Dict[str, str] = {
'Funções Corporais (b)': '#FFC145', # Amarelo/Laranja
'Atividades e Participação (d)': '#B7F242', # Verde claro
'Ambiente (e)': '#4369C0', # Azul
'Estruturas Corporais (s)': '#DA3B95', # Rosa/Roxo
'Outros': '#AAAAAA' # Cinza
}
def create_pie_chart(
input_df: pd.DataFrame,
title: str = "Distribuição da Classificação"
) -> Optional[go.Figure]:
"""
Generates a pie chart from a DataFrame, using consistent colors for ICF categories
present in the input data.
Args:
input_df (pd.DataFrame): DataFrame with 'Componente CIF' (labels)
and 'Frequencia' (values) columns.
title (str): The title of the chart.
Returns:
Optional[go.Figure]: The Plotly Figure object containing the pie chart,
or None if there is no valid data to plot (e.g., all frequencies are zero or negative).
"""
# Verificar se o DataFrame de entrada está vazio
# Check if the input DataFrame is empty
if input_df.empty:
print(f"Aviso: DataFrame de entrada para o gráfico de pizza '{title}' está vazio. Retornando None.")
return None
# Filtrar categorias com frequência zero ou negativa, pois não aparecem no gráfico de pizza
# Filter out categories with zero or negative frequency, as they don't appear in a pie chart
plot_df = input_df[input_df['Frequencia'] > 0].copy()
# Verificar se há dados válidos para plotar após a filtragem
# Check if there's any valid data to plot after filtering
if plot_df.empty:
print(f"Aviso: Nenhum dado com frequência positiva para gerar o gráfico de pizza: '{title}'. Retornando None.")
return None
# Garantir a ordem das categorias no gráfico e na legenda para as categorias presentes
# Ensure the order of categories in the chart and legend for present categories
category_order_map = {'Componente CIF': FIXED_ICF_COMPONENT_LABELS}
figure = px.pie(
plot_df,
names='Componente CIF',
values='Frequencia',
title=title,
color='Componente CIF',
color_discrete_map=ICF_COMPONENT_COLOR_MAP, # Usar o mapeamento fixo de cores
category_orders=category_order_map # Forçar a ordem para categorias presentes
)
figure.update_layout(legend_title_text='Componentes')
figure.update_traces(
direction='clockwise',
rotation=-30, # Rotação inicial das fatias
textinfo="label+value+percent",
textposition='outside',
textfont_size=16,
pull=0.05, # Destaca ligeiramente as fatias
hovertemplate="%{label}
Frequência: %{value}
Porcentagem: %{percent}"
)
return figure
def create_bar_chart(
input_df: pd.DataFrame,
title: str = "Frequência da Classificação"
) -> Optional[go.Figure]:
"""
Generates a bar chart from a DataFrame, using consistent colors for ICF categories
present in the input data.
Args:
input_df (pd.DataFrame): DataFrame with 'Componente CIF' (X-axis)
and 'Frequencia' (Y-axis) columns.
title (str): The title of the chart.
Returns:
Optional[go.Figure]: The Plotly Figure object containing the bar chart,
or None if there is no valid data to plot (e.g., all frequencies are zero).
"""
# Verificar se o DataFrame de entrada está vazio
# Check if the input DataFrame is empty
if input_df.empty:
print(f"Aviso: DataFrame de entrada para o gráfico de barras '{title}' está vazio. Retornando None.")
return None
# Verificar se todas as frequências são zero (ou menores)
# Check if all frequencies are zero (or less)
# Embora frequências devam ser não-negativas, somar pode ser problemático se houver NaNs.
# A simple check for non-positive sum is robust if data is clean.
# If 'Frequencia' can have NaN, they should be handled (e.g., fillna(0) or dropna())
# For simplicity, assuming 'Frequencia' is numeric and NaNs are not the primary concern here.
# A more robust check for "no positive data" might be (input_df['Frequencia'] <= 0).all()
# but sum() == 0 is what was implicitly checked before with merged_df.
if input_df['Frequencia'].sum() == 0: # Assuming frequencies are non-negative
print(f"Aviso: Todos os dados em input_df têm frequência 0 para o gráfico de barras: '{title}'. Retornando None.")
return None
# Garantir a ordem das categorias no gráfico para as categorias presentes
# Ensure the order of categories in the chart for present categories
category_order_map = {'Componente CIF': FIXED_ICF_COMPONENT_LABELS}
figure = px.bar(
input_df, # Usar o DataFrame de entrada diretamente
x='Componente CIF',
y='Frequencia',
title=title,
labels={'Componente CIF': 'Componentes CIF', 'Frequencia': 'Frequência'},
color='Componente CIF',
color_discrete_map=ICF_COMPONENT_COLOR_MAP, # Usar o mapeamento fixo de cores
category_orders=category_order_map, # Forçar a ordem para categorias presentes
text_auto=True # Exibe o valor da frequência em cima da barra automaticamente
)
figure.update_layout(
legend_title_text='Componentes',
xaxis_title="Componentes CIF",
yaxis_title="Frequência",
showlegend=True # Garante que a legenda seja mostrada
)
figure.update_traces(
textfont_size=14,
textangle=0,
textposition="inside", # Posição do texto da frequência (pode ser 'inside' ou 'outside')
hovertemplate="%{x}
Frequência: %{y}"
)
return figure
def create_tree_map_chart(
tree_map_df: pd.DataFrame,
title: str = "Treemap de Frequências por Hierarquia de Códigos"
) -> Optional[go.Figure]:
"""
Generates a Treemap chart from a DataFrame.
Args:
tree_map_df (pd.DataFrame): DataFrame with 'Parent', 'Subparent',
'Filho' (Child), and 'Frequencia' (Frequency) columns.
title (str): The title of the chart.
Returns:
Optional[go.Figure]: The Plotly Figure object containing the Treemap chart,
or None if the DataFrame is empty.
"""
if tree_map_df.empty:
print(f"Aviso: DataFrame vazio para gerar o Treemap: '{title}'.")
return None
figure = px.treemap(
tree_map_df,
path=['Parent', 'Subparent', 'Filho'], # Define a hierarquia
values='Frequencia', # Define os valores que determinam o tamanho dos retângulos
title=title,
color='Frequencia', # Colore os retângulos com base na frequência
color_continuous_scale='spectral_r', # Esquema de cores
height=700, # Altura do gráfico
)
figure.update_traces(
textinfo="label+value+percent entry", # Informações exibidas em cada retângulo
hovertemplate='%{label}
Frequência: %{value}
Porcentagem: %{percentEntry:.1%}',
)
return figure