Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -267,187 +267,301 @@ class ReportGenerator:
|
|
| 267 |
return plt.gcf()
|
| 268 |
|
| 269 |
def create_time_performance_plot(self) -> plt.Figure:
|
| 270 |
-
"""Cria o gráfico de relação entre tempo e acertos com
|
| 271 |
plt.figure(figsize=(15, 10))
|
|
|
|
| 272 |
|
| 273 |
-
# Configuração inicial
|
| 274 |
plt.grid(True, alpha=0.2, linestyle='--')
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
# Função para calcular distância entre pontos
|
| 278 |
-
def calculate_distance(p1, p2):
|
| 279 |
-
return np.sqrt((p1[0] - p2[0])**2 + (p1[1] - p2[1])**2)
|
| 280 |
|
| 281 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
all_points = []
|
|
|
|
| 283 |
|
| 284 |
-
#
|
| 285 |
for nivel, color in self.colors.items():
|
| 286 |
mask = self.data['Nível'] == nivel
|
| 287 |
tempo = pd.to_timedelta(self.data[mask]['Total Tempo']).dt.total_seconds() / 60
|
| 288 |
acertos = self.data[mask]['Acertos Absolutos']
|
| 289 |
|
| 290 |
-
plt.scatter(tempo, acertos, c=color, label=nivel, alpha=0.7, s=100)
|
| 291 |
|
| 292 |
for t, a, nome in zip(tempo, acertos, self.data[mask]['Nome do Aluno']):
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
|
|
|
|
|
|
| 299 |
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
|
|
|
| 306 |
|
| 307 |
-
for
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
|
| 312 |
-
|
| 313 |
-
|
| 314 |
# Adicionar anotações
|
| 315 |
-
for group in
|
| 316 |
if len(group) == 1:
|
| 317 |
x, y, nome, _ = group[0]
|
|
|
|
|
|
|
| 318 |
plt.annotate(
|
| 319 |
nome.split()[0],
|
| 320 |
(x, y),
|
| 321 |
-
xytext=
|
| 322 |
-
textcoords='
|
| 323 |
bbox=dict(
|
| 324 |
facecolor='white',
|
| 325 |
-
edgecolor='
|
| 326 |
-
alpha=0.
|
| 327 |
-
pad=0
|
|
|
|
| 328 |
),
|
| 329 |
-
fontsize=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
)
|
|
|
|
| 331 |
else:
|
| 332 |
-
# Calcular
|
| 333 |
x_center = sum(p[0] for p in group) / len(group)
|
| 334 |
y_center = sum(p[1] for p in group) / len(group)
|
|
|
|
| 335 |
|
| 336 |
-
|
| 337 |
-
nomes = sorted([p[2].split()[0] for p in group])
|
| 338 |
-
|
| 339 |
-
# Determinar direção do deslocamento baseado no quadrante
|
| 340 |
-
x_direction = 30 if x_center < np.mean([p[0] for p in all_points]) else -30
|
| 341 |
-
y_direction = 20 if y_center < np.mean([p[1] for p in all_points]) else -20
|
| 342 |
|
| 343 |
plt.annotate(
|
| 344 |
-
|
| 345 |
(x_center, y_center),
|
| 346 |
-
xytext=
|
| 347 |
-
textcoords='
|
| 348 |
bbox=dict(
|
| 349 |
facecolor='white',
|
| 350 |
edgecolor='lightgray',
|
| 351 |
-
alpha=0.
|
| 352 |
-
pad=0
|
| 353 |
-
boxstyle='round,pad=0.
|
| 354 |
),
|
| 355 |
-
fontsize=
|
| 356 |
arrowprops=dict(
|
| 357 |
arrowstyle='-|>',
|
| 358 |
-
connectionstyle=
|
| 359 |
color='gray',
|
| 360 |
alpha=0.6
|
| 361 |
)
|
| 362 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 363 |
|
| 364 |
-
|
| 365 |
-
plt.
|
| 366 |
-
|
| 367 |
-
plt.legend(
|
| 368 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 369 |
|
|
|
|
| 370 |
return plt.gcf()
|
| 371 |
|
| 372 |
def create_tasks_performance_plot(self) -> plt.Figure:
|
| 373 |
-
"""Cria o gráfico de relação entre tarefas e acertos com
|
| 374 |
plt.figure(figsize=(15, 10))
|
|
|
|
| 375 |
|
| 376 |
# Configuração inicial
|
| 377 |
plt.grid(True, alpha=0.2, linestyle='--')
|
| 378 |
-
|
|
|
|
|
|
|
|
|
|
| 379 |
|
| 380 |
-
# Dicionário para agrupar pontos com mesmas coordenadas
|
| 381 |
point_groups = {}
|
|
|
|
| 382 |
|
| 383 |
-
#
|
| 384 |
for nivel, color in self.colors.items():
|
| 385 |
mask = self.data['Nível'] == nivel
|
| 386 |
tarefas = self.data[mask]['Tarefas Completadas']
|
| 387 |
acertos = self.data[mask]['Acertos Absolutos']
|
| 388 |
|
| 389 |
-
plt.scatter(tarefas, acertos, c=color, label=nivel, alpha=0.7, s=100)
|
| 390 |
|
| 391 |
-
# Agrupar pontos com coordenadas idênticas
|
| 392 |
for t, a, nome in zip(tarefas, acertos, self.data[mask]['Nome do Aluno']):
|
| 393 |
-
|
| 394 |
-
if
|
| 395 |
-
point_groups[
|
| 396 |
-
point_groups[
|
| 397 |
-
|
|
|
|
| 398 |
# Linha de tendência
|
| 399 |
z = np.polyfit(self.data['Tarefas Completadas'],
|
| 400 |
-
|
| 401 |
p = np.poly1d(z)
|
| 402 |
x_range = np.linspace(
|
| 403 |
-
self.data['Tarefas Completadas'].min(),
|
| 404 |
-
self.data['Tarefas Completadas'].max(),
|
| 405 |
100
|
| 406 |
)
|
| 407 |
-
plt.plot(x_range, p(x_range), "--", color='#
|
| 408 |
-
label='Tendência', linewidth=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
|
| 410 |
-
#
|
| 411 |
-
|
| 412 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 413 |
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
|
| 418 |
-
|
| 419 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
color='gray',
|
| 441 |
-
alpha=0.6
|
| 442 |
)
|
| 443 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 |
|
| 445 |
-
|
| 446 |
-
plt.
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
return plt.gcf()
|
| 452 |
|
| 453 |
def generate_graphs(self) -> List[plt.Figure]:
|
|
|
|
| 267 |
return plt.gcf()
|
| 268 |
|
| 269 |
def create_time_performance_plot(self) -> plt.Figure:
|
| 270 |
+
"""Cria o gráfico de relação entre tempo e acertos com visualização otimizada."""
|
| 271 |
plt.figure(figsize=(15, 10))
|
| 272 |
+
ax = plt.gca()
|
| 273 |
|
| 274 |
+
# Configuração inicial com estilo limpo
|
| 275 |
plt.grid(True, alpha=0.2, linestyle='--')
|
| 276 |
+
ax.set_facecolor('#f8f9fa')
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
+
# Linha de média
|
| 279 |
+
media_acertos = self.data['Acertos Absolutos'].mean()
|
| 280 |
+
plt.axhline(y=media_acertos, color='gray', linestyle=':', alpha=0.5,
|
| 281 |
+
label='Média de Acertos')
|
| 282 |
+
|
| 283 |
+
# Estruturas para armazenamento
|
| 284 |
all_points = []
|
| 285 |
+
point_groups = {}
|
| 286 |
|
| 287 |
+
# Plotar pontos e coletar dados
|
| 288 |
for nivel, color in self.colors.items():
|
| 289 |
mask = self.data['Nível'] == nivel
|
| 290 |
tempo = pd.to_timedelta(self.data[mask]['Total Tempo']).dt.total_seconds() / 60
|
| 291 |
acertos = self.data[mask]['Acertos Absolutos']
|
| 292 |
|
| 293 |
+
scatter = plt.scatter(tempo, acertos, c=color, label=nivel, alpha=0.7, s=100)
|
| 294 |
|
| 295 |
for t, a, nome in zip(tempo, acertos, self.data[mask]['Nome do Aluno']):
|
| 296 |
+
point_key = (round(t, 1), a)
|
| 297 |
+
if point_key not in point_groups:
|
| 298 |
+
point_groups[point_key] = []
|
| 299 |
+
point_groups[point_key].append((t, a, nome, color))
|
| 300 |
+
all_points.append((t, a))
|
| 301 |
+
|
| 302 |
+
# Lista para controle de sobreposições
|
| 303 |
+
annotations = []
|
| 304 |
|
| 305 |
+
# Função para calcular melhor posição
|
| 306 |
+
def get_best_position(x, y, existing_annotations):
|
| 307 |
+
angles = np.linspace(0, 2*np.pi, 16) # 16 direções possíveis
|
| 308 |
+
base_radius = 3.0
|
| 309 |
+
max_radius = 6.0
|
| 310 |
+
best_pos = None
|
| 311 |
+
min_overlap = float('inf')
|
| 312 |
|
| 313 |
+
for radius in np.linspace(base_radius, max_radius, 4):
|
| 314 |
+
for angle in angles:
|
| 315 |
+
new_x = x + radius * np.cos(angle)
|
| 316 |
+
new_y = y + radius * np.sin(angle)
|
| 317 |
+
|
| 318 |
+
# Verificar limites do gráfico
|
| 319 |
+
if not (ax.get_xlim()[0] <= new_x <= ax.get_xlim()[1] and
|
| 320 |
+
ax.get_ylim()[0] <= new_y <= ax.get_ylim()[1]):
|
| 321 |
+
continue
|
| 322 |
+
|
| 323 |
+
overlaps = sum(1 for ann in existing_annotations if
|
| 324 |
+
abs(ann[0] - new_x) < 2 and abs(ann[1] - new_y) < 2)
|
| 325 |
+
|
| 326 |
+
if overlaps < min_overlap:
|
| 327 |
+
min_overlap = overlaps
|
| 328 |
+
best_pos = (new_x, new_y)
|
| 329 |
|
| 330 |
+
return best_pos or (x + base_radius, y + base_radius)
|
| 331 |
+
|
| 332 |
# Adicionar anotações
|
| 333 |
+
for key, group in point_groups.items():
|
| 334 |
if len(group) == 1:
|
| 335 |
x, y, nome, _ = group[0]
|
| 336 |
+
new_pos = get_best_position(x, y, annotations)
|
| 337 |
+
|
| 338 |
plt.annotate(
|
| 339 |
nome.split()[0],
|
| 340 |
(x, y),
|
| 341 |
+
xytext=new_pos,
|
| 342 |
+
textcoords='data',
|
| 343 |
bbox=dict(
|
| 344 |
facecolor='white',
|
| 345 |
+
edgecolor='lightgray',
|
| 346 |
+
alpha=0.95,
|
| 347 |
+
pad=1.0,
|
| 348 |
+
boxstyle='round,pad=0.8'
|
| 349 |
),
|
| 350 |
+
fontsize=9,
|
| 351 |
+
arrowprops=dict(
|
| 352 |
+
arrowstyle='-|>',
|
| 353 |
+
connectionstyle='arc3,rad=0.2',
|
| 354 |
+
color='gray',
|
| 355 |
+
alpha=0.6
|
| 356 |
+
)
|
| 357 |
)
|
| 358 |
+
annotations.append(new_pos)
|
| 359 |
else:
|
| 360 |
+
# Calcular centroide do grupo
|
| 361 |
x_center = sum(p[0] for p in group) / len(group)
|
| 362 |
y_center = sum(p[1] for p in group) / len(group)
|
| 363 |
+
nomes = sorted(set([p[2].split()[0] for p in group]))
|
| 364 |
|
| 365 |
+
new_pos = get_best_position(x_center, y_center, annotations)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 366 |
|
| 367 |
plt.annotate(
|
| 368 |
+
f"{len(group)} alunos:\n" + "\n".join(nomes),
|
| 369 |
(x_center, y_center),
|
| 370 |
+
xytext=new_pos,
|
| 371 |
+
textcoords='data',
|
| 372 |
bbox=dict(
|
| 373 |
facecolor='white',
|
| 374 |
edgecolor='lightgray',
|
| 375 |
+
alpha=0.95,
|
| 376 |
+
pad=1.0,
|
| 377 |
+
boxstyle='round,pad=0.8'
|
| 378 |
),
|
| 379 |
+
fontsize=9,
|
| 380 |
arrowprops=dict(
|
| 381 |
arrowstyle='-|>',
|
| 382 |
+
connectionstyle='arc3,rad=0.2',
|
| 383 |
color='gray',
|
| 384 |
alpha=0.6
|
| 385 |
)
|
| 386 |
)
|
| 387 |
+
annotations.append(new_pos)
|
| 388 |
+
|
| 389 |
+
# Configurações finais
|
| 390 |
+
plt.title('Relação entre Tempo e Acertos por Nível', pad=20, fontsize=14)
|
| 391 |
+
plt.xlabel('Tempo Total (minutos)', fontsize=12)
|
| 392 |
+
plt.ylabel('Número de Acertos', fontsize=12)
|
| 393 |
|
| 394 |
+
# Legenda com todos os elementos
|
| 395 |
+
handles, labels = plt.gca().get_legend_handles_labels()
|
| 396 |
+
by_label = dict(zip(labels, handles))
|
| 397 |
+
plt.legend(by_label.values(), by_label.keys(),
|
| 398 |
+
bbox_to_anchor=(1.05, 1),
|
| 399 |
+
loc='upper left',
|
| 400 |
+
borderaxespad=0,
|
| 401 |
+
frameon=True,
|
| 402 |
+
fancybox=True)
|
| 403 |
|
| 404 |
+
plt.tight_layout()
|
| 405 |
return plt.gcf()
|
| 406 |
|
| 407 |
def create_tasks_performance_plot(self) -> plt.Figure:
|
| 408 |
+
"""Cria o gráfico de relação entre tarefas e acertos com visualização otimizada."""
|
| 409 |
plt.figure(figsize=(15, 10))
|
| 410 |
+
ax = plt.gca()
|
| 411 |
|
| 412 |
# Configuração inicial
|
| 413 |
plt.grid(True, alpha=0.2, linestyle='--')
|
| 414 |
+
ax.set_facecolor('#f8f9fa')
|
| 415 |
+
|
| 416 |
+
# Forçar ticks inteiros no eixo x
|
| 417 |
+
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
| 418 |
|
|
|
|
| 419 |
point_groups = {}
|
| 420 |
+
all_points = []
|
| 421 |
|
| 422 |
+
# Plotar pontos e coletar dados
|
| 423 |
for nivel, color in self.colors.items():
|
| 424 |
mask = self.data['Nível'] == nivel
|
| 425 |
tarefas = self.data[mask]['Tarefas Completadas']
|
| 426 |
acertos = self.data[mask]['Acertos Absolutos']
|
| 427 |
|
| 428 |
+
scatter = plt.scatter(tarefas, acertos, c=color, label=nivel, alpha=0.7, s=100)
|
| 429 |
|
|
|
|
| 430 |
for t, a, nome in zip(tarefas, acertos, self.data[mask]['Nome do Aluno']):
|
| 431 |
+
point_key = (t, a)
|
| 432 |
+
if point_key not in point_groups:
|
| 433 |
+
point_groups[point_key] = []
|
| 434 |
+
point_groups[point_key].append((t, a, nome, color))
|
| 435 |
+
all_points.append((t, a))
|
| 436 |
+
|
| 437 |
# Linha de tendência
|
| 438 |
z = np.polyfit(self.data['Tarefas Completadas'],
|
| 439 |
+
self.data['Acertos Absolutos'], 1)
|
| 440 |
p = np.poly1d(z)
|
| 441 |
x_range = np.linspace(
|
| 442 |
+
self.data['Tarefas Completadas'].min() - 0.5,
|
| 443 |
+
self.data['Tarefas Completadas'].max() + 0.5,
|
| 444 |
100
|
| 445 |
)
|
| 446 |
+
plt.plot(x_range, p(x_range), "--", color='#d63031', alpha=0.9,
|
| 447 |
+
label='Tendência', linewidth=2.5)
|
| 448 |
+
|
| 449 |
+
# Linha de média
|
| 450 |
+
media_acertos = self.data['Acertos Absolutos'].mean()
|
| 451 |
+
plt.axhline(y=media_acertos, color='gray', linestyle=':', alpha=0.5,
|
| 452 |
+
label='Média de Acertos')
|
| 453 |
+
|
| 454 |
+
# Lista para controle de sobreposições
|
| 455 |
+
annotations = []
|
| 456 |
|
| 457 |
+
# Função para calcular melhor posição
|
| 458 |
+
def get_best_position(x, y, existing_annotations):
|
| 459 |
+
angles = np.linspace(0, 2*np.pi, 16)
|
| 460 |
+
base_radius = 2.0 # Menor para tarefas discretas
|
| 461 |
+
max_radius = 4.0
|
| 462 |
+
best_pos = None
|
| 463 |
+
min_overlap = float('inf')
|
| 464 |
|
| 465 |
+
for radius in np.linspace(base_radius, max_radius, 4):
|
| 466 |
+
for angle in angles:
|
| 467 |
+
new_x = x + radius * np.cos(angle)
|
| 468 |
+
new_y = y + radius * np.sin(angle)
|
| 469 |
+
|
| 470 |
+
if not (ax.get_xlim()[0] <= new_x <= ax.get_xlim()[1] and
|
| 471 |
+
ax.get_ylim()[0] <= new_y <= ax.get_ylim()[1]):
|
| 472 |
+
continue
|
| 473 |
+
|
| 474 |
+
overlaps = sum(1 for ann in existing_annotations if
|
| 475 |
+
abs(ann[0] - new_x) < 1.5 and abs(ann[1] - new_y) < 1.5)
|
| 476 |
+
|
| 477 |
+
if overlaps < min_overlap:
|
| 478 |
+
min_overlap = overlaps
|
| 479 |
+
best_pos = (new_x, new_y)
|
| 480 |
|
| 481 |
+
return best_pos or (x + base_radius, y + base_radius)
|
| 482 |
+
|
| 483 |
+
# Adicionar anotações
|
| 484 |
+
for key, group in point_groups.items():
|
| 485 |
+
if len(group) == 1:
|
| 486 |
+
x, y, nome, _ = group[0]
|
| 487 |
+
new_pos = get_best_position(x, y, annotations)
|
| 488 |
|
| 489 |
+
plt.annotate(
|
| 490 |
+
nome.split()[0],
|
| 491 |
+
(x, y),
|
| 492 |
+
xytext=new_pos,
|
| 493 |
+
textcoords='data',
|
| 494 |
+
bbox=dict(
|
| 495 |
+
facecolor='white',
|
| 496 |
+
edgecolor='lightgray',
|
| 497 |
+
alpha=0.95,
|
| 498 |
+
pad=1.0,
|
| 499 |
+
boxstyle='round,pad=0.8'
|
| 500 |
+
),
|
| 501 |
+
fontsize=9,
|
| 502 |
+
arrowprops=dict(
|
| 503 |
+
arrowstyle='-|>',
|
| 504 |
+
connectionstyle='arc3,rad=0.2',
|
| 505 |
+
color='gray',
|
| 506 |
+
alpha=0.6
|
| 507 |
+
)
|
|
|
|
|
|
|
| 508 |
)
|
| 509 |
+
annotations.append(new_pos)
|
| 510 |
+
else:
|
| 511 |
+
x_center = sum(p[0] for p in group) / len(group)
|
| 512 |
+
y_center = sum(p[1] for p in group) / len(group)
|
| 513 |
+
nomes = sorted(set([p[2].split()[0] for p in group]))
|
| 514 |
+
|
| 515 |
+
new_pos = get_best_position(x_center, y_center, annotations)
|
| 516 |
+
|
| 517 |
+
plt.annotate(
|
| 518 |
+
f"{len(group)} alunos com\n{group[0][0]:.0f} tarefas:\n" + "\n".join(nomes),
|
| 519 |
+
(x_center, y_center),
|
| 520 |
+
xytext=new_pos,
|
| 521 |
+
textcoords='data',
|
| 522 |
+
bbox=dict(
|
| 523 |
+
facecolor='white',
|
| 524 |
+
edgecolor='lightgray',
|
| 525 |
+
alpha=0.95,
|
| 526 |
+
pad=1.0,
|
| 527 |
+
boxstyle='round,pad=0.8'
|
| 528 |
+
),
|
| 529 |
+
fontsize=9,
|
| 530 |
+
arrowprops=dict(
|
| 531 |
+
arrowstyle='-|>',
|
| 532 |
+
connectionstyle='arc3,rad=0.2',
|
| 533 |
+
color='gray',
|
| 534 |
+
alpha=0.6
|
| 535 |
+
)
|
| 536 |
+
)
|
| 537 |
+
annotations.append(new_pos)
|
| 538 |
+
|
| 539 |
+
# Configurações finais
|
| 540 |
+
plt.title('Relação entre Tarefas Completadas e Acertos', pad=20, fontsize=14)
|
| 541 |
+
plt.xlabel('Número de Tarefas Completadas', fontsize=12)
|
| 542 |
+
plt.ylabel('Número de Acertos', fontsize=12)
|
| 543 |
|
| 544 |
+
# Ajustar limites
|
| 545 |
+
plt.xlim(
|
| 546 |
+
self.data['Tarefas Completadas'].min() - 1,
|
| 547 |
+
self.data['Tarefas Completadas'].max() + 1
|
| 548 |
+
)
|
| 549 |
+
plt.ylim(
|
| 550 |
+
max(0, self.data['Acertos Absolutos'].min() - 1),
|
| 551 |
+
self.data['Acertos Absolutos'].max() + 2
|
| 552 |
+
)
|
| 553 |
|
| 554 |
+
# Legenda com todos os elementos
|
| 555 |
+
handles, labels = plt.gca().get_legend_handles_labels()
|
| 556 |
+
by_label = dict(zip(labels, handles))
|
| 557 |
+
plt.legend(by_label.values(), by_label.keys(),
|
| 558 |
+
bbox_to_anchor=(1.05, 1),
|
| 559 |
+
loc='upper left',
|
| 560 |
+
borderaxespad=0,
|
| 561 |
+
frameon=True,
|
| 562 |
+
fancybox=True)
|
| 563 |
+
|
| 564 |
+
plt.tight_layout()
|
| 565 |
return plt.gcf()
|
| 566 |
|
| 567 |
def generate_graphs(self) -> List[plt.Figure]:
|