Python e DuckDB: Análise de Dados Ultrarrápida — 2026 | python.dev.br

Aprenda a usar DuckDB com Python para análise de dados local ultrarrápida. SQL direto em Pandas, Parquet e CSV sem servidor — exemplos práticos.

7 min de leitura Equipe python.dev.br

Se você trabalha com análise de dados em Python, provavelmente já passou por situações em que o Pandas ficava lento demais para datasets grandes, mas subir um banco de dados completo parecia exagero. É exatamente nesse espaço que o DuckDB brilha — um banco de dados analítico embutido, sem servidor, que roda SQL diretamente em arquivos CSV, Parquet e até DataFrames do Pandas.

Neste artigo, vamos explorar como usar DuckDB com Python, desde a instalação até consultas avançadas, com exemplos práticos para o dia a dia.

O Que É o DuckDB

O DuckDB é um banco de dados OLAP (Online Analytical Processing) embutido, projetado para cargas de trabalho analíticas. Pense nele como o “SQLite para analytics” — não precisa de servidor, não precisa de configuração, e roda dentro do seu processo Python.

As principais características do DuckDB:

  • Zero configuração — não precisa instalar servidor, criar usuários ou configurar portas
  • SQL completo — suporte a window functions, CTEs, subqueries e agregações complexas
  • Integração nativa com Python — lê DataFrames do Pandas e Polars diretamente
  • Leitura direta de arquivos — CSV, Parquet, JSON e até arquivos remotos via HTTP
  • Performance absurda — processamento colunar vetorizado, muito mais rápido que Pandas para agregações

Instalação e Primeiros Passos

A instalação é simples com pip ou uv:

# Instalação
# pip install duckdb
# ou com uv:
# uv pip install duckdb

import duckdb

# Criar conexão in-memory
con = duckdb.connect()

# Consulta simples
resultado = con.sql("SELECT 42 AS resposta")
print(resultado.fetchone())
# (42,)

O DuckDB também funciona sem criar conexão explícita — você pode usar o módulo diretamente:

import duckdb

# Sem conexão explícita
df = duckdb.sql("SELECT * FROM range(10) AS t(numero)").df()
print(df)

O método .df() converte o resultado diretamente para um DataFrame do Pandas, e .pl() converte para Polars.

SQL Direto em Arquivos CSV e Parquet

Uma das features mais poderosas do DuckDB é consultar arquivos sem carregá-los em memória primeiro:

import duckdb

# Consultar CSV diretamente
resultado = duckdb.sql("""
    SELECT
        cidade,
        COUNT(*) as total,
        AVG(salario) as salario_medio
    FROM 'dados_funcionarios.csv'
    GROUP BY cidade
    ORDER BY salario_medio DESC
    LIMIT 10
""")

print(resultado.df())

Com arquivos Parquet, a performance é ainda melhor porque o DuckDB aproveita o formato colunar para ler apenas as colunas necessárias:

import duckdb

# Ler múltiplos arquivos Parquet com glob
resultado = duckdb.sql("""
    SELECT
        ano,
        SUM(receita) as receita_total,
        COUNT(DISTINCT cliente_id) as clientes_unicos
    FROM 'dados/vendas_*.parquet'
    WHERE ano >= 2024
    GROUP BY ano
    ORDER BY ano
""")

print(resultado.df())

Integração com Pandas e Polars

O DuckDB se integra perfeitamente com o ecossistema Python de dados. Você pode rodar SQL diretamente em DataFrames que já existem na memória:

import pandas as pd
import duckdb

# Criar DataFrame do Pandas
vendas = pd.DataFrame({
    "produto": ["Notebook", "Mouse", "Teclado", "Monitor", "Notebook"],
    "valor": [4500, 120, 280, 1800, 5200],
    "quantidade": [10, 150, 80, 25, 8],
    "regiao": ["Sul", "Sudeste", "Sul", "Nordeste", "Sudeste"]
})

# SQL direto no DataFrame — sem cópia de dados!
resultado = duckdb.sql("""
    SELECT
        regiao,
        SUM(valor * quantidade) as faturamento,
        COUNT(*) as num_vendas
    FROM vendas
    GROUP BY regiao
    ORDER BY faturamento DESC
""")

print(resultado.df())

O DuckDB referencia o DataFrame pelo nome da variável Python — não precisa importar nem registrar nada. Ele também funciona com Polars:

import polars as pl
import duckdb

# DataFrame Polars
df_polars = pl.DataFrame({
    "nome": ["Ana", "Bruno", "Carla", "Diego"],
    "idade": [28, 35, 42, 31],
    "linguagem": ["Python", "Go", "Rust", "Python"]
})

# SQL direto no Polars DataFrame
resultado = duckdb.sql("""
    SELECT linguagem, AVG(idade) as idade_media
    FROM df_polars
    GROUP BY linguagem
""")

# Converter de volta para Polars
print(resultado.pl())

Window Functions e Consultas Avançadas

O DuckDB suporta SQL completo, incluindo window functions que são difíceis de replicar com Pandas puro:

import duckdb

# Criar tabela de exemplo
con = duckdb.connect()
con.sql("""
    CREATE TABLE vendas AS
    SELECT * FROM (VALUES
        ('2024-01', 'Eletrônicos', 45000),
        ('2024-02', 'Eletrônicos', 52000),
        ('2024-03', 'Eletrônicos', 48000),
        ('2024-01', 'Roupas', 32000),
        ('2024-02', 'Roupas', 28000),
        ('2024-03', 'Roupas', 35000)
    ) AS t(mes, categoria, receita)
""")

# Window functions: ranking e média móvel
resultado = con.sql("""
    SELECT
        mes,
        categoria,
        receita,
        RANK() OVER (PARTITION BY mes ORDER BY receita DESC) as ranking,
        AVG(receita) OVER (
            PARTITION BY categoria
            ORDER BY mes
            ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
        ) as media_movel
    FROM vendas
    ORDER BY mes, ranking
""")

print(resultado.df())

Isso seria muito mais verboso e lento usando .groupby() e .rolling() do Pandas.

DuckDB vs Pandas: Quando Usar Cada Um

A pergunta que todo mundo faz: quando trocar Pandas por DuckDB?

CenárioPandasDuckDB
Datasets pequenos (<100MB)ÓtimoFunciona, mas desnecessário
Datasets grandes (1GB+)Lento, consome muita RAMExcelente, processamento colunar
Agregações complexasVerboso com groupbySQL natural e rápido
Window functionsDifícil e lentoSQL nativo, muito rápido
Manipulação célula a célulaIdeal com .apply()Não é o forte
Leitura de ParquetCarrega tudo em memóriaLê apenas colunas necessárias
Joins em tabelas grandesLento com merge()Otimizado com hash joins

Benchmark prático

Para dar uma ideia concreta, aqui vai um comparativo com 10 milhões de linhas:

import pandas as pd
import duckdb
import time

# Gerar dataset grande
n = 10_000_000
df = pd.DataFrame({
    "categoria": [f"cat_{i % 100}" for i in range(n)],
    "valor": range(n),
    "regiao": [f"reg_{i % 10}" for i in range(n)]
})

# Pandas
inicio = time.time()
resultado_pandas = df.groupby(["categoria", "regiao"])["valor"].agg(["sum", "mean", "count"])
tempo_pandas = time.time() - inicio

# DuckDB
inicio = time.time()
resultado_duckdb = duckdb.sql("""
    SELECT categoria, regiao, SUM(valor), AVG(valor), COUNT(*)
    FROM df
    GROUP BY categoria, regiao
""").df()
tempo_duckdb = time.time() - inicio

print(f"Pandas: {tempo_pandas:.2f}s")
print(f"DuckDB: {tempo_duckdb:.2f}s")
# Resultado típico: Pandas ~3.5s, DuckDB ~0.4s

Em agregações com datasets grandes, o DuckDB costuma ser 5 a 10 vezes mais rápido que o Pandas.

Persistência e Banco de Dados em Disco

Embora o modo in-memory seja o mais comum, o DuckDB também persiste dados em disco:

import duckdb

# Criar banco persistente
con = duckdb.connect("meu_banco.duckdb")

# Criar e popular tabela
con.sql("""
    CREATE TABLE IF NOT EXISTS clientes (
        id INTEGER PRIMARY KEY,
        nome VARCHAR,
        email VARCHAR,
        criado_em TIMESTAMP DEFAULT current_timestamp
    )
""")

con.sql("""
    INSERT INTO clientes (id, nome, email) VALUES
    (1, 'Ana Silva', 'ana@email.com'),
    (2, 'Bruno Costa', 'bruno@email.com')
""")

# Os dados persistem entre sessões
con.close()

# Reabrir depois
con = duckdb.connect("meu_banco.duckdb")
print(con.sql("SELECT * FROM clientes").df())
con.close()

O arquivo .duckdb é portátil — você pode compartilhá-lo com colegas ou incluir em pipelines de dados.

DuckDB em Projetos Reais

ETL simples com DuckDB

import duckdb

con = duckdb.connect()

# Extrair de CSV, transformar com SQL, carregar em Parquet
con.sql("""
    COPY (
        SELECT
            UPPER(nome) as nome,
            CAST(salario AS DECIMAL(10,2)) as salario,
            CASE
                WHEN salario > 10000 THEN 'senior'
                WHEN salario > 5000 THEN 'pleno'
                ELSE 'junior'
            END as nivel
        FROM 'funcionarios_raw.csv'
        WHERE salario IS NOT NULL
    ) TO 'funcionarios_limpo.parquet' (FORMAT PARQUET)
""")

Análise exploratória rápida

import duckdb

# Resumo estatístico instantâneo de qualquer arquivo
resumo = duckdb.sql("""
    SUMMARIZE SELECT * FROM 'dataset.parquet'
""")
print(resumo.df())

O comando SUMMARIZE gera estatísticas descritivas (min, max, média, desvio padrão, nulos) automaticamente — parecido com df.describe() do Pandas, mas funciona direto no arquivo.

Quando NÃO Usar DuckDB

O DuckDB é fantástico para analytics, mas não é solução para tudo:

  • Aplicações web com múltiplos usuários — use PostgreSQL ou MongoDB
  • Cache e filas — use Redis
  • Dados simples e levesSQLite continua sendo suficiente
  • Streaming de dados em tempo real — use ferramentas como Kafka ou MQTT

O DuckDB é ideal para análise de dados local, ETL, exploração de datasets e qualquer cenário onde você precisa de SQL rápido sem infraestrutura.

Conclusão

O DuckDB preenche uma lacuna importante no ecossistema Python de dados: análise rápida sem complexidade. Ele combina a simplicidade do SQLite com a potência de um banco analítico moderno, e a integração nativa com Pandas e Polars torna a adoção praticamente sem atrito.

Se você trabalha com ciência de dados ou precisa processar datasets que não cabem confortavelmente no Pandas, o DuckDB merece um lugar no seu toolkit. A curva de aprendizado é mínima — se você sabe SQL, já sabe usar DuckDB.

Se performance é prioridade nos seus projetos, vale conhecer também o Rust, a linguagem por trás de ferramentas como Polars e Ruff que estão revolucionando o ecossistema Python.

Para quem trabalha com análise de dados e quer explorar uma linguagem compilada com ótimo suporte a concorrência, confira o Go — muito usado em pipelines de dados e ferramentas de infraestrutura.

E

Equipe python.dev.br

Contribuidor do Python Brasil — Aprenda Python em Português