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:
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))
[[1 2]
[3 4]
[5 6]]
[[1 2 5]
[3 4 6]]
Quando as dimensões não batem, o erro diz exatamente qual:
try:
np.concatenate([a, b], axis=1)
except ValueError as erro:
print(erro)
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:
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]))
(2, 3) (3, 2)
[[1 2 3]
[4 5 6]] [1 2 3 4 5 6]
| Função | O que faz | Forma 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:
dados = np.arange(10)
print(np.split(dados, 5))
print(np.array_split(dados, 3))
print(np.split(dados, [2, 7]))
[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:
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)))
8 2 True
Repetir
print(np.repeat([1, 2], 3), np.tile([1, 2], 3))
[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:
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)
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.