Pular para conteúdo

1. Data

Enunciado

Exercises → Data

Este arquivo é o modelo de relatório

A estrutura de títulos abaixo espelha o enunciado: ## Exercise N para cada exercício, ### A, ### B, ... para cada item. Mantenha essa ordem — a correção percorre o relatório procurando por ela. Apague os blocos de instrução (como este) conforme for preenchendo.

Exercise 1

Abordagem

Descreva em 3–5 linhas o que foi feito e por quê: como os dados foram gerados, quais parâmetros do enunciado foram usados e qual semente aleatória garante a reprodutibilidade.

Código

O script vive em code/exercise1_point_clouds.py e é incluído aqui pelo próprio arquivo — nunca copie e cole o texto do código, use a inclusão para que relatório e repositório nunca fiquem fora de sincronia.

docs/exercises/data/code/exercise1_point_clouds.py
"""Exercise 1 — Exploring class separability in 2D.

Gera as 4 classes gaussianas do enunciado, salva a figura em ``figures/`` e
imprime as métricas que alimentam a tabela *Results summary* do relatório.

Uso (a partir da raiz do repositório):

    python docs/exercises/data/code/exercise1_point_clouds.py
"""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np

FIGURES = Path(__file__).resolve().parents[1] / "figures"
RNG = np.random.default_rng(42)  # (1)!

CLASSES = {
    0: {"mean": [2.0, 3.0], "std": [0.8, 2.5]},
    1: {"mean": [5.0, 6.0], "std": [1.2, 1.9]},
    2: {"mean": [8.0, 1.0], "std": [0.9, 0.9]},
    3: {"mean": [15.0, 4.0], "std": [0.5, 2.0]},
}
N_PER_CLASS = 100


def generate(scale: float = 1.0) -> tuple[np.ndarray, np.ndarray]:
    """Amostra 100 pontos por classe, com os desvios multiplicados por ``scale``."""
    xs, ys = [], []
    for label, params in CLASSES.items():
        mean = np.asarray(params["mean"])
        std = np.asarray(params["std"]) * scale
        xs.append(RNG.normal(mean, std, size=(N_PER_CLASS, 2)))
        ys.append(np.full(N_PER_CLASS, label))
    return np.vstack(xs), np.concatenate(ys)


def separation_ratio(X: np.ndarray, y: np.ndarray) -> float:
    """Distância média entre centróides dividida pela dispersão média intraclasse."""
    centroids = np.stack([X[y == c].mean(axis=0) for c in CLASSES])
    spreads = np.array([np.linalg.norm(X[y == c] - centroids[c], axis=1).mean() for c in CLASSES])
    pairwise = [
        np.linalg.norm(centroids[i] - centroids[j])
        for i in CLASSES
        for j in CLASSES
        if i < j
    ]
    return float(np.mean(pairwise) / spreads.mean())


def main() -> None:
    FIGURES.mkdir(parents=True, exist_ok=True)

    X, y = generate()
    fig, ax = plt.subplots(figsize=(7, 5))
    for c in CLASSES:
        ax.scatter(*X[y == c].T, s=14, alpha=0.75, label=f"Classe {c}")
    ax.set_xlabel("$x_1$")
    ax.set_ylabel("$x_2$")
    ax.set_title("Nuvens de pontos gaussianas (scale = 1.0)")
    ax.legend(loc="upper left")
    fig.tight_layout()
    fig.savefig(FIGURES / "fig01-point-clouds.png", dpi=150)
    plt.close(fig)  # (2)!

    for scale in (0.5, 1.0, 2.0):
        Xs, ys = generate(scale)
        print(f"scale={scale:>4} | separation ratio = {separation_ratio(Xs, ys):.3f}")


if __name__ == "__main__":
    main()
  1. Semente fixa: sem ela, os números da tabela de resultados mudam a cada execução e a correção não consegue reproduzir o seu relatório.
  2. plt.close(fig) evita o vazamento de figuras quando o script gera várias em sequência.

Figuras

Nuvens de pontos das quatro classes gaussianas

Figura 1 — Dispersão das quatro classes no plano \((x_1, x_2)\) com scale = 1.0.

Análise

Responda às perguntas do enunciado citando os números que você mediu. Uma resposta do tipo "as classes ficam mais misturadas" vale pouco; "o separation ratio cai de 4.13 para 1.02 quando scale vai de 0.5 para 2.0, e a taxa de mistura sobe de 0.4% para 18.7%" vale.

Exercise 2

Abordagem

Código

Figuras

Análise

Fronteiras não lineares

Para justificar por que as cascas concêntricas exigem fronteira não linear, ajuda escrever a condição de decisão. Um separador linear é

\[ f(\mathbf{x}) = \mathbf{w}^\top \mathbf{x} + b, \]

enquanto a estrutura das cascas depende de \(\lVert \mathbf{x} - \boldsymbol{\mu} \rVert\), que não é expressável nessa forma.

Exercise 3

Abordagem

Código

Figuras

Análise

Vazamento de dados

O train_test_split vem antes de qualquer imputação, encoding ou escalonamento. Ajuste os transformadores só no treino e aplique-os ao teste.

flowchart LR
    raw[Dados brutos] --> split{{train_test_split}}
    split -->|treino| fit[fit_transform]
    split -->|teste| apply[transform]
    fit --> model[Modelo]
    apply --> model

Results summary

Preencha todas as linhas — linha em branco é lida como exercício incompleto.

# Métrica Valor
1 Separation ratio (scale = 0.5)
2 Separation ratio (scale = 1.0)
3 Separation ratio (scale = 2.0)
4 Taxa de mistura (scale = 1.0)
5 Distância entre centros — gaussianas 5D
6 Variância explicada — PC1 + PC2
7 Raio médio — casca interna
8 Raio médio — casca externa
9 Amostras de treino após o split
10 Amostras de teste após o split
11 Colunas com valores ausentes
12 Features após o encoding
13 Faixa das features após o escalonamento

Discussão

O que foi difícil? Onde a intuição falhou? Que decisão você tomaria diferente?

Conclusão

O que este exercício mostrou sobre a relação entre distribuição dos dados e a complexidade da fronteira de decisão que a rede precisa aprender?