6.10. Broadcasting

Quando um operador binário recebe dois arrays cujos formatos não coincidem exatamente, o numpy não lança erro – ele faz broadcasting. O broadcasting é um pequeno conjunto de regras que decidem se dois formatos são compatíveis e, se forem, como o menor é virtualmente esticado para coincidir com o maior.

6.10.1. As regras

Quando os dois operandos têm formatos A e B, o numpy os processa em duas etapas.

  1. Igualar as ordens. Se um operando tiver menos eixos do que o outro, o numpy preenche virtualmente o início de seu formato com eixos de tamanho 1 até que ambos os formatos tenham o mesmo número de eixos. Um operando 1-D de formato (3,) emparelhado com um operando 2-D de formato (2, 3) torna-se (1, 3) contra (2, 3).

  2. Verificar cada eixo. Percorrendo eixo a eixo os formatos agora de mesmo comprimento, cada par de tamanhos deve satisfazer uma de duas condições: os tamanhos são iguais ou um deles é 1. Um eixo de tamanho 1 é virtualmente esticado para o tamanho do outro lado durante a operação. O par (1, 3) contra (2, 3) é compatível porque o primeiro eixo tem um 1 (estica para 2) e o segundo eixo coincide (3 == 3); o resultado tem formato (2, 3).

Se algum par de eixos não satisfizer nenhuma das condições, os formatos são incompatíveis e o operador lança ValueError.

6.10.2. Exemplos

Escalar contra qualquer array. O escalar age como se tivesse formato (1,) e estica para qualquer coisa:

a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float)
a + 10                 # (2, 3) + scalar -> (2, 3)

Vetor 1-D sobre uma matriz 2-D. A regra 1 antepõe um eixo de tamanho 1 para transformar (3,) em (1, 3); a regra 2 então estica essa linha por cada coluna de a

row = np.array([100, 200, 300], dtype=np.float)
a + row                # (2, 3) + (3,) -> (2, 3)

Dois arrays 1-D de mesmo comprimento somam elemento a elemento – sem necessidade de broadcasting:

np.arange(4) + np.arange(4)

Vetor coluna contra um vetor linha produz um formato “externo” 2-D: (4, 1) emparelhado com (3,) torna-se (4, 1) contra (1, 3) após a anteposição de ordem, e a regra 2 estica cada operando ao longo de seu eixo de tamanho 1:

x = np.array([1, 2, 3, 4]).reshape((4, 1))     # column
y = np.array([10, 20, 30])                      # row
x + y                                           # (4, 3) matrix

As mesmas regras de formato se aplicam a qualquer ufunc de dois argumentos, incluindo arctan2()

np.arctan2(y, 1.0)
np.arctan2(y, x)

6.10.3. O que o broadcasting não aloca

O esticamento é virtual. O numpy percorre ambos os operandos juntos, relendo o menor ao longo de seu eixo de broadcast em vez de copiá-lo. Os dados do array mais curto nunca são replicados na memória.

O tamanho do array de saída é o que importa para a memória. a + row aloca uma saída com o formato de a, não com o formato de a mais o formato de row. Cadeias longas de broadcasting ainda podem produzir grandes resultados intermediários.

6.10.4. Quando o broadcasting dá errado

A falha clássica são dois formatos em que nenhum tem um eixo de tamanho 1 para esticar e os tamanhos divergem – (3, 4) contra (4, 3), por exemplo. A regra 2 não consegue casar um 3 com um 4, então o numpy lança ValueError.

Um problema mais sutil é um broadcasting que tem sucesso, mas não da maneira que a aplicação pretendia. (5,) contra (5, 1) é o caso canônico: a anteposição de ordem transforma o (5,) em (1, 5), que faz broadcasting contra (5, 1) para produzir uma matriz (5, 5) – a combinação externa dos dois vetores, e não o resultado elemento a elemento de comprimento 5 que a aplicação provavelmente queria. Em caso de dúvida, imprima shape em ambos os lados e percorra as regras antes de recorrer a reshape() ou transpose().