Broadcasting do NumPy para Computação Eficiente

NumPyBeginner
Pratique Agora

Introdução

Broadcasting é um conceito fundamental no NumPy que permite operações aritméticas elemento a elemento em arrays de diferentes formas. Este recurso poderoso elimina a necessidade de loops explícitos, resultando em código mais conciso e computacionalmente eficiente. Neste laboratório, você aprenderá as regras de broadcasting e as aplicará a exemplos práticos escrevendo e executando scripts Python.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível iniciante com uma taxa de conclusão de 97%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Transmitindo um Escalar para um Array

A forma mais simples de broadcasting ocorre quando você realiza uma operação entre um array e um único número (um escalar). O NumPy "estica" ou "transmite" automaticamente o escalar para corresponder à forma do array.

Primeiro, localize o arquivo broadcasting.py no explorador de arquivos no lado esquerdo da sua tela. Dê um duplo clique nele para abri-lo no editor.

Agora, substitua o conteúdo de broadcasting.py pelo seguinte código. Este código cria um array NumPy 1D e o multiplica por um valor escalar.

import numpy as np

## Create a 1D array
a = np.array([1.0, 2.0, 3.0])

## Define a scalar
b = 2.0

## Multiply the array by the scalar
## The scalar 'b' is broadcast to the shape of 'a'
result = a * b

print("Array 'a':", a)
print("Scalar 'b':", b)
print("Result of a * b:", result)

Para ver o resultado, você precisa executar o script. Abra um terminal clicando no ícone + no painel do terminal na parte inferior da tela e selecionando Terminal. Em seguida, execute o seguinte comando:

python broadcasting.py

Você deverá ver a seguinte saída, onde cada elemento do array a foi multiplicado por 2.0.

Array 'a': [1. 2. 3.]
Scalar 'b': 2.0
Result of a * b: [2. 4. 6.]

Transmitindo Dois Arrays com Formas Compatíveis

O broadcasting também funciona entre dois arrays se suas formas forem compatíveis. A regra geral para compatibilidade é: ao comparar as dimensões de dois arrays da direita para a esquerda, cada par de dimensões deve ser igual ou uma delas deve ser 1.

Vamos ver um exemplo de adição de um array 1D a um array 2D. O array 1D será transmitido para cada linha do array 2D.

Atualize seu arquivo broadcasting.py com o seguinte código:

import numpy as np

## Create a 2D array (shape: 2, 3)
a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

## Create a 1D array (shape: 3,)
b = np.array([10.0, 20.0, 30.0])

## Add the two arrays
## 'b' is broadcast to shape (2, 3) to match 'a'
## It becomes [[10. 20. 30.], [10. 20. 30.]] internally
result = a + b

print("Array 'a' (shape {}):\n{}".format(a.shape, a))
print("Array 'b' (shape {}): {}".format(b.shape, b))
print("Result of a + b:\n", result)

Execute o script novamente no terminal:

python broadcasting.py

A saída mostra que o array 1D b foi adicionado a cada linha do array 2D a.

Array 'a' (shape (2, 3)):
[[1. 2. 3.]
 [4. 5. 6.]]
Array 'b' (shape (3,)): [10. 20. 30.]
Result of a + b:
 [[11. 22. 33.]
 [14. 25. 36.]]

Compreendendo Formas Incompatíveis

O broadcasting falhará se as formas dos arrays não forem compatíveis de acordo com as regras. Isso resulta em um ValueError. Compreender quando isso acontece é crucial para depuração.

Vamos tentar uma operação com formas incompatíveis. Aqui, tentaremos adicionar um array de forma (2, 3) com um array de forma (2,). O NumPy compara as dimensões finais (3 e 2), descobre que elas não são iguais e nenhuma delas é 1. Isso causará um erro.

Modifique seu arquivo broadcasting.py para conter o seguinte código:

import numpy as np

## Create a 2D array (shape: 2, 3)
a = np.array([[1.0, 2.0, 3.0],
              [4.0, 5.0, 6.0]])

## Create an incompatible 1D array (shape: 2,)
b = np.array([1.0, 2.0])

print("Array 'a' (shape {}):\n{}".format(a.shape, a))
print("Array 'b' (shape {}): {}".format(b.shape, b))

## This will raise a ValueError
try:
    result = a + b
except ValueError as e:
    print("\nError:", e)

Execute o script a partir do terminal:

python broadcasting.py

Como esperado, o programa captura um ValueError e imprime uma mensagem de erro explicando que as formas não estão alinhadas. Este é o comportamento correto e esperado para formas incompatíveis.

Array 'a' (shape (2, 3)):
[[1. 2. 3.]
 [4. 5. 6.]]
Array 'b' (shape (2,)): [1. 2.]

Error: operands could not be broadcast together with shapes (2,3) (2,)

Exemplo Prático - Quantização Vetorial

Vamos aplicar o broadcasting a um problema prático. A Quantização Vetorial (VQ) é uma técnica usada em compressão de dados e classificação. Um passo fundamental é encontrar o vetor "código" mais próximo de um "codebook" para um dado vetor de "observação". O broadcasting torna este cálculo eficiente.

Imagine que temos uma observação (por exemplo, peso e altura de um atleta) e um codebook de diferentes tipos de atletas. Queremos descobrir a qual tipo nossa observação é mais próxima.

Substitua o código em broadcasting.py pelo seguinte:

import numpy as np

## An observation vector (e.g., weight, height)
observation = np.array([111.0, 188.0])

## A codebook of vectors
codes = np.array([[102.0, 203.0],
                  [132.0, 193.0],
                  [45.0, 155.0],
                  [57.0, 173.0]])

## Use broadcasting to subtract the observation from all codes at once
## observation (2,) is broadcast to (4, 2)
diff = codes - observation

## Calculate the squared Euclidean distance
dist_sq = np.sum(diff**2, axis=-1)

## Find the index of the minimum distance
closest_index = np.argmin(dist_sq)

## Get the closest code from the codebook
closest_code = codes[closest_index]

print("Observation:", observation)
print("Codebook:\n", codes)
print("Distances squared:", dist_sq)
print("Closest code index:", closest_index)
print("Closest code:", closest_code)

Execute o script no seu terminal:

python broadcasting.py

A saída mostrará as distâncias ao quadrado da observação para cada código e identificará o vetor código correspondente mais próximo. Todo este cálculo foi realizado sem nenhum loop explícito em Python, graças ao broadcasting.

Observation: [111. 188.]
Codebook:
 [[102. 203.]
 [132. 193.]
 [ 45. 155.]
 [ 57. 173.]]
Distances squared: [ 306.  466. 5445. 3141.]
Closest code index: 0
Closest code: [102. 203.]

Resumo

Neste laboratório, você aprendeu os fundamentos do broadcasting do NumPy. Começou com o caso simples de transmitir um escalar para um array, passou para o broadcasting entre dois arrays compatíveis e aprendeu a reconhecer formas incompatíveis que geram erros. Finalmente, aplicou o broadcasting a um problema prático de quantização vetorial, demonstrando seu poder para escrever código limpo, eficiente e sem loops para computações numéricas. Dominar o broadcasting é um passo fundamental para escrever código NumPy eficaz e profissional.