NumPy: O que É e Como Funciona | Python Brasil
NumPy é a biblioteca fundamental para computação numérica em Python. Aprenda arrays, broadcasting, vetorização e como usar NumPy na prática.
O que é NumPy?
O NumPy (Numerical Python) é a biblioteca fundamental para computação numérica em Python. Ela fornece suporte a arrays multidimensionais de alta performance e uma vasta coleção de funções matemáticas para operar sobre esses arrays de forma eficiente.
Praticamente toda biblioteca de ciência de dados em Python — como Pandas, scikit-learn, SciPy e TensorFlow — é construída sobre o NumPy ou depende de suas estruturas de dados. Entender o NumPy é, portanto, um pré-requisito para trabalhar bem com o ecossistema científico do Python.
Por que usar NumPy em vez de listas Python?
A principal vantagem do NumPy é a velocidade e eficiência de memória. Enquanto listas Python são flexíveis (podem misturar tipos diferentes), elas armazenam referências a objetos espalhados pela memória. Arrays NumPy são implementados em C e armazenam dados em blocos de memória contíguos, todos do mesmo tipo, o que permite operações vetorizadas ordens de grandeza mais rápidas.
import numpy as np
import time
tamanho = 1_000_000
# Com lista Python
lista = list(range(tamanho))
inicio = time.perf_counter()
resultado_lista = [x * 2 for x in lista]
tempo_lista = time.perf_counter() - inicio
# Com NumPy
array = np.arange(tamanho)
inicio = time.perf_counter()
resultado_numpy = array * 2
tempo_numpy = time.perf_counter() - inicio
print(f"Lista Python: {tempo_lista:.4f}s")
print(f"NumPy: {tempo_numpy:.4f}s")
print(f"NumPy é {tempo_lista / tempo_numpy:.1f}x mais rápido")
# NumPy costuma ser 10x a 50x mais rápido nesse cenário
Funções de criação de arrays
O NumPy oferece diversas funções para criar arrays de formas específicas, sem precisar construí-los manualmente:
import numpy as np
# np.zeros: array preenchido com zeros
zeros = np.zeros((3, 4)) # Matriz 3x4 de zeros (float64 por padrão)
zeros_int = np.zeros((2, 3), dtype=int) # Inteiros
# np.ones: array preenchido com uns
uns = np.ones((2, 5)) # Matriz 2x5 de uns
# np.arange: semelhante ao range() do Python
seq = np.arange(0, 10, 2) # [0, 2, 4, 6, 8]
seq_float = np.arange(0.0, 1.0, 0.25) # [0.0, 0.25, 0.5, 0.75]
# np.linspace: N pontos igualmente espaçados entre dois valores
linear = np.linspace(0, 1, 5) # [0.0, 0.25, 0.5, 0.75, 1.0]
graus = np.linspace(0, 360, 7) # 7 ângulos de 0 a 360 graus
# np.eye: matriz identidade
identidade = np.eye(3) # Matriz 3x3 identidade
# np.full: array preenchido com um valor constante
constante = np.full((3, 3), 7) # Matriz 3x3 com valor 7
# np.empty: array sem inicialização (mais rápido, mas conteúdo imprevisível)
vazio = np.empty((2, 2))
print(linear) # [0. 0.25 0.5 0.75 1. ]
print(identidade)
# [[1. 0. 0.]
# [0. 1. 0.]
# [0. 0. 1.]]
Indexação, fatiamento e indexação avançada
A indexação em arrays NumPy é mais poderosa do que em listas Python:
import numpy as np
matriz = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# Indexação básica (linha, coluna)
print(matriz[0, 2]) # 3 (primeira linha, terceira coluna)
print(matriz[-1, -1]) # 12 (ultima linha, ultima coluna)
# Fatiamento (slicing)
print(matriz[0, :]) # [1 2 3 4] - primeira linha inteira
print(matriz[:, 1]) # [2 6 10] - segunda coluna inteira
print(matriz[1:, 2:]) # submatriz das linhas 1-2, colunas 2-3
# Fancy indexing: indexação com arrays de indices
indices = np.array([0, 2])
print(matriz[indices]) # Linhas 0 e 2
# Indexação booleana (filtros)
arr = np.array([1, 5, 3, 8, 2, 9, 4])
maiores_que_4 = arr[arr > 4] # [5 8 9]
pares = arr[arr % 2 == 0] # [8 2 4]
# Combinando condições
filtro = arr[(arr > 2) & (arr < 8)] # [5 3 4]
Broadcasting: operações entre arrays de formas diferentes
O broadcasting é um dos conceitos mais poderosos do NumPy. Ele permite realizar operações entre arrays de formas diferentes, seguindo regras específicas:
import numpy as np
# Escalar com array: o escalar é "transmitido" para todos os elementos
arr = np.array([1, 2, 3, 4, 5])
print(arr + 10) # [11 12 13 14 15]
print(arr * 3) # [3 6 9 12 15]
# Array 1D com matriz 2D
matriz = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
linha = np.array([10, 20, 30]) # Shape: (3,)
print(matriz + linha)
# [[11 22 33]
# [14 25 36]
# [17 28 39]]
# Normalização de dados (padrão comum em ML)
dados = np.random.rand(100, 4) # 100 amostras, 4 features
media = dados.mean(axis=0) # Média por coluna: shape (4,)
desvio = dados.std(axis=0) # Desvio por coluna: shape (4,)
normalizado = (dados - media) / desvio # Broadcasting automático
# Regra: dimensões são compatíveis se forem iguais ou uma delas for 1
a = np.ones((3, 1)) # Shape: (3, 1)
b = np.ones((1, 4)) # Shape: (1, 4)
c = a + b # Resultado: shape (3, 4)
Módulo numpy.random
import numpy as np
rng = np.random.default_rng(seed=42) # Gerador moderno (recomendado)
# Distribuições comuns
uniforme = rng.random((3, 3)) # Uniforme [0, 1)
normal = rng.normal(loc=0, scale=1, size=1000) # Normal padrão
inteiros = rng.integers(1, 101, size=10) # Inteiros entre 1 e 100
escolha = rng.choice([10, 20, 30, 40], size=5) # Amostragem aleatória
# Embaralhar array
arr = np.arange(10)
rng.shuffle(arr)
print(arr) # Ordem aleatória
# Simulação de Monte Carlo
lancamentos = rng.integers(1, 7, size=100_000) # 100k lançamentos de dado
prob_seis = (lancamentos == 6).mean()
print(f"Probabilidade de sair 6: {prob_seis:.3f}") # ~0.167
Layout de memória: ordem C vs Fortran
O NumPy permite controlar como os dados são armazenados na memória, o que impacta a performance de certas operações:
import numpy as np
# Ordem C (padrão): row-major - linha por linha
arr_c = np.array([[1, 2, 3], [4, 5, 6]], order='C')
# Ordem Fortran: column-major - coluna por coluna
arr_f = np.array([[1, 2, 3], [4, 5, 6]], order='F')
print(arr_c.flags['C_CONTIGUOUS']) # True
print(arr_f.flags['F_CONTIGUOUS']) # True
# Verificar e converter
arr = np.ones((1000, 1000))
arr_fortran = np.asfortranarray(arr) # Converte para Fortran
arr_c = np.ascontiguousarray(arr_f) # Converte para C (row-major)
Arrays estruturados
Arrays estruturados permitem armazenar dados heterogêneos com campos nomeados, semelhante a uma tabela:
import numpy as np
# Definindo o tipo estruturado
dtype_pessoa = np.dtype([
('nome', 'U20'), # String unicode de até 20 chars
('idade', 'i4'), # Inteiro de 32 bits
('salario', 'f8'), # Float de 64 bits
])
pessoas = np.array([
('Ana', 28, 8500.0),
('Bruno', 34, 12000.0),
('Carla', 25, 7200.0),
], dtype=dtype_pessoa)
# Acessando campos por nome
print(pessoas['nome']) # ['Ana' 'Bruno' 'Carla']
print(pessoas['salario']) # [8500. 12000. 7200.]
# Filtrando como array normal
acima_de_30 = pessoas[pessoas['idade'] > 30]
print(acima_de_30['nome']) # ['Bruno']
Vetorização com numpy.vectorize e ufuncs
import numpy as np
# Vetorizando uma função Python comum
def classificar(valor):
if valor < 0:
return 'negativo'
elif valor == 0:
return 'zero'
return 'positivo'
vetorizado = np.vectorize(classificar)
arr = np.array([-2, -1, 0, 1, 2])
print(vetorizado(arr)) # ['negativo' 'negativo' 'zero' 'positivo' 'positivo']
# Universal functions (ufuncs): operações elemento a elemento altamente otimizadas
a = np.array([1.0, 4.0, 9.0, 16.0])
print(np.sqrt(a)) # [1. 2. 3. 4.]
print(np.log(a)) # logaritmo natural
print(np.exp(a)) # e^x para cada elemento
print(np.sin(np.linspace(0, np.pi, 5))) # seno em 5 pontos
# Acumulações e reduções
arr = np.arange(1, 6)
print(np.cumsum(arr)) # [1 3 6 10 15] - soma acumulada
print(np.cumprod(arr)) # [1 2 6 24 120] - produto acumulado
Integração com outras bibliotecas
O NumPy é o ponto de integração de todo o ecossistema científico do Python:
import numpy as np
# Pandas usa arrays NumPy internamente
import pandas as pd
serie = pd.Series(np.random.randn(100))
df = pd.DataFrame(np.random.rand(50, 3), columns=['A', 'B', 'C'])
# scikit-learn espera arrays NumPy
from sklearn.preprocessing import StandardScaler
X = np.random.rand(100, 4)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X) # Retorna array NumPy
# Matplotlib aceita arrays NumPy diretamente
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
# plt.plot(x, y)
# Conversões entre formatos
lista_python = arr.tolist() # NumPy -> lista Python
de_lista = np.array(lista_python) # lista Python -> NumPy
Quando usar NumPy
Use NumPy quando precisar de operações matematicas em grandes volumes de dados numericos, implementar algoritmos que exijam algebra linear (multiplicacao de matrizes, decomposicoes), trabalhar com imagens ou sinais representados como arrays numericos, ou preparar dados para modelos de machine learning. Evite NumPy para colecoes de objetos Python heterogeneos ou quando a simplicidade de uma lista for suficiente.
Erros comuns
O erro mais frequente com NumPy e tentar usar operacoes de lista Python em arrays NumPy (como append) no lugar das funcoes equivalentes do NumPy (np.append ou pre-alocar com np.zeros). Outro erro e ignorar o dtype do array, o que pode causar perda de precisao ao misturar inteiros e floats. Sempre verifique arr.dtype quando os resultados forem inesperados.
Termos Relacionados
- Pandas - Analise de dados construida sobre NumPy
- Machine Learning - Aprendizado de maquina com Python
- Python - A linguagem base