Manipulação e Análise de Dados em Python com NumPy e Pandas

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")

Tags: NumPy pandas Python vectorization dataframe

Publicado em 10-18 13:46