emanoelopes commited on
Commit
e0a53d2
·
1 Parent(s): 706d01e

Update UCI and OULAD metrics calculations to be dynamic, enhancing the educational dashboard with real-time data insights. Refactor visualizations to reflect actual dataset values, improving user experience and accuracy in performance metrics. Add error handling for data loading processes.

Browse files
uci.pkl CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d1a10bb28929402a6f7f24f23f2ed2ccdf8031345a43410c2a021b8460662507
3
- size 3177017
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ff57645cb7ed1d00c72be46f40dd51cd4f7beeef976675b7aa2254cf1d6e3b61
3
+ size 3176814
webapp/home_1.py CHANGED
@@ -109,15 +109,25 @@ with tab3:
109
  st.pyplot(fig_comparativo)
110
  plt.clf()
111
 
112
- # Resumo comparativo
113
  st.markdown("### 📈 Resumo Comparativo")
114
- st.markdown("""
 
 
 
 
 
 
 
 
 
115
  **Principais Diferenças:**
116
  - **Modalidade**: UCI (presencial) vs OULAD (online)
117
- - **Taxa de Aprovação**: OULAD (78.5%) supera UCI (67.3%)
118
- - **Demografia**: UCI tem mais mulheres (58.2%), OULAD tem mais homens (56.2%)
119
- - **Faixa Etária**: UCI (15-19 anos) vs OULAD (35-55 anos)
120
- - **Engajamento**: OULAD permite medir cliques e atividades online
 
121
  """)
122
 
123
  with tab4:
@@ -166,31 +176,43 @@ col1, col2 = st.columns(2)
166
 
167
  with col1:
168
  st.markdown("### 📚 Para Escolas Públicas (UCI)")
169
- st.markdown("""
 
 
 
 
170
  **Pontos de Atenção:**
171
- - Focar em redução de faltas (correlação negativa com desempenho)
172
- - Incentivar tempo de estudo adequado (5-10h/semana)
173
- - Apoiar estudantes com baixo consumo de álcool
174
  - Considerar influência da escolaridade dos pais
 
175
 
176
  **Recomendações:**
177
  - Programas de acompanhamento para estudantes com muitas faltas
178
  - Workshops sobre gestão de tempo de estudo
179
  - Envolvimento das famílias no processo educacional
 
180
  """)
181
 
182
  with col2:
183
  st.markdown("### 🌐 Para Plataformas Online (OULAD)")
184
- st.markdown("""
 
 
 
 
185
  **Pontos Fortes:**
186
- - Alta taxa de aprovação (78.5%)
187
- - Boa distribuição de atividades
188
- - Engajamento moderado mas efetivo
 
189
 
190
  **Recomendações:**
191
- - Aumentar atividades do tipo 'outcontent' e 'forumng'
192
- - Focar em estudantes da faixa 35-55 anos
193
- - Desenvolver estratégias para reduzir taxa de reprovação (13.3%)
 
194
  """)
195
 
196
  # Footer
 
109
  st.pyplot(fig_comparativo)
110
  plt.clf()
111
 
112
+ # Resumo comparativo dinâmico
113
  st.markdown("### 📈 Resumo Comparativo")
114
+
115
+ # Carregar métricas para comparação dinâmica
116
+ metricas_uci = obter_metricas_principais_uci()
117
+ metricas_oulad = obter_metricas_principais_oulad()
118
+
119
+ # Determinar gênero predominante
120
+ uci_genero_maioria = max(metricas_uci['distribuicao_genero'], key=metricas_uci['distribuicao_genero'].get) if metricas_uci['distribuicao_genero'] else 'N/A'
121
+ oulad_genero_maioria = max(metricas_oulad['distribuicao_genero'], key=metricas_oulad['distribuicao_genero'].get) if metricas_oulad['distribuicao_genero'] else 'N/A'
122
+
123
+ st.markdown(f"""
124
  **Principais Diferenças:**
125
  - **Modalidade**: UCI (presencial) vs OULAD (online)
126
+ - **Taxa de Aprovação**: OULAD ({metricas_oulad['taxa_aprovacao']:.1f}%) vs UCI ({metricas_uci['taxa_aprovacao']:.1f}%)
127
+ - **Total de Estudantes**: UCI ({metricas_uci['total_estudantes']:,}) vs OULAD ({metricas_oulad['total_estudantes']:,})
128
+ - **Demografia**: UCI tem mais {uci_genero_maioria} ({metricas_uci['distribuicao_genero'].get(uci_genero_maioria, 0):.1f}%), OULAD tem mais {oulad_genero_maioria} ({metricas_oulad['distribuicao_genero'].get(oulad_genero_maioria, 0):.1f}%)
129
+ - **Faixa Etária**: UCI (15-19 anos) vs OULAD ({metricas_oulad['faixa_etaria_principal']})
130
+ - **Engajamento**: OULAD permite medir cliques ({metricas_oulad['media_cliques']:.1f} cliques/estudante) e atividades online
131
  """)
132
 
133
  with tab4:
 
176
 
177
  with col1:
178
  st.markdown("### 📚 Para Escolas Públicas (UCI)")
179
+
180
+ # Carregar métricas UCI para insights dinâmicos
181
+ metricas_uci = obter_metricas_principais_uci()
182
+
183
+ st.markdown(f"""
184
  **Pontos de Atenção:**
185
+ - Focar em redução de faltas (média atual: {metricas_uci['media_faltas']:.1f} faltas/estudante)
186
+ - Incentivar tempo de estudo adequado (média atual: {metricas_uci['media_tempo_estudo']:.1f}h/semana)
187
+ - Apoiar estudantes com baixo consumo de álcool ({metricas_uci['estudantes_alcool_baixo']:.1f}% têm baixo consumo)
188
  - Considerar influência da escolaridade dos pais
189
+ - Taxa de aprovação atual: {metricas_uci['taxa_aprovacao']:.1f}%
190
 
191
  **Recomendações:**
192
  - Programas de acompanhamento para estudantes com muitas faltas
193
  - Workshops sobre gestão de tempo de estudo
194
  - Envolvimento das famílias no processo educacional
195
+ - Foco em melhorar a taxa de aprovação de {metricas_uci['taxa_aprovacao']:.1f}%
196
  """)
197
 
198
  with col2:
199
  st.markdown("### 🌐 Para Plataformas Online (OULAD)")
200
+
201
+ # Carregar métricas OULAD para insights dinâmicos
202
+ metricas_oulad = obter_metricas_principais_oulad()
203
+
204
+ st.markdown(f"""
205
  **Pontos Fortes:**
206
+ - Alta taxa de aprovação ({metricas_oulad['taxa_aprovacao']:.1f}%)
207
+ - Boa distribuição de atividades (principal: {metricas_oulad['atividade_mais_comum']})
208
+ - Engajamento moderado mas efetivo ({metricas_oulad['media_cliques']:.1f} cliques/estudante)
209
+ - {metricas_oulad['estudantes_distincao']:.1f}% dos estudantes obtêm distinção
210
 
211
  **Recomendações:**
212
+ - Aumentar atividades do tipo '{metricas_oulad['atividade_mais_comum']}'
213
+ - Focar em estudantes da faixa {metricas_oulad['faixa_etaria_principal']}
214
+ - Desenvolver estratégias para reduzir taxa de reprovação ({metricas_oulad['estudantes_reprovados']:.1f}%)
215
+ - Manter foco na região {metricas_oulad['regiao_principal']}
216
  """)
217
 
218
  # Footer
webapp/home_old_1.py ADDED
File without changes
webapp/src/utilidades.py CHANGED
@@ -46,34 +46,169 @@ def carregar_dados_dashboard():
46
  return df_uci, df_oulad
47
 
48
  def obter_metricas_principais_uci():
49
- """Retorna métricas principais do dataset UCI baseadas nas análises"""
50
- return {
51
- 'total_estudantes': 1044,
52
- 'media_nota_final': 10.42,
53
- 'taxa_aprovacao': 67.3,
54
- 'media_faltas': 5.7,
55
- 'distribuicao_genero': {'F': 58.2, 'M': 41.8},
56
- 'media_tempo_estudo': 2.0,
57
- 'correlacao_g1_g3': 0.81,
58
- 'correlacao_g2_g3': 0.91,
59
- 'estudantes_alcool_baixo': 45.2,
60
- 'estudantes_alcool_alto': 12.8
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  def obter_metricas_principais_oulad():
64
- """Retorna métricas principais do dataset OULAD baseadas nas análises"""
65
- return {
66
- 'total_estudantes': 28000,
67
- 'taxa_aprovacao': 78.5,
68
- 'media_cliques': 4.65,
69
- 'distribuicao_genero': {'M': 56.2, 'F': 43.8},
70
- 'faixa_etaria_principal': '35-55 anos',
71
- 'atividade_mais_comum': 'outcontent',
72
- 'regiao_principal': 'South West Region',
73
- 'estudantes_aprovados': 78.5,
74
- 'estudantes_distincao': 8.2,
75
- 'estudantes_reprovados': 13.3
76
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  def calcular_metricas_uci(df_uci):
79
  """Calcula métricas principais para o dataset UCI"""
@@ -85,7 +220,7 @@ def calcular_metricas_uci(df_uci):
85
  'media_nota_final': df_uci['G3'].mean() if 'G3' in df_uci.columns else 0,
86
  'taxa_aprovacao': (df_uci['G3'] >= 10).mean() * 100 if 'G3' in df_uci.columns else 0,
87
  'media_faltas': df_uci['absences'].mean() if 'absences' in df_uci.columns else 0,
88
- 'media_tempo_estudo': df_uci['studytime'].mean() if 'studytime' in df_uci.columns else 0,
89
  'distribuicao_genero': df_uci['sex'].value_counts().to_dict() if 'sex' in df_uci.columns else {},
90
  'correlacao_notas': df_uci[['G1', 'G2', 'G3']].corr().to_dict() if all(col in df_uci.columns for col in ['G1', 'G2', 'G3']) else {}
91
  }
@@ -131,19 +266,23 @@ def criar_sidebar_dashboard():
131
  with st.sidebar:
132
  st.markdown("### 📊 Dashboard Educacional")
133
 
 
 
 
 
134
  st.markdown("### 📚 Sobre os Datasets")
135
- st.markdown("""
136
  **📚 UCI Dataset:**
137
  - Escolas públicas portuguesas
138
- - 1,044 estudantes
139
  - Dados demográficos e acadêmicos
140
  - Análise de fatores de sucesso
141
  """)
142
 
143
- st.markdown("""
144
  **🌐 OULAD Dataset:**
145
  - Plataforma de aprendizado online
146
- - 28,000 estudantes
147
  - Dados de engajamento digital
148
  - Análise de atividades online
149
  """)
@@ -154,38 +293,62 @@ def criar_sidebar_dashboard():
154
  # Métricas UCI
155
  st.metric(
156
  "���� UCI - Aprovação",
157
- "67.3%",
158
  help="Taxa de aprovação nas escolas públicas"
159
  )
160
 
161
  st.metric(
162
  "📊 UCI - Média Notas",
163
- "10.4",
164
  help="Média das notas finais"
165
  )
166
 
167
  # Métricas OULAD
168
  st.metric(
169
  "🌐 OULAD - Aprovação",
170
- "78.5%",
171
  help="Taxa de aprovação na plataforma online"
172
  )
173
 
174
  st.metric(
175
  "🖱️ OULAD - Engajamento",
176
- "4.65",
177
  help="Média de cliques por estudante"
178
  )
179
 
180
  st.markdown("---")
181
  st.markdown("### 💡 Principais Insights")
182
- st.markdown("""
183
- - **Correlação forte** entre notas bimestrais e finais
184
- - **Gênero influencia** desempenho acadêmico
185
- - **Faltas impactam** negativamente o desempenho
186
- - **Tempo de estudo** ideal: 5-10h/semana
187
- - **Atividades online** mais efetivas: outcontent, forumng
188
- """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
  st.markdown("---")
191
  st.markdown("### ℹ️ Informações")
@@ -302,30 +465,102 @@ def exibir_cartoes_detalhados():
302
  )
303
 
304
  def obter_insights_uci():
305
- """Retorna insights principais do dataset UCI"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  return {
307
  'titulo': '📚 Principais Insights - Dataset UCI',
308
- 'insights': [
309
- "🎯 **Correlação Forte**: Notas do 1º e 2º bimestre têm correlação de 0.81 e 0.91 com a nota final",
310
- "👥 **Gênero**: Estudantes do sexo feminino representam 58.2% e têm desempenho ligeiramente superior",
311
- "🍷 **Consumo de Álcool**: 45.2% dos estudantes têm baixo consumo, com melhor desempenho acadêmico",
312
- "📚 **Tempo de Estudo**: Estudantes que estudam 5-10h/semana têm concentração de notas mais altas",
313
- " **Faltas**: Estudantes com menos de 10 faltas alcançam notas máximas (10-14 pontos)",
314
- "👨‍👩‍👧‍👦 **Família**: Escolaridade dos pais influencia diretamente o desempenho dos filhos"
315
  ]
316
  }
317
 
318
  def obter_insights_oulad():
319
- """Retorna insights principais do dataset OULAD"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  return {
321
  'titulo': '🌐 Principais Insights - Dataset OULAD',
322
- 'insights': [
323
- "👥 **Demografia**: 56.2% são do sexo masculino, com faixa etária predominante de 35-55 anos",
324
- "🏆 **Alto Desempenho**: 78.5% de aprovação, com 8.2% obtendo distinção",
325
- "🖱️ **Engajamento**: Média de 4.65 cliques por estudante, indicando engajamento moderado",
326
- "📚 **Atividades**: 'outcontent' é a atividade mais realizada, seguida por 'forumng'",
327
- "🌍 **Região**: South West Region concentra a maior parte dos estudantes",
328
- "📊 **Distribuição**: Aprovação supera largamente outras categorias (reprovação: 13.3%)"
329
  ]
330
  }
331
 
 
46
  return df_uci, df_oulad
47
 
48
  def obter_metricas_principais_uci():
49
+ """Retorna métricas principais do dataset UCI calculadas dinamicamente"""
50
+ try:
51
+ df_uci = carregar_dados_uci_cached()
52
+ if df_uci.empty:
53
+ return {
54
+ 'total_estudantes': 0,
55
+ 'media_nota_final': 0,
56
+ 'taxa_aprovacao': 0,
57
+ 'media_faltas': 0,
58
+ 'distribuicao_genero': {},
59
+ 'media_tempo_estudo': 0,
60
+ 'correlacao_g1_g3': 0,
61
+ 'correlacao_g2_g3': 0,
62
+ 'estudantes_alcool_baixo': 0,
63
+ 'estudantes_alcool_alto': 0
64
+ }
65
+
66
+ # Calcular métricas reais
67
+ total_estudantes = len(df_uci)
68
+ media_nota_final = df_uci['G3'].mean() if 'G3' in df_uci.columns else 0
69
+ taxa_aprovacao = (df_uci['G3'] >= 10).mean() * 100 if 'G3' in df_uci.columns else 0
70
+ media_faltas = df_uci['absences'].mean() if 'absences' in df_uci.columns else 0
71
+
72
+ # Distribuição de gênero
73
+ if 'sex' in df_uci.columns:
74
+ dist_genero = df_uci['sex'].value_counts(normalize=True) * 100
75
+ distribuicao_genero = {k: round(v, 1) for k, v in dist_genero.to_dict().items()}
76
+ else:
77
+ distribuicao_genero = {}
78
+
79
+ # Tempo de estudo médio - converter strings para números
80
+ if 'studytime' in df_uci.columns:
81
+ # Mapear strings para números para calcular média
82
+ studytime_map = {'<2h': 1, '2-5h': 2, '5-10h': 3, '>10h': 4}
83
+ studytime_numeric = df_uci['studytime'].map(studytime_map)
84
+ media_tempo_estudo = studytime_numeric.mean()
85
+ else:
86
+ media_tempo_estudo = 0
87
+
88
+ # Correlações
89
+ correlacao_g1_g3 = df_uci[['G1', 'G3']].corr().iloc[0, 1] if all(col in df_uci.columns for col in ['G1', 'G3']) else 0
90
+ correlacao_g2_g3 = df_uci[['G2', 'G3']].corr().iloc[0, 1] if all(col in df_uci.columns for col in ['G2', 'G3']) else 0
91
+
92
+ # Consumo de álcool
93
+ if 'Dalc' in df_uci.columns:
94
+ alcool_baixo = (df_uci['Dalc'] <= 2).mean() * 100
95
+ alcool_alto = (df_uci['Dalc'] >= 4).mean() * 100
96
+ else:
97
+ alcool_baixo = 0
98
+ alcool_alto = 0
99
+
100
+ return {
101
+ 'total_estudantes': total_estudantes,
102
+ 'media_nota_final': round(media_nota_final, 2),
103
+ 'taxa_aprovacao': round(taxa_aprovacao, 1),
104
+ 'media_faltas': round(media_faltas, 1),
105
+ 'distribuicao_genero': distribuicao_genero,
106
+ 'media_tempo_estudo': round(media_tempo_estudo, 1),
107
+ 'correlacao_g1_g3': round(correlacao_g1_g3, 2),
108
+ 'correlacao_g2_g3': round(correlacao_g2_g3, 2),
109
+ 'estudantes_alcool_baixo': round(alcool_baixo, 1),
110
+ 'estudantes_alcool_alto': round(alcool_alto, 1)
111
+ }
112
+ except Exception as e:
113
+ st.warning(f"Erro ao calcular métricas UCI: {e}")
114
+ return {
115
+ 'total_estudantes': 0,
116
+ 'media_nota_final': 0,
117
+ 'taxa_aprovacao': 0,
118
+ 'media_faltas': 0,
119
+ 'distribuicao_genero': {},
120
+ 'media_tempo_estudo': 0,
121
+ 'correlacao_g1_g3': 0,
122
+ 'correlacao_g2_g3': 0,
123
+ 'estudantes_alcool_baixo': 0,
124
+ 'estudantes_alcool_alto': 0
125
+ }
126
 
127
  def obter_metricas_principais_oulad():
128
+ """Retorna métricas principais do dataset OULAD calculadas dinamicamente"""
129
+ try:
130
+ df_oulad = carregar_dados_oulad_cached()
131
+ if df_oulad.empty:
132
+ return {
133
+ 'total_estudantes': 0,
134
+ 'taxa_aprovacao': 0,
135
+ 'media_cliques': 0,
136
+ 'distribuicao_genero': {},
137
+ 'faixa_etaria_principal': 'N/A',
138
+ 'atividade_mais_comum': 'N/A',
139
+ 'regiao_principal': 'N/A',
140
+ 'estudantes_aprovados': 0,
141
+ 'estudantes_distincao': 0,
142
+ 'estudantes_reprovados': 0
143
+ }
144
+
145
+ # Calcular métricas reais
146
+ total_estudantes = len(df_oulad)
147
+ media_cliques = df_oulad['clicks'].mean() if 'clicks' in df_oulad.columns else 0
148
+
149
+ # Taxa de aprovação
150
+ if 'final_result' in df_oulad.columns:
151
+ taxa_aprovacao = (df_oulad['final_result'] == 'Pass').mean() * 100
152
+ estudantes_aprovados = taxa_aprovacao
153
+ estudantes_distincao = (df_oulad['final_result'] == 'Distinction').mean() * 100
154
+ estudantes_reprovados = (df_oulad['final_result'] == 'Fail').mean() * 100
155
+ else:
156
+ taxa_aprovacao = 0
157
+ estudantes_aprovados = 0
158
+ estudantes_distincao = 0
159
+ estudantes_reprovados = 0
160
+
161
+ # Distribuição de gênero
162
+ if 'gender' in df_oulad.columns:
163
+ dist_genero = df_oulad['gender'].value_counts(normalize=True) * 100
164
+ distribuicao_genero = {k: round(v, 1) for k, v in dist_genero.to_dict().items()}
165
+ else:
166
+ distribuicao_genero = {}
167
+
168
+ # Faixa etária principal
169
+ if 'age_band' in df_oulad.columns:
170
+ faixa_etaria_principal = df_oulad['age_band'].mode().iloc[0] if not df_oulad['age_band'].mode().empty else 'N/A'
171
+ else:
172
+ faixa_etaria_principal = 'N/A'
173
+
174
+ # Atividade mais comum
175
+ if 'activity_type' in df_oulad.columns:
176
+ atividade_mais_comum = df_oulad['activity_type'].mode().iloc[0] if not df_oulad['activity_type'].mode().empty else 'N/A'
177
+ else:
178
+ atividade_mais_comum = 'N/A'
179
+
180
+ # Região principal
181
+ if 'region' in df_oulad.columns:
182
+ regiao_principal = df_oulad['region'].mode().iloc[0] if not df_oulad['region'].mode().empty else 'N/A'
183
+ else:
184
+ regiao_principal = 'N/A'
185
+
186
+ return {
187
+ 'total_estudantes': total_estudantes,
188
+ 'taxa_aprovacao': round(taxa_aprovacao, 1),
189
+ 'media_cliques': round(media_cliques, 2),
190
+ 'distribuicao_genero': distribuicao_genero,
191
+ 'faixa_etaria_principal': faixa_etaria_principal,
192
+ 'atividade_mais_comum': atividade_mais_comum,
193
+ 'regiao_principal': regiao_principal,
194
+ 'estudantes_aprovados': round(estudantes_aprovados, 1),
195
+ 'estudantes_distincao': round(estudantes_distincao, 1),
196
+ 'estudantes_reprovados': round(estudantes_reprovados, 1)
197
+ }
198
+ except Exception as e:
199
+ st.warning(f"Erro ao calcular métricas OULAD: {e}")
200
+ return {
201
+ 'total_estudantes': 0,
202
+ 'taxa_aprovacao': 0,
203
+ 'media_cliques': 0,
204
+ 'distribuicao_genero': {},
205
+ 'faixa_etaria_principal': 'N/A',
206
+ 'atividade_mais_comum': 'N/A',
207
+ 'regiao_principal': 'N/A',
208
+ 'estudantes_aprovados': 0,
209
+ 'estudantes_distincao': 0,
210
+ 'estudantes_reprovados': 0
211
+ }
212
 
213
  def calcular_metricas_uci(df_uci):
214
  """Calcula métricas principais para o dataset UCI"""
 
220
  'media_nota_final': df_uci['G3'].mean() if 'G3' in df_uci.columns else 0,
221
  'taxa_aprovacao': (df_uci['G3'] >= 10).mean() * 100 if 'G3' in df_uci.columns else 0,
222
  'media_faltas': df_uci['absences'].mean() if 'absences' in df_uci.columns else 0,
223
+ 'media_tempo_estudo': df_uci['studytime'].map({'<2h': 1, '2-5h': 2, '5-10h': 3, '>10h': 4}).mean() if 'studytime' in df_uci.columns else 0,
224
  'distribuicao_genero': df_uci['sex'].value_counts().to_dict() if 'sex' in df_uci.columns else {},
225
  'correlacao_notas': df_uci[['G1', 'G2', 'G3']].corr().to_dict() if all(col in df_uci.columns for col in ['G1', 'G2', 'G3']) else {}
226
  }
 
266
  with st.sidebar:
267
  st.markdown("### 📊 Dashboard Educacional")
268
 
269
+ # Carregar métricas dinâmicas
270
+ metricas_uci = obter_metricas_principais_uci()
271
+ metricas_oulad = obter_metricas_principais_oulad()
272
+
273
  st.markdown("### 📚 Sobre os Datasets")
274
+ st.markdown(f"""
275
  **📚 UCI Dataset:**
276
  - Escolas públicas portuguesas
277
+ - {metricas_uci['total_estudantes']:,} estudantes
278
  - Dados demográficos e acadêmicos
279
  - Análise de fatores de sucesso
280
  """)
281
 
282
+ st.markdown(f"""
283
  **🌐 OULAD Dataset:**
284
  - Plataforma de aprendizado online
285
+ - {metricas_oulad['total_estudantes']:,} estudantes
286
  - Dados de engajamento digital
287
  - Análise de atividades online
288
  """)
 
293
  # Métricas UCI
294
  st.metric(
295
  "���� UCI - Aprovação",
296
+ f"{metricas_uci['taxa_aprovacao']:.1f}%",
297
  help="Taxa de aprovação nas escolas públicas"
298
  )
299
 
300
  st.metric(
301
  "📊 UCI - Média Notas",
302
+ f"{metricas_uci['media_nota_final']:.1f}",
303
  help="Média das notas finais"
304
  )
305
 
306
  # Métricas OULAD
307
  st.metric(
308
  "🌐 OULAD - Aprovação",
309
+ f"{metricas_oulad['taxa_aprovacao']:.1f}%",
310
  help="Taxa de aprovação na plataforma online"
311
  )
312
 
313
  st.metric(
314
  "🖱️ OULAD - Engajamento",
315
+ f"{metricas_oulad['media_cliques']:.1f}",
316
  help="Média de cliques por estudante"
317
  )
318
 
319
  st.markdown("---")
320
  st.markdown("### 💡 Principais Insights")
321
+
322
+ # Insights dinâmicos baseados nos dados reais
323
+ insights_text = []
324
+
325
+ if metricas_uci['correlacao_g1_g3'] > 0.7:
326
+ insights_text.append(f"**Correlação forte** entre notas bimestrais e finais ({metricas_uci['correlacao_g1_g3']:.2f})")
327
+
328
+ if metricas_uci['distribuicao_genero']:
329
+ genero_maioria = max(metricas_uci['distribuicao_genero'], key=metricas_uci['distribuicao_genero'].get)
330
+ insights_text.append(f"**Gênero predominante**: {genero_maioria} ({metricas_uci['distribuicao_genero'][genero_maioria]:.1f}%)")
331
+
332
+ if metricas_uci['media_faltas'] > 0:
333
+ insights_text.append(f"**Média de faltas**: {metricas_uci['media_faltas']:.1f} por estudante")
334
+
335
+ if metricas_uci['media_tempo_estudo'] > 0:
336
+ insights_text.append(f"**Tempo de estudo médio**: {metricas_uci['media_tempo_estudo']:.1f}h/semana")
337
+
338
+ if metricas_oulad['atividade_mais_comum'] != 'N/A':
339
+ insights_text.append(f"**Atividade mais comum**: {metricas_oulad['atividade_mais_comum']}")
340
+
341
+ if insights_text:
342
+ for insight in insights_text:
343
+ st.markdown(f"- {insight}")
344
+ else:
345
+ st.markdown("""
346
+ - **Correlação forte** entre notas bimestrais e finais
347
+ - **Gênero influencia** desempenho acadêmico
348
+ - **Faltas impactam** negativamente o desempenho
349
+ - **Tempo de estudo** ideal: 5-10h/semana
350
+ - **Atividades online** mais efetivas: outcontent, forumng
351
+ """)
352
 
353
  st.markdown("---")
354
  st.markdown("### ℹ️ Informações")
 
465
  )
466
 
467
  def obter_insights_uci():
468
+ """Retorna insights principais do dataset UCI baseados em dados reais"""
469
+ metricas = obter_metricas_principais_uci()
470
+
471
+ insights = []
472
+
473
+ # Correlação forte
474
+ if metricas['correlacao_g1_g3'] > 0.7 and metricas['correlacao_g2_g3'] > 0.7:
475
+ insights.append(f"🎯 **Correlação Forte**: Notas do 1º e 2º bimestre têm correlação de {metricas['correlacao_g1_g3']:.2f} e {metricas['correlacao_g2_g3']:.2f} com a nota final")
476
+
477
+ # Gênero
478
+ if metricas['distribuicao_genero']:
479
+ genero_maioria = max(metricas['distribuicao_genero'], key=metricas['distribuicao_genero'].get)
480
+ genero_menor = min(metricas['distribuicao_genero'], key=metricas['distribuicao_genero'].get)
481
+ insights.append(f"👥 **Gênero**: Estudantes do sexo {genero_maioria} representam {metricas['distribuicao_genero'][genero_maioria]:.1f}% vs {genero_menor} com {metricas['distribuicao_genero'][genero_menor]:.1f}%")
482
+
483
+ # Consumo de álcool
484
+ if metricas['estudantes_alcool_baixo'] > 0:
485
+ insights.append(f"🍷 **Consumo de Álcool**: {metricas['estudantes_alcool_baixo']:.1f}% dos estudantes têm baixo consumo, com melhor desempenho acadêmico")
486
+
487
+ # Tempo de estudo
488
+ if metricas['media_tempo_estudo'] > 0:
489
+ insights.append(f"📚 **Tempo de Estudo**: Média de {metricas['media_tempo_estudo']:.1f}h/semana por estudante")
490
+
491
+ # Faltas
492
+ if metricas['media_faltas'] > 0:
493
+ insights.append(f"❌ **Faltas**: Média de {metricas['media_faltas']:.1f} faltas por estudante")
494
+
495
+ # Taxa de aprovação
496
+ if metricas['taxa_aprovacao'] > 0:
497
+ insights.append(f"✅ **Aprovação**: Taxa de aprovação de {metricas['taxa_aprovacao']:.1f}%")
498
+
499
+ # Média de notas
500
+ if metricas['media_nota_final'] > 0:
501
+ insights.append(f"📊 **Desempenho**: Média de notas finais de {metricas['media_nota_final']:.1f}")
502
+
503
  return {
504
  'titulo': '📚 Principais Insights - Dataset UCI',
505
+ 'insights': insights if insights else [
506
+ "🎯 **Correlação Forte**: Notas do 1º e 2º bimestre têm correlação forte com a nota final",
507
+ "👥 **Gênero**: Distribuição equilibrada entre gêneros",
508
+ "📚 **Tempo de Estudo**: Fator importante para o desempenho acadêmico",
509
+ " **Faltas**: Impactam negativamente o desempenho",
510
+ "👨‍👩‍👧‍👦 **Família**: Escolaridade dos pais influencia o desempenho dos filhos"
 
511
  ]
512
  }
513
 
514
  def obter_insights_oulad():
515
+ """Retorna insights principais do dataset OULAD baseados em dados reais"""
516
+ metricas = obter_metricas_principais_oulad()
517
+
518
+ insights = []
519
+
520
+ # Demografia
521
+ if metricas['distribuicao_genero']:
522
+ genero_maioria = max(metricas['distribuicao_genero'], key=metricas['distribuicao_genero'].get)
523
+ insights.append(f"👥 **Demografia**: {metricas['distribuicao_genero'][genero_maioria]:.1f}% são do sexo {genero_maioria}")
524
+
525
+ if metricas['faixa_etaria_principal'] != 'N/A':
526
+ insights.append(f"👥 **Faixa Etária**: Faixa etária predominante de {metricas['faixa_etaria_principal']}")
527
+
528
+ # Desempenho
529
+ if metricas['taxa_aprovacao'] > 0:
530
+ insights.append(f"🏆 **Alto Desempenho**: {metricas['taxa_aprovacao']:.1f}% de aprovação")
531
+
532
+ if metricas['estudantes_distincao'] > 0:
533
+ insights.append(f"🏆 **Distinção**: {metricas['estudantes_distincao']:.1f}% obtendo distinção")
534
+
535
+ # Engajamento
536
+ if metricas['media_cliques'] > 0:
537
+ insights.append(f"🖱️ **Engajamento**: Média de {metricas['media_cliques']:.1f} cliques por estudante, indicando engajamento moderado")
538
+
539
+ # Atividades
540
+ if metricas['atividade_mais_comum'] != 'N/A':
541
+ insights.append(f"📚 **Atividades**: '{metricas['atividade_mais_comum']}' é a atividade mais realizada")
542
+
543
+ # Região
544
+ if metricas['regiao_principal'] != 'N/A':
545
+ insights.append(f"🌍 **Região**: {metricas['regiao_principal']} concentra a maior parte dos estudantes")
546
+
547
+ # Distribuição de resultados
548
+ if metricas['estudantes_reprovados'] > 0:
549
+ insights.append(f"📊 **Distribuição**: Aprovação supera largamente outras categorias (reprovação: {metricas['estudantes_reprovados']:.1f}%)")
550
+
551
+ # Total de estudantes
552
+ if metricas['total_estudantes'] > 0:
553
+ insights.append(f"👥 **Total**: {metricas['total_estudantes']:,} estudantes analisados")
554
+
555
  return {
556
  'titulo': '🌐 Principais Insights - Dataset OULAD',
557
+ 'insights': insights if insights else [
558
+ "👥 **Demografia**: Distribuição equilibrada entre gêneros",
559
+ "🏆 **Alto Desempenho**: Boa taxa de aprovação geral",
560
+ "🖱️ **Engajamento**: Nível moderado de engajamento na plataforma",
561
+ "📚 **Atividades**: Diversas atividades disponíveis",
562
+ "🌍 **Região**: Distribuição geográfica variada",
563
+ "📊 **Distribuição**: Resultados positivos predominam"
564
  ]
565
  }
566
 
webapp/src/vizualizacoes.py CHANGED
@@ -209,153 +209,236 @@ def criar_grafico_comparativo_aprovacao(df_uci, df_oulad):
209
  return fig
210
 
211
  def criar_grafico_sugerido_uci():
212
- """Cria gráfico sugerido para UCI baseado nos insights"""
213
- fig, axes = plt.subplots(2, 2, figsize=(15, 12))
214
-
215
- # 1. Distribuição de notas finais
216
- notas = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
217
- frequencias = [5, 8, 12, 15, 18, 22, 25, 28, 32, 35, 40, 45, 50, 45, 40, 35, 30, 25, 20, 15, 10]
218
-
219
- axes[0, 0].hist(notas, bins=20, weights=frequencias, alpha=0.7, color='skyblue', edgecolor='black')
220
- axes[0, 0].set_title('Distribuição de Notas Finais (UCI)')
221
- axes[0, 0].set_xlabel('Nota Final')
222
- axes[0, 0].set_ylabel('Frequência')
223
- axes[0, 0].axvline(x=10, color='red', linestyle='--', label='Nota de Aprovação')
224
- axes[0, 0].legend()
225
-
226
- # 2. Desempenho por gênero
227
- generos = ['Feminino', 'Masculino']
228
- medias = [10.8, 9.9]
229
- cores = ['pink', 'lightblue']
230
-
231
- bars = axes[0, 1].bar(generos, medias, color=cores)
232
- axes[0, 1].set_title('Média de Notas por Gênero')
233
- axes[0, 1].set_ylabel('Média de Notas')
234
- for bar, media in zip(bars, medias):
235
- axes[0, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1,
236
- f'{media:.1f}', ha='center', va='bottom')
237
-
238
- # 3. Faltas vs Desempenho
239
- faltas_cat = ['0-5', '6-10', '11-15', '16-20', '21+']
240
- medias_notas = [11.2, 10.8, 10.1, 9.5, 8.8]
241
-
242
- axes[1, 0].plot(faltas_cat, medias_notas, marker='o', linewidth=2, markersize=8, color='red')
243
- axes[1, 0].set_title('Faltas vs Média de Notas')
244
- axes[1, 0].set_xlabel('Categoria de Faltas')
245
- axes[1, 0].set_ylabel('Média de Notas')
246
- axes[1, 0].tick_params(axis='x', rotation=45)
247
-
248
- # 4. Tempo de estudo vs Desempenho
249
- tempo_estudo = ['<2h', '2-5h', '5-10h', '>10h']
250
- medias_tempo = [9.8, 10.5, 11.2, 10.9]
251
-
252
- axes[1, 1].bar(tempo_estudo, medias_tempo, color=['lightcoral', 'lightgreen', 'gold', 'lightblue'])
253
- axes[1, 1].set_title('Tempo de Estudo vs Média de Notas')
254
- axes[1, 1].set_xlabel('Tempo de Estudo Semanal')
255
- axes[1, 1].set_ylabel('Média de Notas')
256
- for i, media in enumerate(medias_tempo):
257
- axes[1, 1].text(i, media + 0.1, f'{media:.1f}', ha='center', va='bottom')
258
-
259
- plt.tight_layout()
260
- return fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
  def criar_grafico_sugerido_oulad():
263
- """Cria gráfico sugerido para OULAD baseado nos insights"""
264
- fig, axes = plt.subplots(2, 2, figsize=(15, 12))
265
-
266
- # 1. Distribuição de resultados finais
267
- resultados = ['Pass', 'Distinction', 'Fail', 'Withdrawn']
268
- percentuais = [78.5, 8.2, 13.3, 0]
269
- cores = ['lightgreen', 'gold', 'lightcoral', 'lightgray']
270
-
271
- wedges, texts, autotexts = axes[0, 0].pie(percentuais, labels=resultados, colors=cores, autopct='%1.1f%%', startangle=90)
272
- axes[0, 0].set_title('Distribuição de Resultados Finais (OULAD)')
273
-
274
- # 2. Distribuição por gênero
275
- generos = ['Masculino', 'Feminino']
276
- percentuais_gen = [56.2, 43.8]
277
- cores_gen = ['lightblue', 'pink']
278
-
279
- bars = axes[0, 1].bar(generos, percentuais_gen, color=cores_gen)
280
- axes[0, 1].set_title('Distribuição por Gênero')
281
- axes[0, 1].set_ylabel('Percentual (%)')
282
- for bar, pct in zip(bars, percentuais_gen):
283
- axes[0, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
284
- f'{pct:.1f}%', ha='center', va='bottom')
285
-
286
- # 3. Distribuição de atividades
287
- atividades = ['outcontent', 'forumng', 'subpage', 'resource', 'url', 'homepage']
288
- cliques = [45, 25, 15, 8, 4, 3]
289
-
290
- axes[1, 0].barh(atividades, cliques, color='lightsteelblue')
291
- axes[1, 0].set_title('Distribuição de Atividades por Tipo')
292
- axes[1, 0].set_xlabel('Número de Cliques (milhares)')
293
-
294
- # 4. Distribuição por faixa etária
295
- faixas_etarias = ['0-35', '35-55', '55+']
296
- percentuais_idade = [35, 45, 20]
297
- cores_idade = ['lightgreen', 'gold', 'lightcoral']
298
-
299
- bars = axes[1, 1].bar(faixas_etarias, percentuais_idade, color=cores_idade)
300
- axes[1, 1].set_title('Distribuição por Faixa Etária')
301
- axes[1, 1].set_ylabel('Percentual (%)')
302
- for bar, pct in zip(bars, percentuais_idade):
303
- axes[1, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
304
- f'{pct}%', ha='center', va='bottom')
305
-
306
- plt.tight_layout()
307
- return fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
  def criar_grafico_comparativo_insights():
310
- """Cria gráfico comparativo de insights entre os datasets"""
311
- fig, axes = plt.subplots(1, 3, figsize=(18, 6))
312
-
313
- # 1. Taxa de aprovação comparativa
314
- datasets = ['UCI\n(Escolas Públicas)', 'OULAD\n(Online)']
315
- taxas = [67.3, 78.5]
316
- cores = ['lightcoral', 'lightgreen']
317
-
318
- bars = axes[0].bar(datasets, taxas, color=cores)
319
- axes[0].set_title('Taxa de Aprovação Comparativa')
320
- axes[0].set_ylabel('Taxa de Aprovação (%)')
321
- axes[0].set_ylim(0, 100)
322
- for bar, taxa in zip(bars, taxas):
323
- axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
324
- f'{taxa:.1f}%', ha='center', va='bottom')
325
-
326
- # 2. Distribuição de gênero comparativa
327
- generos = ['Feminino', 'Masculino']
328
- uci_pct = [58.2, 41.8]
329
- oulad_pct = [43.8, 56.2]
330
-
331
- x = range(len(generos))
332
- width = 0.35
333
-
334
- axes[1].bar([i - width/2 for i in x], uci_pct, width, label='UCI', color='lightcoral', alpha=0.8)
335
- axes[1].bar([i + width/2 for i in x], oulad_pct, width, label='OULAD', color='lightgreen', alpha=0.8)
336
- axes[1].set_title('Distribuição de Gênero Comparativa')
337
- axes[1].set_ylabel('Percentual (%)')
338
- axes[1].set_xlabel('Gênero')
339
- axes[1].set_xticks(x)
340
- axes[1].set_xticklabels(generos)
341
- axes[1].legend()
342
-
343
- # 3. Engajamento vs Desempenho
344
- categorias = ['Baixo\nEngajamento', 'Médio\nEngajamento', 'Alto\nEngajamento']
345
- uci_desempenho = [8.5, 10.2, 11.8]
346
- oulad_desempenho = [65, 78, 85]
347
-
348
- x = range(len(categorias))
349
- width = 0.35
350
-
351
- axes[2].bar([i - width/2 for i in x], uci_desempenho, width, label='UCI (Notas)', color='lightcoral', alpha=0.8)
352
- axes[2].bar([i + width/2 for i in x], oulad_desempenho, width, label='OULAD (Aprovação %)', color='lightgreen', alpha=0.8)
353
- axes[2].set_title('Engajamento vs Desempenho')
354
- axes[2].set_ylabel('Desempenho')
355
- axes[2].set_xlabel('Nível de Engajamento')
356
- axes[2].set_xticks(x)
357
- axes[2].set_xticklabels(categorias)
358
- axes[2].legend()
359
-
360
- plt.tight_layout()
361
- return fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  return fig
210
 
211
  def criar_grafico_sugerido_uci():
212
+ """Cria gráfico sugerido para UCI baseado em dados reais"""
213
+ try:
214
+ from .utilidades import carregar_dados_uci_cached
215
+ df_uci = carregar_dados_uci_cached()
216
+
217
+ if df_uci.empty:
218
+ return None
219
+
220
+ fig, axes = plt.subplots(2, 2, figsize=(15, 12))
221
+
222
+ # 1. Distribuição de notas finais
223
+ if 'G3' in df_uci.columns:
224
+ axes[0, 0].hist(df_uci['G3'], bins=20, alpha=0.7, color='skyblue', edgecolor='black')
225
+ axes[0, 0].set_title('Distribuição de Notas Finais (UCI)')
226
+ axes[0, 0].set_xlabel('Nota Final')
227
+ axes[0, 0].set_ylabel('Frequência')
228
+ axes[0, 0].axvline(x=10, color='red', linestyle='--', label='Nota de Aprovação')
229
+ axes[0, 0].legend()
230
+ else:
231
+ axes[0, 0].text(0.5, 0.5, 'Dados de notas não disponíveis', ha='center', va='center', transform=axes[0, 0].transAxes)
232
+ axes[0, 0].set_title('Distribuição de Notas Finais (UCI)')
233
+
234
+ # 2. Desempenho por gênero
235
+ if 'sex' in df_uci.columns and 'G3' in df_uci.columns:
236
+ genero_medias = df_uci.groupby('sex')['G3'].mean()
237
+ generos = genero_medias.index.tolist()
238
+ medias = genero_medias.values.tolist()
239
+ cores = ['pink', 'lightblue']
240
+
241
+ bars = axes[0, 1].bar(generos, medias, color=cores[:len(generos)])
242
+ axes[0, 1].set_title('Média de Notas por Gênero')
243
+ axes[0, 1].set_ylabel('Média de Notas')
244
+ for bar, media in zip(bars, medias):
245
+ axes[0, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1,
246
+ f'{media:.1f}', ha='center', va='bottom')
247
+ else:
248
+ axes[0, 1].text(0.5, 0.5, 'Dados de gênero/notas não disponíveis', ha='center', va='center', transform=axes[0, 1].transAxes)
249
+ axes[0, 1].set_title('Média de Notas por Gênero')
250
+
251
+ # 3. Faltas vs Desempenho
252
+ if 'absences' in df_uci.columns and 'G3' in df_uci.columns:
253
+ # Criar categorias de faltas
254
+ temp_df = df_uci.copy()
255
+ temp_df['absences_cat'] = pd.cut(temp_df['absences'],
256
+ bins=[0, 5, 10, 15, 20, 100],
257
+ labels=['0-5', '6-10', '11-15', '16-20', '21+'])
258
+
259
+ faltas_medias = temp_df.groupby('absences_cat')['G3'].mean()
260
+ faltas_cat = faltas_medias.index.tolist()
261
+ medias_notas = faltas_medias.values.tolist()
262
+
263
+ axes[1, 0].plot(faltas_cat, medias_notas, marker='o', linewidth=2, markersize=8, color='red')
264
+ axes[1, 0].set_title('Faltas vs Média de Notas')
265
+ axes[1, 0].set_xlabel('Categoria de Faltas')
266
+ axes[1, 0].set_ylabel('Média de Notas')
267
+ axes[1, 0].tick_params(axis='x', rotation=45)
268
+ else:
269
+ axes[1, 0].text(0.5, 0.5, 'Dados de faltas/notas não disponíveis', ha='center', va='center', transform=axes[1, 0].transAxes)
270
+ axes[1, 0].set_title('Faltas vs Média de Notas')
271
+
272
+ # 4. Tempo de estudo vs Desempenho
273
+ if 'studytime' in df_uci.columns and 'G3' in df_uci.columns:
274
+ tempo_medias = df_uci.groupby('studytime')['G3'].mean()
275
+ tempo_estudo = tempo_medias.index.tolist()
276
+ medias_tempo = tempo_medias.values.tolist()
277
+ cores = ['lightcoral', 'lightgreen', 'gold', 'lightblue']
278
+
279
+ bars = axes[1, 1].bar(tempo_estudo, medias_tempo, color=cores[:len(tempo_estudo)])
280
+ axes[1, 1].set_title('Tempo de Estudo vs Média de Notas')
281
+ axes[1, 1].set_xlabel('Tempo de Estudo Semanal')
282
+ axes[1, 1].set_ylabel('Média de Notas')
283
+ axes[1, 1].tick_params(axis='x', rotation=45)
284
+ for i, media in enumerate(medias_tempo):
285
+ axes[1, 1].text(i, media + 0.1, f'{media:.1f}', ha='center', va='bottom')
286
+ else:
287
+ axes[1, 1].text(0.5, 0.5, 'Dados de tempo de estudo/notas não disponíveis', ha='center', va='center', transform=axes[1, 1].transAxes)
288
+ axes[1, 1].set_title('Tempo de Estudo vs Média de Notas')
289
+
290
+ plt.tight_layout()
291
+ return fig
292
+
293
+ except Exception as e:
294
+ st.warning(f"Erro ao criar gráfico UCI: {e}")
295
+ return None
296
 
297
  def criar_grafico_sugerido_oulad():
298
+ """Cria gráfico sugerido para OULAD baseado em dados reais"""
299
+ try:
300
+ from .utilidades import carregar_dados_oulad_cached
301
+ df_oulad = carregar_dados_oulad_cached()
302
+
303
+ if df_oulad.empty:
304
+ return None
305
+
306
+ fig, axes = plt.subplots(2, 2, figsize=(15, 12))
307
+
308
+ # 1. Distribuição de resultados finais
309
+ if 'final_result' in df_oulad.columns:
310
+ resultados_counts = df_oulad['final_result'].value_counts()
311
+ resultados = resultados_counts.index.tolist()
312
+ percentuais = (resultados_counts / len(df_oulad) * 100).tolist()
313
+ cores = ['lightgreen', 'gold', 'lightcoral', 'lightgray']
314
+
315
+ wedges, texts, autotexts = axes[0, 0].pie(percentuais, labels=resultados, colors=cores[:len(resultados)], autopct='%1.1f%%', startangle=90)
316
+ axes[0, 0].set_title('Distribuição de Resultados Finais (OULAD)')
317
+ else:
318
+ axes[0, 0].text(0.5, 0.5, 'Dados de resultados não disponíveis', ha='center', va='center', transform=axes[0, 0].transAxes)
319
+ axes[0, 0].set_title('Distribuição de Resultados Finais (OULAD)')
320
+
321
+ # 2. Distribuição por gênero
322
+ if 'gender' in df_oulad.columns:
323
+ genero_counts = df_oulad['gender'].value_counts()
324
+ generos = genero_counts.index.tolist()
325
+ percentuais_gen = (genero_counts / len(df_oulad) * 100).tolist()
326
+ cores_gen = ['lightblue', 'pink']
327
+
328
+ bars = axes[0, 1].bar(generos, percentuais_gen, color=cores_gen[:len(generos)])
329
+ axes[0, 1].set_title('Distribuição por Gênero')
330
+ axes[0, 1].set_ylabel('Percentual (%)')
331
+ for bar, pct in zip(bars, percentuais_gen):
332
+ axes[0, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
333
+ f'{pct:.1f}%', ha='center', va='bottom')
334
+ else:
335
+ axes[0, 1].text(0.5, 0.5, 'Dados de gênero não disponíveis', ha='center', va='center', transform=axes[0, 1].transAxes)
336
+ axes[0, 1].set_title('Distribuição por Gênero')
337
+
338
+ # 3. Distribuição de atividades
339
+ if 'activity_type' in df_oulad.columns:
340
+ atividades_counts = df_oulad['activity_type'].value_counts().head(6) # Top 6 atividades
341
+ atividades = atividades_counts.index.tolist()
342
+ cliques = atividades_counts.values.tolist()
343
+
344
+ axes[1, 0].barh(atividades, cliques, color='lightsteelblue')
345
+ axes[1, 0].set_title('Distribuição de Atividades por Tipo')
346
+ axes[1, 0].set_xlabel('Número de Registros')
347
+ else:
348
+ axes[1, 0].text(0.5, 0.5, 'Dados de atividades não disponíveis', ha='center', va='center', transform=axes[1, 0].transAxes)
349
+ axes[1, 0].set_title('Distribuição de Atividades por Tipo')
350
+
351
+ # 4. Distribuição por faixa etária
352
+ if 'age_band' in df_oulad.columns:
353
+ idade_counts = df_oulad['age_band'].value_counts()
354
+ faixas_etarias = idade_counts.index.tolist()
355
+ percentuais_idade = (idade_counts / len(df_oulad) * 100).tolist()
356
+ cores_idade = ['lightgreen', 'gold', 'lightcoral']
357
+
358
+ bars = axes[1, 1].bar(faixas_etarias, percentuais_idade, color=cores_idade[:len(faixas_etarias)])
359
+ axes[1, 1].set_title('Distribuição por Faixa Etária')
360
+ axes[1, 1].set_ylabel('Percentual (%)')
361
+ axes[1, 1].tick_params(axis='x', rotation=45)
362
+ for bar, pct in zip(bars, percentuais_idade):
363
+ axes[1, 1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
364
+ f'{pct:.1f}%', ha='center', va='bottom')
365
+ else:
366
+ axes[1, 1].text(0.5, 0.5, 'Dados de idade não disponíveis', ha='center', va='center', transform=axes[1, 1].transAxes)
367
+ axes[1, 1].set_title('Distribuição por Faixa Etária')
368
+
369
+ plt.tight_layout()
370
+ return fig
371
+
372
+ except Exception as e:
373
+ st.warning(f"Erro ao criar gráfico OULAD: {e}")
374
+ return None
375
 
376
  def criar_grafico_comparativo_insights():
377
+ """Cria gráfico comparativo de insights entre os datasets baseado em dados reais"""
378
+ try:
379
+ from .utilidades import carregar_dados_uci_cached, carregar_dados_oulad_cached, obter_metricas_principais_uci, obter_metricas_principais_oulad
380
+
381
+ # Carregar métricas
382
+ metricas_uci = obter_metricas_principais_uci()
383
+ metricas_oulad = obter_metricas_principais_oulad()
384
+
385
+ fig, axes = plt.subplots(1, 3, figsize=(18, 6))
386
+
387
+ # 1. Taxa de aprovação comparativa
388
+ datasets = ['UCI\n(Escolas Públicas)', 'OULAD\n(Online)']
389
+ taxas = [metricas_uci['taxa_aprovacao'], metricas_oulad['taxa_aprovacao']]
390
+ cores = ['lightcoral', 'lightgreen']
391
+
392
+ bars = axes[0].bar(datasets, taxas, color=cores)
393
+ axes[0].set_title('Taxa de Aprovação Comparativa')
394
+ axes[0].set_ylabel('Taxa de Aprovação (%)')
395
+ axes[0].set_ylim(0, 100)
396
+ for bar, taxa in zip(bars, taxas):
397
+ axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
398
+ f'{taxa:.1f}%', ha='center', va='bottom')
399
+
400
+ # 2. Distribuição de gênero comparativa
401
+ if metricas_uci['distribuicao_genero'] and metricas_oulad['distribuicao_genero']:
402
+ # Normalizar para ter os mesmos gêneros
403
+ generos = ['F', 'M'] # Assumindo F e M como padrão
404
+ uci_pct = [metricas_uci['distribuicao_genero'].get('F', 0), metricas_uci['distribuicao_genero'].get('M', 0)]
405
+ oulad_pct = [metricas_oulad['distribuicao_genero'].get('F', 0), metricas_oulad['distribuicao_genero'].get('M', 0)]
406
+
407
+ x = range(len(generos))
408
+ width = 0.35
409
+
410
+ axes[1].bar([i - width/2 for i in x], uci_pct, width, label='UCI', color='lightcoral', alpha=0.8)
411
+ axes[1].bar([i + width/2 for i in x], oulad_pct, width, label='OULAD', color='lightgreen', alpha=0.8)
412
+ axes[1].set_title('Distribuição de Gênero Comparativa')
413
+ axes[1].set_ylabel('Percentual (%)')
414
+ axes[1].set_xlabel('Gênero')
415
+ axes[1].set_xticks(x)
416
+ axes[1].set_xticklabels(generos)
417
+ axes[1].legend()
418
+ else:
419
+ axes[1].text(0.5, 0.5, 'Dados de gênero não disponíveis', ha='center', va='center', transform=axes[1].transAxes)
420
+ axes[1].set_title('Distribuição de Gênero Comparativa')
421
+
422
+ # 3. Comparação de métricas principais
423
+ metricas_comparacao = ['Total de\nEstudantes', 'Taxa de\nAprovação', 'Média de\nNotas/Cliques']
424
+ uci_valores = [metricas_uci['total_estudantes']/1000, metricas_uci['taxa_aprovacao'], metricas_uci['media_nota_final']]
425
+ oulad_valores = [metricas_oulad['total_estudantes']/1000, metricas_oulad['taxa_aprovacao'], metricas_oulad['media_cliques']]
426
+
427
+ x = range(len(metricas_comparacao))
428
+ width = 0.35
429
+
430
+ axes[2].bar([i - width/2 for i in x], uci_valores, width, label='UCI', color='lightcoral', alpha=0.8)
431
+ axes[2].bar([i + width/2 for i in x], oulad_valores, width, label='OULAD', color='lightgreen', alpha=0.8)
432
+ axes[2].set_title('Comparação de Métricas Principais')
433
+ axes[2].set_ylabel('Valores')
434
+ axes[2].set_xlabel('Métricas')
435
+ axes[2].set_xticks(x)
436
+ axes[2].set_xticklabels(metricas_comparacao)
437
+ axes[2].legend()
438
+
439
+ plt.tight_layout()
440
+ return fig
441
+
442
+ except Exception as e:
443
+ st.warning(f"Erro ao criar gráfico comparativo: {e}")
444
+ return None