NumPy: arrays e operações numéricas em Python
Domine arrays NumPy: criação, indexação, operações matemáticas e por que é mais rápido que listas.
O que é NumPy?
NumPy (Numerical Python) é a biblioteca fundamental para computação numérica em Python. Ela fornece o ndarray, uma estrutura de dados para armazenar arrays multidimensionais de forma extremamente eficiente.
Se você já trabalhou com listas em Python, pode estar pensando: "Por que preciso de outra estrutura de dados?" A resposta é simples: velocidade e praticidade.
import numpy as np
# Comparando lista Python vs array NumPy
lista = list(range(1_000_000))
array = np.arange(1_000_000)
# A operacao com NumPy e dezenas de vezes mais rapida
# Lista: precisamos de um loop
resultado_lista = [x * 2 for x in lista]
# NumPy: operacao vetorizada (sem loop!)
resultado_array = array * 2
Por que NumPy é tão rápido?
NumPy é escrito em C por baixo dos panos. Quando você faz uma operação em um array, o NumPy executa o cálculo em código C compilado, não em Python puro. Isso traz três vantagens:
| Característica | Lista Python | Array NumPy |
|---|---|---|
| Armazenamento | Cada elemento é um objeto Python separado | Dados contíguos na memória |
| Tipo dos dados | Pode misturar tipos | Todos os elementos têm o mesmo tipo |
| Operações | Precisa de loops | Vetorizadas (sem loops) |
| Velocidade | Lenta para grandes volumes | 10x a 100x mais rápida |
Criando arrays
Existem várias formas de criar arrays NumPy:
import numpy as np
# A partir de uma lista
notas = np.array([7.5, 8.0, 6.5, 9.0, 7.0])
print(notas) # [7.5 8. 6.5 9. 7. ]
# Array de zeros
zeros = np.zeros(5)
print(zeros) # [0. 0. 0. 0. 0.]
# Array de uns
uns = np.ones(5)
print(uns) # [1. 1. 1. 1. 1.]
# Sequencia com arange (similar ao range)
sequencia = np.arange(0, 10, 2)
print(sequencia) # [0 2 4 6 8]
# Valores igualmente espacados com linspace
espacados = np.linspace(0, 1, 5)
print(espacados) # [0. 0.25 0.5 0.75 1. ]
# Array com valores aleatorios
aleatorios = np.random.rand(5)
print(aleatorios) # [0.23 0.67 0.11 0.89 0.45] (valores variam)
# Matriz 3x3 (array bidimensional)
matriz = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(matriz)
Propriedades dos arrays
Todo array tem propriedades que descrevem sua estrutura:
import numpy as np
dados = np.array([[10, 20, 30],
[40, 50, 60]])
print(dados.shape) # (2, 3) -> 2 linhas, 3 colunas
print(dados.ndim) # 2 -> numero de dimensoes
print(dados.size) # 6 -> total de elementos
print(dados.dtype) # int64 -> tipo dos dados
Você também pode especificar o tipo de dado na criação:
# Forcar tipo float
precos = np.array([10, 20, 30], dtype=np.float64)
print(precos) # [10. 20. 30.]
print(precos.dtype) # float64
Indexação e fatiamento
Acessar elementos em arrays funciona de forma parecida com listas, mas com recursos extras para arrays multidimensionais:
import numpy as np
# Array unidimensional
notas = np.array([7.5, 8.0, 6.5, 9.0, 7.0, 8.5])
print(notas[0]) # 7.5 (primeiro elemento)
print(notas[-1]) # 8.5 (ultimo elemento)
print(notas[1:4]) # [8. 6.5 9. ] (do indice 1 ao 3)
print(notas[:3]) # [7.5 8. 6.5] (primeiros 3)
print(notas[::2]) # [7.5 6.5 7. ] (de 2 em 2)
# Array bidimensional (matriz)
matriz = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
print(matriz[0, 0]) # 1 (linha 0, coluna 0)
print(matriz[1, 2]) # 6 (linha 1, coluna 2)
print(matriz[0]) # [1 2 3] (linha inteira)
print(matriz[:, 1]) # [2 5 8] (coluna inteira)
print(matriz[0:2, 1:]) # [[2 3] [5 6]] (submatriz)
Operações matemáticas
A grande força do NumPy está nas operações vetorizadas. Elas aplicam o cálculo a todos os elementos de uma só vez:
import numpy as np
precos = np.array([100, 200, 150, 300, 250])
# Operacoes elemento a elemento
print(precos + 10) # [110 210 160 310 260]
print(precos * 1.1) # [110. 220. 165. 330. 275.]
print(precos ** 2) # [10000 40000 22500 90000 62500]
# Operacoes entre arrays
descontos = np.array([10, 20, 15, 30, 25])
preco_final = precos - descontos
print(preco_final) # [ 90 180 135 270 225]
Funções de agregação
NumPy oferece funções para resumir dados rapidamente:
import numpy as np
vendas = np.array([1500, 2300, 1800, 3100, 2750, 1950, 2400])
print("Soma total:", vendas.sum()) # 15800
print("Media:", vendas.mean()) # 2257.14...
print("Mediana:", np.median(vendas)) # 2300.0
print("Desvio padrao:", vendas.std()) # 497.96...
print("Minimo:", vendas.min()) # 1500
print("Maximo:", vendas.max()) # 3100
print("Indice do maximo:", vendas.argmax()) # 3
Para arrays bidimensionais, você pode aplicar as funções por eixo:
import numpy as np
# Vendas: 3 lojas, 4 meses
vendas = np.array([[100, 120, 110, 130],
[200, 180, 210, 190],
[150, 160, 140, 170]])
print("Total por loja:", vendas.sum(axis=1)) # [460 780 620]
print("Total por mes:", vendas.sum(axis=0)) # [450 460 460 490]
print("Media por loja:", vendas.mean(axis=1)) # [115. 195. 155.]
O parâmetro axis=0 opera nas colunas (ao longo das linhas) e axis=1 opera nas linhas (ao longo das colunas).
Redimensionamento (reshape)
Você pode mudar a forma de um array sem alterar seus dados:
import numpy as np
original = np.arange(12)
print(original) # [ 0 1 2 3 4 5 6 7 8 9 10 11]
# Transformar em matriz 3x4
matriz = original.reshape(3, 4)
print(matriz)
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
# Transformar em matriz 4x3
outra = original.reshape(4, 3)
print(outra)
# [[ 0 1 2]
# [ 3 4 5]
# [ 6 7 8]
# [ 9 10 11]]
# Usar -1 para calcular automaticamente uma dimensao
auto = original.reshape(2, -1) # 2 linhas, colunas calculadas
print(auto.shape) # (2, 6)
Indexação booleana (filtragem)
Um dos recursos mais poderosos do NumPy é filtrar dados com condições:
import numpy as np
notas = np.array([5.5, 7.0, 8.5, 4.0, 9.0, 6.5, 3.5, 7.5])
# Criar mascara booleana
aprovados = notas >= 7.0
print(aprovados) # [False True True False True False False True]
# Usar a mascara para filtrar
print(notas[aprovados]) # [7. 8.5 9. 7.5]
# Combinar condicoes
bons = (notas >= 7.0) & (notas < 9.0)
print(notas[bons]) # [7. 8.5 7.5]
# Contar quantos atendem a condicao
print("Aprovados:", aprovados.sum()) # 4
print("Percentual:", aprovados.mean() * 100) # 50.0%
Exemplo prático: análise de notas
Vamos juntar tudo em um exemplo completo. Imagine que você tem as notas de uma turma em três provas:
import numpy as np
# Notas de 8 alunos em 3 provas
np.random.seed(42) # Para resultados reproduziveis
notas = np.random.uniform(3.0, 10.0, size=(8, 3)).round(1)
print("Notas da turma:")
print(notas)
print()
# Media de cada aluno (ao longo das provas)
medias = notas.mean(axis=1).round(1)
print("Media por aluno:", medias)
# Media de cada prova (ao longo dos alunos)
media_provas = notas.mean(axis=0).round(1)
print("Media por prova:", media_provas)
# Alunos aprovados (media >= 7)
aprovados = medias >= 7.0
print(f"\nAprovados: {aprovados.sum()} de {len(medias)}")
print(f"Taxa de aprovacao: {aprovados.mean() * 100:.1f}%")
# Nota mais alta e mais baixa da turma
print(f"\nMaior nota: {notas.max()}")
print(f"Menor nota: {notas.min()}")
# Desvio padrao por prova (variacao das notas)
desvio = notas.std(axis=0).round(2)
print(f"Desvio padrao por prova: {desvio}")
Resumo dos comandos
| Operação | Comando | Exemplo |
|---|---|---|
| Criar array | np.array() |
np.array([1, 2, 3]) |
| Zeros | np.zeros() |
np.zeros(5) |
| Uns | np.ones() |
np.ones((3, 3)) |
| Sequência | np.arange() |
np.arange(0, 10, 2) |
| Espaçados | np.linspace() |
np.linspace(0, 1, 5) |
| Forma | .shape |
arr.shape |
| Soma | .sum() |
arr.sum() |
| Média | .mean() |
arr.mean() |
| Desvio padrão | .std() |
arr.std() |
| Mínimo/Máximo | .min(), .max() |
arr.min() |
| Redimensionar | .reshape() |
arr.reshape(3, 4) |
| Filtrar | Indexação booleana | arr[arr > 5] |
Conclusão
NumPy é a fundação de todo o ecossistema de Data Science em Python. Sem ele, bibliotecas como Pandas, Matplotlib e Scikit-learn simplesmente não existiriam. Os conceitos que você aprendeu aqui — arrays, operações vetorizadas, agregações e filtragem — vão aparecer o tempo todo na sua jornada como cientista de dados. No próximo artigo, vamos subir de nível e aprender Pandas, a biblioteca que transforma arrays em tabelas poderosas para análise de dados.