NumPy: A Base da Computação Numérica Vetorizada
O NumPy (Numerical Python) é o alicerce do ecossistema científico em Python, fornecendo suporte para arrays multidimensionais de alto desempenho e uma vasta coleção de funções matemáticas para operá-los de forma otimizada.
Criação e Inicialização de Arrays
import numpy as np
# Criação a partir de tuplas
vetor = np.array((10, 20, 30, 40, 50))
# Matriz bidimensional
matriz = np.array([[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]])
print(matriz.shape)
# Arrays especiais
identidade = np.eye(3)
diagonal = np.diag([5, 10, 15])
# Sequências numéricas
seq_arange = np.arange(5, 25, 5)
seq_linspace = np.linspace(10, 50, 5)
# Geração de números aleatórios
np.random.seed(99)
uniforme = np.random.uniform(0, 10, (2, 2))
normal = np.random.normal(loc=5.0, scale=2.0, size=(2, 2))
Inspeção de Atributos
mat = np.array([[10, 20, 30], [40, 50, 60]])
print(f"Formato: {mat.shape}")
print(f"Dimensões: {mat.ndim}")
print(f"Total de elementos: {mat.size}")
print(f"Tipo de dado: {mat.dtype}")
print(f"Bytes por elemento: {mat.itemsize}")
Indexação e Fatiamento
grade = np.array([[10, 20, 30, 40],
[50, 60, 70, 80],
[90, 100, 110, 120]])
print(grade[1, 2]) # 70
print(grade[0:2, 2:]) # Fatiamento de submatriz
print(grade[grade % 20 == 0]) # Indexação booleana
Operações Aritméticas e Broadcasting
x = np.array([10, 20, 30])
y = np.array([1, 2, 3])
print(x + y)
print(x * y)
# Broadcasting (expansão automática de dimensões)
print(x + 100)
print(x * 0.5)
# Produto matricial
m1 = np.array([[1, 2], [3, 4]])
m2 = np.array([[2, 0], [1, 2]])
print(m1 @ m2)
Funções Matemáticas e Estatísticas
angulos = np.array([0, np.pi/4, np.pi/2])
print(np.tan(angulos))
metricas = np.array([15, 25, 35, 45, 55])
print(np.median(metricas))
print(np.var(metricas))
print(np.percentile(metricas, 75))
Manipulação de Forma e Álgebra Linear
dados = np.arange(16)
grade_4x4 = dados.reshape(4, 4)
# Transposição e expansão de dimensões
print(grade_4x4.T)
expandido = np.expand_dims(dados, axis=0)
# Resolução de sistemas lineares (Ax = b)
A = np.array([[2, 1], [1, 3]])
b = np.array([5, 10])
x = np.linalg.solve(A, b)
# Decomposição em valores singulares (SVD)
U, S, Vh = np.linalg.svd(A)
Pandas: Estruturas de Dados e Análise Tabular
Construído sobre o NumPy, o Pandas introduz estruturas de dados flexíveis como Series e DataFrames, projetadas para tornar a manipulação de dados estruturados e a limpeza de informações intuitivas e efciientes.
Series e DataFrames
import pandas as pd
import numpy as np
# Series unidimensional
temperaturas = pd.Series([22.5, 23.1, np.nan, 21.8, 24.0], index=['Seg', 'Ter', 'Qua', 'Qui', 'Sex'])
print(temperaturas.interpolate())
# DataFrame bidimensional
inventario = pd.DataFrame({
'item': ['Notebook', 'Mouse', 'Teclado', 'Monitor', 'Headset'],
'estoque': [45, 120, 85, 30, 60],
'preco': [1200.50, 25.99, 75.00, 350.00, 110.25],
'fornecedor': ['TechCorp', 'TechCorp', 'InputLtd', 'VisualInc', 'AudioPro']
})
print(inventario.head(3))
print(inventario.dtypes)
Seleção, Filtragem e Limpeza
# Seleção por rótulo e posição
print(inventario.loc[1:3, ['item', 'preco']])
print(inventario.iloc[[0, 2, 4]])
# Filtros condicionais
alto_valor = inventario[inventario['preco'] > 100]
tech_items = inventario.query("fornecedor == 'TechCorp' and estoque > 50")
# Tratamento de dados e transformação
limpo = inventario.dropna().drop_duplicates(subset=['item'])
inventario['categoria_preco'] = pd.cut(inventario['preco'], bins=[0, 50, 200, 1500], labels=['Baixo', 'Medio', 'Alto'])
inventario['codigo'] = inventario['item'].str.upper().str[:3]
inventario['valor_total'] = inventario.apply(lambda row: row['estoque'] * row['preco'], axis=1)
Agrupamento, Mesclagem e Tabelas Dinâmicas
# Agregação por grupo
stats_fornecedor = inventario.groupby('fornecedor').agg(
estoque_total=('estoque', 'sum'),
preco_medio=('preco', 'mean'),
qtd_itens=('item', 'count')
).reset_index()
# Mesclagem de DataFrames (JOIN)
vendas = pd.DataFrame({
'item': ['Notebook', 'Mouse', 'Monitor'],
'unidades_vendidas': [10, 50, 5]
})
dados_mesclados = pd.merge(inventario, vendas, on='item', how='left')
dados_mesclados['unidades_vendidas'] = dados_mesclados['unidades_vendidas'].fillna(0)
# Tabelas dinâmicas
transacoes = pd.DataFrame({
'dia': ['Seg', 'Seg', 'Ter', 'Ter', 'Qua'],
'loja': ['Norte', 'Sul', 'Norte', 'Sul', 'Norte'],
'receita': [500, 300, 600, 450, 550]
})
tabela_pivot = pd.pivot_table(transacoes, values='receita', index='loja', columns='dia', aggfunc='sum', fill_value=0)
Caso Prático: Processamento de Dados de Sensores IoT
np.random.seed(123)
n_leituras = 1000
# Geração de dataset simulado
dados_sensor = pd.DataFrame({
'sensor_id': np.random.choice(['S1', 'S2', 'S3', 'S4'], n_leituras),
'timestamp': pd.date_range(start='2024-01-01', periods=n_leituras, freq='15min'),
'temperatura': np.random.normal(loc=25.0, scale=5.0, size=n_leituras),
'umidade': np.random.uniform(low=30.0, high=80.0, size=n_leituras)
})
# Inserção de ruído (valores nulos) e tratamento
dados_sensor.loc[np.random.choice(dados_sensor.index, 50), 'temperatura'] = np.nan
dados_sensor['temperatura'] = dados_sensor['temperatura'].ffill()
dados_sensor['hora'] = dados_sensor['timestamp'].dt.hour
# Agregação temporal diária
media_diaria = dados_sensor.groupby([dados_sensor['timestamp'].dt.date, 'sensor_id']).agg(
temp_media=('temperatura', 'mean'),
umidade_max=('umidade', 'max')
).reset_index()
Otimização de Memória e Performance
# Prevenção do SettingWithCopyWarning
subconjunto = dados_sensor[dados_sensor['sensor_id'] == 'S1'].copy()
subconjunto['temp_celsius'] = subconjunto['temperatura']
# Redução de pegada de memória usando tipos categóricos
dados_sensor['sensor_id'] = dados_sensor['sensor_id'].astype('category')
# Benchmark: Loop nativo vs Vetorização NumPy
import time
tamanho = 5000000
arr_a = np.random.rand(tamanho)
arr_b = np.random.rand(tamanho)
inicio = time.time()
resultado_loop = [x + y for x, y in zip(arr_a, arr_b)]
tempo_loop = time.time() - inicio
inicio = time.time()
resultado_numpy = arr_a + arr_b
tempo_numpy = time.time() - inicio
print(f"Loop Python: {tempo_loop:.4f}s | NumPy: {tempo_numpy:.4f}s")