DEV Community

Cover image for De SQL ao Polars: Migrando para um Motor OLAP Moderno com Performance Máxima
Francisco Júnior
Francisco Júnior

Posted on

De SQL ao Polars: Migrando para um Motor OLAP Moderno com Performance Máxima

Este guia foi desenhado para quem já tem familiaridade com SQL e deseja migrar para o Polars, aproveitando a máxima performance do hardware moderno.

O Polars não é apenas uma "alternativa ao Pandas"; ele é, essencialmente, um motor de consulta (Query Engine) OLAP escrito em Rust que utiliza a sintaxe Python para expressar planos de execução lógica.


1. Preparação do Ambiente com uv

Para garantir que você está usando a versão mais recente e otimizada para o seu chip (especialmente se for Apple Silicon), utilize o uv para gerenciar o projeto.

# Inicie um novo projeto
uv init lab-polars
cd lab-polars

# Adicione o Polars e suporte a arquivos Parquet
uv add polars pyarrow

Enter fullscreen mode Exit fullscreen mode

2. O Modelo Mental: Expressões e Contextos

Diferente do SQL, onde você escreve uma string única, no Polars você compõe Expressões dentro de Contextos.

  • Contextos: Definem "onde" você está operando (select, filter, with_columns, group_by).
  • Expressões: Definem "o que" você está fazendo com as colunas (pl.col("vendas").sum()).

Dica: No Polars, quase tudo é vetorizado. Se você estiver pensando em usar um loop for, provavelmente há uma forma mais rápida de fazer isso usando expressões.


3. Pedra de Roseta: SQL vs. Polars

Abaixo, as operações mais comuns traduzidas diretamente da linguagem SQL para a sintaxe Polars.

3.1 Seleção e Filtro (SELECT & WHERE)

Operação SQL Sintaxe Polars
SELECT nome, idade FROM tabela df.select("nome", "idade")
SELECT * FROM tabela WHERE idade > 25 df.filter(pl.col("idade") > 25)
SELECT DISTINCT categoria FROM tabela df.select("categoria").unique()

3.2 Agregações (GROUP BY)

O Polars brilha aqui devido ao seu motor multithreaded que processa grupos em paralelo.

Operação SQL Sintaxe Polars
GROUP BY regiao df.group_by("regiao")
COUNT(*), AVG(vendas) .agg([pl.len(), pl.col("vendas").mean()])
HAVING total > 1000 .filter(pl.col("total") > 1000)

3.3 Window Functions (O Poder do .over())

Se você usa OVER (PARTITION BY ...) no SQL, a transição para Polars é natural e extremamente poderosa.

SQL:

SELECT 
    vendedor, 
    venda,
    venda / SUM(venda) OVER (PARTITION BY regiao) as share_regiao
FROM vendas

Enter fullscreen mode Exit fullscreen mode

Polars:

df.with_columns(
    (pl.col("venda") / pl.col("venda").sum().over("regiao")).alias("share_regiao")
)

Enter fullscreen mode Exit fullscreen mode

4. Otimização Automática: A Lazy API

Em SQL, o banco de dados não executa sua query linha por linha; ele cria um plano e otimiza (Predicate Pushdown). O Polars faz exatamente o mesmo através da sua Lazy API.

Como usar o Modo Lazy:

  1. Use scan_parquet ou scan_csv em vez de read_.
  2. Construa toda a sua lógica de transformação.
  3. Chame .collect() no final para disparar a execução.
import polars as pl

# Criando um plano lógico (não carrega os dados ainda)
lazy_plan = (
    pl.scan_parquet("dados_gigantes.parquet")
    .filter(pl.col("status") == "ERRO")
    .group_by("servidor")
    .agg(pl.len().alias("total_erros"))
)

# Agora sim, o processamento ocorre (usando todos os núcleos da CPU)
df_final = lazy_plan.collect()

Enter fullscreen mode Exit fullscreen mode

5. Exemplo Real: O Showdown de Logs Massivos

Para o seu post no dev.to, aqui está um exemplo prático que ilustra a diferença brutal de performance entre a abordagem legada (Pandas) e a moderna (Polars).

Cenário: Processar um arquivo de log de 10GB (aprox. 100 milhões de linhas) para encontrar a média de tempo de resposta por endpoint apenas para status 200.

Abordagem Pandas (O Gargalo)

import pandas as pd
import time

start = time.time()

# Pandas carrega tudo na RAM (Pode causar erro de memória)
df = pd.read_csv("access_logs.csv")
df_filtered = df[df['status'] == 200]
result = df_filtered.groupby('endpoint')['response_time'].mean()

print(f"Pandas levou: {time.time() - start:.2f} segundos")

Enter fullscreen mode Exit fullscreen mode

Abordagem Polars (A Eficiência)

import polars as pl
import time

start = time.time()

# Polars usa Lazy API e Streaming para processar sem estourar a RAM
result = (
    pl.scan_csv("access_logs.csv")
    .filter(pl.col("status") == 200)
    .group_by("endpoint")
    .agg(pl.col("response_time").mean())
    .collect(streaming=True)
)

print(f"Polars levou: {time.time() - start:.2f} segundos")

Enter fullscreen mode Exit fullscreen mode

Resultado Típico: Em hardware Apple Silicon, o Polars costuma ser 10x a 50x mais rápido que o Pandas para esta tarefa, consumindo apenas uma fração da memória RAM devido ao processamento por chunks.


Conclusão

Ao adotar o Polars, você deixa de ser um "manipulador de tabelas" para se tornar um "arquiteto de dados". O Python serve como a interface amigável, enquanto o motor em Rust garante que seu MacBook não se torne um aquecedor de mãos ineficiente.

Top comments (0)