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.

Ibis Python: DataFrames Portáteis Guia 2026

Atualizado: 1 de Agosto de 2026

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ísticaPandasPolarsIbis
Linguagem núcleoPython / NumPyRustPython (gera SQL)
AvaliaçãoAnsiosa (eager)Preguiçosa + AnsiosaPreguiçosa
BackendsApenas memóriaUma máquina20+ backends
ParalelismoLimitadoMulti-core nativoDepende do backend
Uso de memória5–10× o dataset2–4× o datasetZero (roda no warehouse)
Melhor paraProtótipos, ML clássicoAnálises locais rápidasPipelines 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.

Sobre o Autor Marcus Holloway

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.