Pular para o conteúdo

    Capítulo 19, Intermediário

    Juntar, empilhar e dividir

    Dados raramente chegam em um bloco só. Eles vêm em pedaços que você precisa juntar, e depois você precisa separar em treino e teste, em lotes, em grupos.

    Código deste capítulo: intermediario/cap19_juntar_dividir.py

    Juntar ao longo de um eixo

    O np.concatenate une arrays ao longo de um eixo que já existe. Todas as outras dimensões precisam ser iguais:

    intermediario/cap19_juntar_dividir.pylinhas 10 a 15
    import numpy as np
    
    a = np.array([[1, 2], [3, 4]])
    b = np.array([[5, 6]])
    print(np.concatenate([a, b], axis=0))
    print(np.concatenate([a, b.T], axis=1))
    
    Saída
    [[1 2]
     [3 4]
     [5 6]]
    [[1 2 5]
     [3 4 6]]
    

    Quando as dimensões não batem, o erro diz exatamente qual:

    intermediario/cap19_juntar_dividir.pylinhas 17 a 20
    try:
        np.concatenate([a, b], axis=1)
    except ValueError as erro:
        print(erro)
    
    Saída
    all the input array dimensions except for the concatenation axis must match exactly, but along dimension 0, the array at index 0 has size 2 and the array at index 1 has size 1
    

    Empilhar em um eixo novo

    O np.stack cria um eixo novo e coloca cada array em uma posição dele. É o que você quer para juntar vários vetores de mesma forma em uma matriz, ou várias imagens em um lote. Os atalhos vstack e hstack empilham na vertical e na horizontal:

    intermediario/cap19_juntar_dividir.pylinhas 25 a 28
    x = np.array([1, 2, 3])
    y = np.array([4, 5, 6])
    print(np.stack([x, y]).shape, np.stack([x, y], axis=1).shape)
    print(np.vstack([x, y]), np.hstack([x, y]))
    
    Saída
    (2, 3) (3, 2)
    [[1 2 3]
     [4 5 6]] [1 2 3 4 5 6]
    
    FunçãoO que fazForma de x e y com 3 elementos cada
    np.concatenate([x, y])Une em um eixo existente(6,)
    np.stack([x, y])Cria um eixo novo, na frente(2, 3)
    np.stack([x, y], axis=1)Cria o eixo novo no meio(3, 2)
    np.vstack([x, y])Empilha como linhas(2, 3)
    np.hstack([x, y])Une lado a lado(6,)

    Dividir

    O np.split divide em partes iguais (e reclama se não der), o np.array_split aceita partes de tamanhos diferentes, e uma lista de posições diz onde cortar:

    intermediario/cap19_juntar_dividir.pylinhas 33 a 36
    dados = np.arange(10)
    print(np.split(dados, 5))
    print(np.array_split(dados, 3))
    print(np.split(dados, [2, 7]))
    
    Saída
    [array([0, 1]), array([2, 3]), array([4, 5]), array([6, 7]), array([8, 9])]
    [array([0, 1, 2, 3]), array([4, 5, 6]), array([7, 8, 9])]
    [array([0, 1]), array([2, 3, 4, 5, 6]), array([7, 8, 9])]
    

    O uso mais comum é separar treino e teste: embaralhe as posições (capítulo 16) e corte. Embaralhar antes é essencial, porque dados guardados em ordem (por data, por classe) dariam um teste enviesado:

    intermediario/cap19_juntar_dividir.pylinhas 38 a 41
    rng = np.random.default_rng(0)
    ordem = rng.permutation(10)
    treino, teste = np.split(ordem, [8])
    print(len(treino), len(teste), sorted(np.concatenate([treino, teste]).tolist()) == list(range(10)))
    
    Saída
    8 2 True
    

    Repetir

    intermediario/cap19_juntar_dividir.pylinha 46
    print(np.repeat([1, 2], 3), np.tile([1, 2], 3))
    
    Saída
    [1 1 1 2 2 2] [1 2 1 2 1 2]
    

    O repeat repete cada elemento, e o tile repete o bloco inteiro.

    O `np.append` em laço é uma armadilha

    Cada np.append cria um array novo, copiando tudo o que já existia. Em um laço, isso custa tempo proporcional ao quadrado do tamanho. O padrão certo é acumular em uma lista (que cresce barato) e converter uma vez no fim:

    intermediario/cap19_juntar_dividir.pylinhas 51 a 68
    import timeit
    
    
    def com_append(n):
        a = np.array([])
        for i in range(n):
            a = np.append(a, i)
        return a
    
    
    def com_lista(n):
        return np.array([i for i in range(n)], dtype=float)
    
    
    print(np.array_equal(com_append(500), com_lista(500)))
    t_append = min(timeit.repeat(lambda: com_append(3000), number=1, repeat=3))
    t_lista = min(timeit.repeat(lambda: com_lista(3000), number=1, repeat=3))
    print("a lista foi pelo menos 5 vezes mais rápida:", t_append > 5 * t_lista)
    
    Saída
    True
    a lista foi pelo menos 5 vezes mais rápida: True
    

    Exercício 1

    Adicionar a coluna de uns

    Escreva adicionar_vies(X) que acrescente uma coluna de uns no início de uma matriz de características X. É o passo padrão antes de ajustar uma regressão linear com intercepto.