File size: 7,814 Bytes
cc0607e 33c855c cc0607e a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a 33c855c a6beb3a cc0607e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# utils/report/graph_creation.py
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
from typing import Optional, Tuple # Dict e List não são mais necessários para as constantes globais
# Importa a Enum para centralizar as definições de categoria, rótulos e cores
from .icf_categories import ICFComponent
# Obtém as informações de ordenação e cores diretamente da Enum ICFComponent
ORDERED_ICF_LABELS = ICFComponent.get_ordered_labels()
ICF_COLOR_MAP_FROM_LABEL = ICFComponent.get_color_map() # Mapeia Rótulo Completo -> Cor
ICF_COLOR_MAP_FROM_SHORT_CODE = { # Mapeia Código Curto -> Cor (para o Treemap)
member.short_code: member.color for member in ICFComponent
}
def create_pie_chart(
input_df: pd.DataFrame,
title: str = "Distribuição da Classificação"
) -> Optional[go.Figure]:
"""
Gera um gráfico de pizza a partir de um DataFrame, usando cores consistentes
para as categorias CIF presentes nos dados de entrada.
Args:
input_df (pd.DataFrame): DataFrame com colunas 'Componente CIF' (rótulos)
e 'Frequencia' (valores).
Espera-se que 'Componente CIF' já esteja como
pd.Categorical ordenado.
title (str): O título do gráfico.
Returns:
Optional[go.Figure]: Objeto Plotly Figure ou None se não houver dados válidos.
"""
if input_df.empty:
print(f"Aviso: DataFrame de entrada para o gráfico de pizza '{title}' está vazio. Retornando None.")
return None
plot_df = input_df[input_df['Frequência'] > 0].copy()
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
category_order_map = {'Componente CIF': ORDERED_ICF_LABELS}
figure = px.pie(
plot_df,
names='Componente CIF',
# values='Frequencia', # ANTES
values='Frequência', # DEPOIS
title=title,
color='Componente CIF',
color_discrete_map=ICF_COLOR_MAP_FROM_LABEL,
category_orders=category_order_map
)
figure.update_layout(legend_title_text='Componentes')
figure.update_traces(
direction='clockwise',
rotation=-30,
textinfo="label+value+percent",
textposition='outside',
textfont_size=16,
pull=[0.05 if val > 0 else 0 for val in plot_df['Frequência']], # DEPOIS
hovertemplate="<b>%{label}</b><br>Frequência: %{value}<br>Porcentagem: %{percent}<extra></extra>"
)
return figure
def create_bar_chart(
input_df: pd.DataFrame,
title: str = "Frequência da Classificação"
) -> Optional[go.Figure]:
"""
Gera um gráfico de barras a partir de um DataFrame, usando cores consistentes
para as categorias CIF.
Args:
input_df (pd.DataFrame): DataFrame com colunas 'Componente CIF' (eixo X)
e 'Frequencia' (eixo Y).
Espera-se que 'Componente CIF' já esteja como
pd.Categorical ordenado.
title (str): O título do gráfico.
Returns:
Optional[go.Figure]: Objeto Plotly Figure ou None se não houver dados válidos.
"""
if input_df.empty:
print(f"Aviso: DataFrame de entrada para o gráfico de barras '{title}' está vazio. Retornando None.")
return None
# Para gráficos de barra, podemos plotar frequências zero, então não filtramos > 0
if not input_df['Frequência'].any(): # DEPOIS
print(f"Aviso: Todos os dados em input_df têm frequência 0 para o gráfico de barras: '{title}'.")
category_order_map = {'Componente CIF': ORDERED_ICF_LABELS}
figure = px.bar(
input_df,
x='Componente CIF',
y='Frequência', # DEPOIS
title=title,
labels={'Componente CIF': 'Componentes CIF', 'Frequência': 'Frequência'}, # DEPOIS (chave 'Frequência')
color='Componente CIF',
color_discrete_map=ICF_COLOR_MAP_FROM_LABEL,
category_orders=category_order_map,
text_auto=True
)
figure.update_layout(
legend_title_text='Componentes',
xaxis_title="Componentes CIF",
yaxis_title="Frequência",
showlegend=True
)
figure.update_traces(
textfont_size=14,
textangle=0,
textposition="inside",
hovertemplate="<b>%{x}</b><br>Frequência: %{y}<extra></extra>"
)
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]:
"""
Gera um gráfico Treemap a partir de um DataFrame hierárquico.
As cores do nível 'Parent' são baseadas nos códigos curtos dos componentes CIF.
Args:
tree_map_df (pd.DataFrame): DataFrame com colunas 'Parent', 'Subparent',
'Filho' (Rótulo), e 'Frequencia'.
title (str): O título do gráfico.
Returns:
Optional[go.Figure]: Objeto Plotly Figure ou None se o DataFrame estiver vazio.
"""
if tree_map_df.empty:
print(f"Aviso: DataFrame vazio para gerar o Treemap: '{title}'.")
return None
# Filtra linhas onde 'Filho' é nulo ou vazio, pois podem causar problemas no treemap
# e geralmente representam nós estruturais que não devem ser folhas.
plot_df = tree_map_df.dropna(subset=['Código'])
plot_df = plot_df[plot_df['Código'] != ""]
if plot_df.empty:
print(f"Aviso: DataFrame para Treemap não contém 'Códigos' válidos após filtragem: '{title}'.") # DEPOIS
return None
figure = px.treemap(
plot_df,
path=['Componente', 'Capítulo', 'Código'],
values='Frequência',
title=title,
color='Componente',
color_discrete_map=ICF_COLOR_MAP_FROM_SHORT_CODE,
height=700
)
figure.update_traces(
textinfo="label+value+percent entry", # Informações exibidas
# Para hovertemplate, %{customdata} pode ser usado se você adicionar colunas extras
# Exemplo: customdata=plot_df[['Parent', 'Subparent']]
# hovertemplate='<b>%{label}</b> (%{customdata[0]} > %{customdata[1]})<br>Frequência: %{value}<br>Porcentagem da Entrada: %{percentEntry:.1%}<extra></extra>',
hovertemplate='<b>%{label}</b><br>Frequência: %{value}<br>Porcentagem da Entrada: %{percentEntry:.1%}<extra></extra>',
marker_line_width=1,
marker_line_color='white' # Linhas brancas para separar os blocos
)
figure.update_layout(
hoverlabel=dict(
bgcolor="white",
font_size=12,
)
)
return figure
def create_report_plots(df_group: pd.DataFrame, df_individual_treemap: pd.DataFrame) -> Tuple[go.Figure, go.Figure, go.Figure]:
"""
Cria as figuras Plotly dos gráficos a partir dos DataFrames processados.
Args:
df_group (pd.DataFrame): DataFrame de frequência por grupo CIF.
df_individual_treemap (pd.DataFrame): DataFrame para o treemap de códigos individuais.
(Esperado ter colunas: 'Filho', 'Parent', 'Frequencia')
Returns:
Tuple[go.Figure, go.Figure, go.Figure]: Figuras de pizza, barras e treemap.
"""
print("Gerando gráficos...")
fig_pie = create_pie_chart(df_group, title="Distribuição da Classificação por Componentes CIF")
fig_bar = create_bar_chart(df_group, title="Frequência da Classificação por Componentes CIF")
fig_tree_map = create_tree_map_chart(df_individual_treemap, title="Treemap de Frequência por Código CIF")
return fig_pie, fig_bar, fig_tree_map |