6.10. Broadcasting

Quando um operador binário recebe dois arrays cujas formas não correspondem exatamente, o numpy não levanta erro – faz broadcasting. O broadcasting é um pequeno conjunto de regras que decide se duas formas são compatíveis e, em caso afirmativo, como a menor é virtualmente esticada para corresponder à maior.

6.10.1. As regras

Quando os dois operandos têm formas A e B, o numpy percorre-os em dois passos.

  1. Igualar as dimensões. Se um operando tiver menos eixos do que o outro, o numpy preenche virtualmente a frente da sua forma com eixos de tamanho 1 até ambas as formas terem o mesmo número de eixos. Um operando 1-D de forma (3,) emparelhado com um operando 2-D de forma (2, 3) torna-se (1, 3) contra (2, 3).

  2. Verificar cada eixo. Percorrendo as formas de igual comprimento eixo a eixo, 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 para 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 corresponde (3 == 3); o resultado tem a forma (2, 3).

Se algum par de eixos não satisfizer nenhuma das condições, as formas são incompatíveis e o operador levanta ValueError.

6.10.2. Exemplos

Escalar contra qualquer array. O escalar age como forma (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 através de uma matriz 2-D. A regra 1 acrescenta um eixo de tamanho 1 para transformar (3,) em (1, 3); a regra 2 estica essa linha ao longo de 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 comprimento igual somam elemento a elemento – não é necessário broadcasting:

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

Vetor coluna contra um vetor linha produz uma forma 2-D «exterior»: (4, 1) emparelhado com (3,) torna-se (4, 1) contra (1, 3) após o acréscimo de dimensão, e a regra 2 estica cada operando ao longo do 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 forma aplicam-se 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 estiramento é virtual. O numpy percorre ambos os operandos em conjunto, relendo o menor ao longo do seu eixo de broadcast em vez de o copiar. Os dados do array mais curto nunca são replicados em memória.

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

6.10.4. Quando o broadcasting corre mal

O falhanço clássico ocorre com duas formas em que nenhuma tem um eixo de tamanho 1 para esticar e os tamanhos discordam – (3, 4) contra (4, 3), por exemplo. A regra 2 não consegue fazer corresponder um 3 a um 4, pelo que o numpy levanta ValueError.

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