| RESUMO | |
|---|---|
| Palavras: | ~1.860 |
| Leitura: | 9 min |
| Linha | 01 Fundamentos |
| Progresso: | 16.7% [■■---------] |
Até agora, em nossa jornada, construímos uma base matemática para entender as transformações de dados (Álgebra Linear) e exploramos as estruturas de baixo nível que os computadores usam para armazenar e buscar informações na memória (Árvores Binárias e Hashes). Mas, no dia a dia, um Cientista de Dados raramente interage diretamente com ponteiros de memória ou implementa funções de hash do zero. Nós interagimos com Bancos de Dados.
Nesta terceira estação, vamos elevar o nosso nível de abstração. Vamos descobrir como a humanidade resolveu o problema de organizar, consultar e garantir a integridade de montanhas de informações comerciais. Para isso, precisamos voltar à década de 1970, nos laboratórios da IBM, onde um matemático chamado Edgar F. Codd publicou um artigo que mudaria a computação para sempre, introduzindo o Modelo Relacional e sua linguagem matemática subjacente: a Álgebra Relacional.
Pode parecer contraintuitivo estudar álgebra para aprender sobre bancos de dados, mas não se engane: toda consulta SQL que você escreve, e toda filtragem que você faz no Pandas, é executada nos bastidores como uma equação de Álgebra Relacional. Compreender essa matemática é obter a planta arquitetônica de como os dados fluem dentro de uma corporação.
1. Do Caos à Ordem: O Paradigma Relacional
Antes do modelo relacional, os sistemas de banco de dados eram um pesadelo logístico. Eles usavam modelos de navegação hierárquica ou em rede. O que isso significa na prática? Significava que a forma como o dado era consultado dependia estritamente de como ele estava gravado no disco físico. Se o engenheiro mudasse a estrutura do disco, todos os programas de consulta quebravam. Não havia independência de dados.
Codd revolucionou isso aplicando a Teoria dos Conjuntos (da matemática) aos dados de negócios. Ele propôs que o usuário não precisaria saber onde ou como os dados estavam armazenados fisicamente. O usuário apenas declararia o que ele queria, operando sobre estruturas lógicas simples e bidimensionais.
O Vocabulário Relacional
No mercado de trabalho, usamos termos práticos, mas na academia e nos motores de banco de dados, o vocabulário é rigorosamente matemático. É vital conhecer essa tradução:
-
Relação (Relation): No mundo prático, chamamos de Tabela. É a estrutura bidimensional principal. Um banco de dados relacional é simplesmente uma coleção de relações.
-
Tupla (Tuple): No dia a dia, chamamos de Linha ou Registro. Representa uma única entidade no mundo real (ex: um cliente específico).
-
Atributo (Attribute): Na prática, é a Coluna. Descreve as propriedades de uma relação (ex: "Nome", "Idade", "CPF").
-
Domínio (Domain): É o conjunto de valores atômicos válidos para um atributo. O domínio é o que chamamos de Tipagem de Dados. Se o atributo é "Idade", o domínio são números inteiros positivos. Você não pode armazenar a string "João" na coluna de idade.
Essa separação rígida impõe uma ordem estrutural (o schema) que garante a integridade e a consistência da informação, evitando que os dados se transformem em um pântano inútil.
2. A Álgebra Relacional: A Matemática das Tabelas
A Álgebra Relacional é uma linguagem de consulta procedimental. Ela consiste em um conjunto de operações que recebem uma ou mais relações (tabelas) como entrada e produzem uma nova relação como saída.
Como a saída de uma operação é sempre uma tabela, podemos encadear essas operações infinitamente, assim como na álgebra básica podemos resolver . Vamos dissecar as operações fundamentais que suportam todo o processamento analítico.
2.1 Seleção (Selection - )
A Seleção é usada para escolher um subconjunto de tuplas (linhas) de uma relação que satisfaçam uma condição específica. Ela filtra os dados horizontalmente.
Na matemática, usamos a letra grega Sigma (). A sintaxe é .
Exemplo Lógico: Imagine uma tabela chamada . Queremos apenas os clientes com idade superior a 30 anos.
O resultado é uma nova tabela que mantém todas as colunas originais, mas contém apenas as linhas onde a condição é verdadeira.
2.2 Projeção (Projection - )
Enquanto a Seleção filtra as linhas (horizontalmente), a Projeção filtra os atributos (verticalmente). Ela escolhe colunas específicas e descarta o resto. O símbolo utilizado é a letra grega Pi (). A sintaxe é .
Exemplo Lógico: Queremos apenas os nomes e os emails da nossa tabela de Clientes, ignorando seus CPFs, endereços e idades.
Nota matemática vital: Como a Álgebra Relacional é baseada na teoria dos conjuntos, um conjunto não pode ter itens duplicados. Portanto, a operação de projeção remove automaticamente qualquer linha inteiramente duplicada no resultado.
2.3 Combinando Operações (Composição)
Na vida real, queremos filtrar linhas e colunas ao mesmo tempo. Podemos aninhar as fórmulas:
Lê-se de dentro para fora: Primeiro, encontre os clientes com mais de 30 anos (Seleção). Do resultado dessa operação, extraia apenas os nomes e emails (Projeção).
2.4 União (Union - )
Permite juntar as tuplas de duas relações diferentes. Para que a união seja possível, as tabelas precisam ser compatíveis em esquema (mesmo número de colunas e os mesmos domínios/tipos de dados na mesma ordem).
Exemplo Lógico: Você tem uma tabela e outra . Para obter a lista nacional:
Novamente, duplicatas exatas são eliminadas automaticamente.
2.5 Diferença de Conjuntos (Set Difference - )
Retorna as tuplas que estão presentes na primeira relação, mas não estão na segunda. Também requer que as tabelas sejam compatíveis.
Exemplo Lógico: Você tem uma tabela e uma tabela . Para encontrar os usuários ativos:
2.6 Produto Cartesiano (Cartesian Product - )
Esta é a operação mais perigosa e poderosa. O Produto Cartesiano combina cada tupla de uma relação com todas as tuplas da outra relação.
Se a Tabela A tem 100 linhas e a Tabela B tem 1000 linhas, gerará uma monstruosidade de 100.000 linhas, combinando tudo com tudo. Isolado, o produto cartesiano raramente tem utilidade analítica, mas ele é a base matemática fundamental para a operação de Join, que dissecaremos profundamente na Estação 4.
2.7 Renomeação (Rename - )
Muitas vezes, após várias operações, os nomes das colunas ficam confusos ou se chocam. A operação Rho () permite alterar o nome de atributos ou de relações inteiras. Ela não altera os dados, apenas os rótulos de apresentação (o famoso AS no SQL).
3. A Ponte: Da Álgebra para o SQL
Por que nós não digitamos no teclado hoje em dia?
Porque nos anos 70, criar teclados com letras gregas e treinar analistas de negócios para escrever fórmulas matemáticas complexas era inviável. Então, a IBM desenvolveu a Structured Query Language (SQL).
O SQL foi desenhado para parecer com a sintaxe do idioma inglês, mas, por baixo dos panos, o Otimizador de Consultas (Query Optimizer) do banco de dados traduz imediatamente seu código SQL de volta para a Álgebra Relacional.
Veja a correspondência direta:
-
A Relação
FROM tabela -
A Seleção ()
WHERE condição -
A Projeção ()
SELECT coluna1, coluna2
Portanto, a fórmula matemática é literalmente:
SELECT nome, email FROM Clientes WHERE idade > 30;
A grande diferença de paradigma é que a Álgebra Relacional é procedimental (você especifica a ordem dos passos matemáticos), enquanto o SQL é declarativo (você descreve o que quer, e o Otimizador do banco de dados usa heurísticas e Estatísticas para descobrir qual é a ordem algébrica mais rápida para chegar lá usando as Árvores Binárias dos índices).
4. O Impacto Direto em Data Science
Por que um Cientista de Dados não pode simplesmente ignorar isso e baixar tudo como CSV para ler no Python?
-
Volume Intratável: Bases de dados modernas têm Terabytes, às vezes Petabytes. Tentar puxar tudo para a memória RAM do Python via rede vai travar sua máquina e o servidor.
-
Pushdown Computation: O conhecimento profundo da Álgebra Relacional permite que você empurre (pushdown) a computação para o banco de dados. Um Cientista de Dados Sênior usa as operações de seleção () e projeção () no SQL para extrair apenas a matriz exata necessária para o modelo preditivo, aproveitando o poder de processamento distribuído e os índices O(log n) do banco de dados.
-
Fundamento do Pandas/Spark: O pacote Pandas em Python e o Apache Spark são, na sua essência, motores de Álgebra Relacional processados em memória. Compreender a teoria dos conjuntos garante que você manipule DataFrames sem criar explosões combinatórias que consomem toda a sua memória.
5. Resumo e Próximos Passos
O modelo relacional retirou a navegação física do controle dos programadores e a entregou para a matemática dos conjuntos, criando sistemas resilientes, seguros e flexíveis. Compreendemos a Seleção (cortar na horizontal), a Projeção (cortar na vertical) e as operações de conjunto (União e Diferença).
No entanto, o verdadeiro poder dos bancos de dados não está em uma tabela isolada, mas na relação entre elas. Como conectamos Vendas, Clientes e Produtos de forma eficiente? Na Estação 4, mergulharemos fundo na evolução direta do Produto Cartesiano: Os Joins (Inner, Outer, Cross e Theta).
Antes de seguir em frente, vamos traduzir essas teorias matemáticas abstratas para o código Python que você rodará todos os dias em seus notebooks Jupyter.
Mão na Massa: Álgebra Relacional com Pandas
Embora o SQL seja a linguagem oficial dos bancos de dados, os Cientistas de Dados realizam as exatas mesmas operações de Álgebra Relacional usando a biblioteca pandas em Python. O Pandas abstrai as tabelas como DataFrames.
Abaixo, simulamos um banco de dados na memória do Python e executamos as operações matemáticas fundamentais que vimos na teoria.
import pandas as pd dados_clientes = { 'cliente_id': [1, 2, 3, 4, 5], 'nome': ['Ana', 'Bruno', 'Carlos', 'Diana', 'Eduardo'], 'idade': [28, 35, 42, 29, 55], 'cidade': ['SP', 'RJ', 'MG', 'SP', 'RS'] } dados_novos_clientes = { 'cliente_id': [6, 7, 1], 'nome': ['Fernanda', 'Gustavo', 'Ana'], 'idade': [31, 22, 28], 'cidade': ['PR', 'SC', 'SP'] } df_clientes = pd.DataFrame(dados_clientes) df_novos = pd.DataFrame(dados_novos_clientes) # ------------------------------------- # RELAÇÃO ORIGINAL (TABELA CLIENTES) # ------------------------------------- print(df_clientes, "\n") # --------------------------------------- # SELEÇÃO (Sigma - Filtragem de Linhas) # --------------------------------------- # SQL: SELECT * FROM df_clientes WHERE idade > 30 selecao = df_clientes[df_clientes['idade'] > 30] print(selecao, "\n") # -------------------------------------- # PROJEÇÃO (Pi - Filtragem de Colunas) # -------------------------------------- # SQL: SELECT nome, cidade FROM df_clientes projecao = df_clientes[['nome', 'cidade']] print(projecao, "\n") # --------------------------------------- # COMPOSIÇÃO (Seleção e Projeção Juntas) # --------------------------------------- composicao = df_clientes[df_clientes['cidade'] == 'SP'][['nome', 'idade']] print(composicao, "\n") # ---------------------------------- # UNIÃO (Union - Juntar Conjuntos) # ---------------------------------- uniao = pd.concat([df_clientes, df_novos]).drop_duplicates(subset=['cliente_id']) print(uniao, "\n") # ----------------------------------------- # DIFERENÇA DE CONJUNTOS (Set Difference) # ----------------------------------------- diferenca_merge = df_novos.merge(df_clientes, on='cliente_id', how='left', indicator=True) diferenca = diferenca_merge[diferenca_merge['_merge'] == 'left_only'] diferenca = diferenca[['cliente_id', 'nome_x', 'idade_x', 'cidade_x']].rename( columns={'nome_x': 'nome', 'idade_x': 'idade', 'cidade_x': 'cidade'} ) print(diferenca, "\n") # ---------------------------------------- # PRODUTO CARTESIANO (Cartesian Product) # ---------------------------------------- produto_cartesiano = df_clientes.merge(df_novos, how='cross', suffixes=('_orig', '_novo')) print(f"Número de linhas geradas: {len(produto_cartesiano)}") print(produto_cartesiano.head(5))
