Ibis Framework em Python: DataFrames Portáteis do DuckDB ao BigQuery (2026)
Guia prático de Ibis 12.0 em Python: escreva DataFrames uma vez e execute em DuckDB, BigQuery, Snowflake ou Polars trocando apenas o objeto de conexão.
O Ibis Framework é uma biblioteca de DataFrames portátil para Python que compila expressões idênticas em SQL nativo para mais de 20 motores (do DuckDB local ao BigQuery, Snowflake, PostgreSQL, ClickHouse e Spark), permitindo que você escreva a lógica analítica uma única vez e execute em qualquer lugar. Na versão 12.0.0, lançada em fevereiro de 2026, o Ibis se consolidou como a camada de abstração preferida para times que prototipam localmente e implantam em warehouses na nuvem. Neste guia, mostro como usei o Ibis em projetos com mais de 4000 modelos analíticos e por que ele virou parte permanente do meu stack.
Ibis 12.0.0 (fev/2026) é um query compiler: converte código Python em SQL específico para cada backend usando SQLGlot, sem executar nada por conta própria.
Com uma única linha de código você troca o backend (DuckDB local para BigQuery ou Snowflake em produção) mantendo a mesma expressão de DataFrame.
Suporta mais de 20 backends: DuckDB (padrão), BigQuery, Snowflake, PostgreSQL, MySQL, Trino, ClickHouse, Databricks, Polars, DataFusion, PySpark e outros.
Avaliação é preguiçosa (lazy): o plano de execução é otimizado pelo backend, entregando performance de DuckDB, BigQuery ou Snowflake sem overhead de tradução.
O backend Pandas foi removido na v12: a partir de agora, DuckDB é o padrão para execução local.
O IbisML estende o padrão para pipelines de feature engineering portáteis entre notebooks de treino e SQL de produção.
O que é o Ibis Framework em Python?
Ibis é uma biblioteca Python que oferece uma API única de DataFrame (parecida com Pandas, mas com semântica preguiçosa) e a compila em SQL nativo para o motor onde você quiser executar. Diferente de Pandas ou Polars, o Ibis não é um engine: ele é um query compiler que traduz expressões Python em SQL otimizado usando SQLGlot como camada de tradução de dialetos. A execução acontece inteiramente no backend escolhido (DuckDB, BigQuery, Snowflake, PostgreSQL e assim por diante), o que significa que a performance é a do próprio backend, sem penalidade da camada Python.
Sinceramente, em nove anos trabalhando com warehouses analíticos, vi times reescreverem a mesma lógica de negócio três vezes: uma em Pandas para o notebook de exploração, outra em SQL para o dbt de produção e uma terceira em PySpark para o pipeline batch. Cada reescrita é um custo de manutenção e uma oportunidade de divergência silenciosa entre ambientes. O Ibis resolve esse problema fundamental: você escreve a expressão uma vez em Python e ela roda idêntica no seu laptop com DuckDB e no BigQuery em produção. O projeto é governado de forma independente pela comunidade e mantido por contribuidores de empresas como Voltron Data, Google, Microsoft e Databricks.
Como instalar o Ibis em 2026
A instalação em 2026 exige explicitar quais backends você quer usar. O pacote base é enxuto e não puxa drivers automaticamente. A versão estável mais recente é a ibis-framework 12.0.0, lançada em 7 de fevereiro de 2026, e requer Python 3.10 ou superior. Consulte as notas de release oficiais do Ibis para o histórico completo de mudanças.
# DuckDB (backend padrão) + dados de exemplo
pip install "ibis-framework[duckdb,examples]"
# BigQuery para produção
pip install "ibis-framework[bigquery]"
# Snowflake
pip install "ibis-framework[snowflake]"
# Combinação típica: dev local + prod na nuvem
pip install "ibis-framework[duckdb,bigquery,polars]"
Verifique a instalação em um REPL Python:
import ibis
print(ibis.__version__) # 12.0.0
con = ibis.duckdb.connect() # cria uma instância em memória
print(con.list_tables()) # []
Primeiro exemplo: DataFrame portátil com DuckDB
Vamos partir de um caso concreto que aparece em quase todo projeto analítico: agregar vendas por região e categoria, filtrando períodos e ordenando por receita total. Uso um dataset de exemplo distribuído com o Ibis para manter o código reproduzível.
import ibis
from ibis import _
ibis.options.interactive = True # imprime resultados como tabelas
con = ibis.duckdb.connect()
pedidos = ibis.examples.penguins.fetch(backend=con)
# Expressão preguiçosa: nada executa ainda
resumo = (
pedidos
.filter(_.year == 2008)
.group_by(["species", "island"])
.aggregate(
contagem=_.count(),
peso_medio=_.body_mass_g.mean(),
bico_max=_.bill_length_mm.max(),
)
.order_by(_.peso_medio.desc())
)
# Só neste ponto o SQL é gerado e enviado ao DuckDB
print(resumo.execute())
O objeto _ é o underscore expression, um atalho para referenciar colunas da tabela em construção, equivalente ao col() do PySpark ou ao pl.col() do Polars. Ele mantém o código mais legível quando as expressões se aninham.
Para inspecionar o SQL que o Ibis gera antes de executar, use ibis.to_sql():
print(ibis.to_sql(resumo))
# SELECT species, island, COUNT(*) AS contagem, ...
# FROM penguins WHERE year = 2008
# GROUP BY 1, 2 ORDER BY peso_medio DESC
Essa transparência é fundamental para code review e depuração: você sempre pode ver exatamente o SQL que atinge o warehouse. Para uma revisão do modelo mental de DataFrames em Python, veja também nosso guia sobre como migrar do Pandas para o Polars.
Como migrar do DuckDB para o BigQuery com uma linha
Este é o argumento matador do Ibis. A mesma expressão que rodou contra DuckDB em memória roda contra BigQuery mudando apenas o objeto de conexão. Nenhuma linha da lógica analítica precisa ser tocada. Em projetos que arquitetei em clientes enterprise, essa portabilidade eliminou a categoria inteira de bugs onde o dev prototipa em Pandas e o SQL de produção diverge sutilmente.
# Local (dev)
con = ibis.duckdb.connect("./analytics.db")
# Produção: trocar apenas esta linha
# con = ibis.bigquery.connect(
# project_id="meu-projeto-prd",
# dataset_id="analytics",
# )
pedidos = con.table("pedidos")
receita = (
pedidos
.filter(_.data_pedido >= "2026-01-01")
.group_by([_.regiao, _.categoria])
.aggregate(
receita_total=_.valor.sum(),
ticket_medio=_.valor.mean(),
num_pedidos=_.count(),
)
.order_by(_.receita_total.desc())
.limit(50)
)
resultado = receita.execute() # retorna PyArrow / Pandas
Ibis vs Pandas vs Polars: quando usar cada um
Uma pergunta que recebo com frequência: se o Polars já é rápido e o Pandas já é familiar, para que serve o Ibis? A resposta curta é que eles resolvem problemas diferentes. Pandas é uma biblioteca de manipulação de dados em memória; Polars é uma biblioteca de execução em Rust otimizada para uma máquina; Ibis é uma camada de abstração que gera SQL para o motor onde os dados já estão. Quando seus dados vivem em um warehouse com terabytes, você não quer trazê-los para a memória local. Você quer empurrar a computação para junto dos dados.
Característica
Pandas
Polars
Ibis
Linguagem núcleo
Python / NumPy
Rust
Python (gera SQL)
Avaliação
Ansiosa (eager)
Preguiçosa + Ansiosa
Preguiçosa
Backends
Apenas memória
Uma máquina
20+ backends
Paralelismo
Limitado
Multi-core nativo
Depende do backend
Uso de memória
5–10× o dataset
2–4× o dataset
Zero (roda no warehouse)
Melhor para
Protótipos, ML clássico
Análises locais rápidas
Pipelines portáteis warehouse-first
Na prática, uso os três em conjunto. Pandas continua sendo a lingua franca para receber dados de bibliotecas ML como scikit-learn. Polars é minha escolha para transformações em máquinas isoladas quando o dataset cabe em RAM. E Ibis é o que amarra tudo: uma expressão Ibis pode ser materializada para Polars, para PyArrow ou para um write em BigQuery com a mesma chamada. Se sua análise atual gera outliers e você quer detectá-los antes de agregar, veja também nosso guia sobre detecção de outliers em Python.
Quais backends o Ibis suporta em 2026?
Na versão 12, o Ibis suporta oficialmente mais de 20 backends, divididos em dois grupos: motores que geram SQL e motores baseados em DataFrames. A cobertura da API varia por backend (funções de janela, tipos aninhados e operações geoespaciais nem sempre têm equivalente em todos os dialetos), mas o núcleo comum de filtros, agregações, joins, ordenações e subqueries é universal.
Motores SQL suportados
DuckDB: padrão para dev local, altíssima performance, formato Parquet nativo.
BigQuery: warehouse serverless do Google Cloud, ideal para escalas de petabytes.
Snowflake: warehouse elástico da nuvem, muito usado em enterprise.
PostgreSQL e MySQL: bancos transacionais para casos operacionais.
ClickHouse: banco colunar OLAP para análises em tempo real.
Trino / Presto: engine federada para data lakes.
Databricks: plataforma unificada de dados e ML.
Microsoft SQL Server, Oracle, SQLite, Impala, SingleStoreDB.
Motores de DataFrame suportados
Polars: para quem quer performance local sem sair do ecossistema Python.
DataFusion: engine em Rust similar ao DuckDB.
PyArrow: para pipelines colunares em memória.
PySpark: para escala distribuída em cluster.
Para uma introdução aprofundada ao motor de execução padrão do Ibis, veja nosso guia sobre DuckDB com Python. E consulte a lista canônica de backends nos docs oficiais do Ibis.
Como funciona a avaliação preguiçosa do Ibis
Toda expressão Ibis é uma árvore de operações (IR, ou intermediate representation) que só é traduzida em SQL e enviada ao backend quando você chama .execute(), .to_pandas(), .to_polars() ou .to_pyarrow(). Isso permite que o Ibis (e depois o backend) otimizem o plano completo: predicate pushdown, column pruning, reordenação de joins e materialização preguiçosa de subexpressões, tudo antes de qualquer byte ser lido do disco.
expr = (
con.table("eventos")
.filter(_.timestamp >= "2026-07-01")
.join(con.table("usuarios"), "user_id")
.group_by(_.pais)
.aggregate(receita=_.valor.sum())
)
# Nada aconteceu ainda: expr é só um plano
print(type(expr)) # <class 'ibis.expr.types.relations.Table'>
# Inspeciona o plano
print(expr.op())
# Materializa em Pandas
df = expr.to_pandas()
# Ou em Polars sem intermediário Pandas
df_polars = expr.to_polars()
Feature engineering portátil com IbisML
O IbisML é uma extensão que aplica o mesmo princípio de portabilidade a pipelines de feature engineering. Você define transformações uma vez (normalização, one-hot encoding, binning, criação de lags temporais) e o mesmo pipeline gera features em SQL no warehouse durante o treinamento e durante a inferência em produção. Isso elimina a training/serving skew, ou seja, a divergência sutil entre features de treino e produção que arruína modelos sem que ninguém perceba de imediato.
import ibis
from ibis_ml import Recipe, steps
con = ibis.duckdb.connect()
train = con.table("clientes_treino")
recipe = Recipe(
steps.ImputeMean(["renda", "idade"]),
steps.OneHotEncode(["cidade", "canal"]),
steps.ScaleStandard(["renda", "idade"]),
)
recipe.fit(train, outcomes=["churn"])
# Aplica em produção: mesmo pipeline, backend diferente
prod_con = ibis.bigquery.connect(project_id="ml-prod")
inference = prod_con.table("clientes_ativos")
features = recipe.transform(inference)
Para times que usam Great Expectations para garantir qualidade de dados antes do feature engineering, o Ibis se encaixa naturalmente como camada de transformação. Veja também nosso guia sobre Great Expectations em Python para o passo anterior do pipeline.
Armadilhas práticas e boas práticas
Cobertura de API varia por backend
Nem toda operação Ibis funciona em todo backend. Funções de janela avançadas, tipos ARRAY aninhados e certas funções de string têm gaps. Antes de escrever uma transformação crítica, teste-a no backend de produção real, não apenas no DuckDB local. Use con.has_operation(ops.RegexExtract) para checagens programáticas.
Sempre teste com dados reais do warehouse em CI
DuckDB é ótimo para testes rápidos, mas a semântica de tipos diverge sutilmente do BigQuery, especialmente para TIMESTAMP WITH TIME ZONE, valores nulos em colunas numéricas e casts implícitos. Rode uma test suite menor contra o warehouse real em CI antes de fazer deploy. Os custos são baixos e a confiança que você ganha compensa. Eu aprendi isso da forma mais dolorosa: um cast implícito de NUMERIC em BigQuery quebrou uma agregação de receita que passava tranquilamente no DuckDB.
Não confunda "preguiçoso" com "grátis"
Quando você chama .execute() contra BigQuery, cada consulta é uma query cobrada. Encadeie transformações no mesmo plano em vez de chamar .execute() em cada passo. Se precisar reutilizar um resultado intermediário, materialize-o explicitamente com .cache() ou salve como tabela temporária.
Use dbt e Ibis juntos, não competindo
Ibis não substitui dbt. Ele é excelente para lógica analítica dinâmica em Python (pipelines de ML, geração programática de queries, exploração), enquanto dbt continua imbatível para modelagem declarativa de warehouse, contratos de coluna e lineage estático. Nos projetos que arquitetei, dbt manda no warehouse layer (staging → intermediate → marts) e Ibis manda no application layer (notebooks, dashboards dinâmicos, pipelines ML).
Perguntas frequentes
Ibis substitui o Pandas?
Não. Ibis e Pandas resolvem problemas diferentes. Pandas é uma biblioteca de execução em memória para uma única máquina; Ibis é um compilador de queries que envia a computação para o backend onde os dados estão. Você pode (e deve) usar os dois. Expressões Ibis podem ser materializadas em DataFrames Pandas com .to_pandas() quando você precisa passá-los para scikit-learn ou outra biblioteca do ecossistema.
Qual a diferença entre Ibis e SQLAlchemy?
SQLAlchemy é um ORM focado em modelagem de dados transacionais e mapeamento objeto-relacional. Ibis é uma API de DataFrame analítica orientada a expressões (não há noção de "objetos de linha"). Para relatórios, agregações e pipelines de ML, a API do Ibis é muito mais concisa e alinhada com como analistas pensam sobre dados.
Ibis é mais rápido que o Pandas?
Sim. Quando executado em backends locais como DuckDB, DataFusion ou Polars, o Ibis é ordens de magnitude mais rápido que o Pandas e usa muito menos memória, porque a execução ocorre em engines colunares otimizados. Quando executado em warehouses na nuvem, a performance é a do próprio warehouse, sem overhead perceptível da tradução.
Posso usar Ibis com Jupyter Notebook?
Sim, e é onde ele brilha. Ative ibis.options.interactive = True no início do notebook para que cada expressão seja avaliada e renderizada como tabela automaticamente. Combinado com DuckDB, você tem uma experiência de exploração similar ao Pandas, mas com performance muito superior em datasets maiores.
O Ibis funciona offline?
Sim, usando backends locais como DuckDB, SQLite, Polars ou DataFusion. Você pode desenvolver e testar toda a lógica sem conexão com internet e depois trocar apenas o objeto de conexão para o BigQuery ou Snowflake em produção. A lógica analítica permanece inalterada.
Marcus is an analytics engineer with 9 years in the dbt and warehouse-modeling trenches. He spent three years at dbt Labs as a senior solutions architect helping enterprise customers (a large US bank, two telecom carriers) untangle 4000-model projects, and before that ran the analytics platform at HelloFresh's North America org where he rebuilt the supply-chain mart on Snowflake + dbt.
His writing focuses on dbt project structure at scale, incremental model patterns that actually survive backfills, and the unglamorous work of column-level lineage and contract testing. He is a regular contributor to the dbt-utils package and co-maintains a small open-source linter for SQL style.
Marcus lives in Berlin, holds a master's in statistics from UNC Chapel Hill, and roasts his own coffee badly.
Aprenda a usar Narwhals para escrever código Python compatível com Pandas, Polars, PyArrow, Modin, cuDF e Dask sem duplicação. Guia 2026 com exemplos práticos, benchmarks e três padrões de produção que uso no dia a dia.
Marimo é o notebook Python reativo que substitui o Jupyter: instale, use SQL com DuckDB, publique no navegador via WASM e versione tudo no Git com arquivos .py puros.
Pydantic 2 é a biblioteca padrão para validar dados em pipelines Python: BaseModel, TypeAdapter, uniões discriminadas e Settings com um núcleo em Rust até 17× mais rápido que a v1. Este guia mostra os padrões que uso em produção em 2026.