---
title: "Ibis"
description: "Ibis is a Python dataframe library that lets you write a single portable API and execute it against 20+ backends, with DuckDB as its default local engine."
canonical: "https://motherduck.com/glossary/ibis/"
related:
  - title: "DuckDB vs Pandas vs Polars for Python Developers"
    url: "https://motherduck.com/blog/duckdb-versus-pandas-versus-polars/"
  - title: "Ibis: One Library To Query Any Backend | MotherDuck"
    url: "https://motherduck.com/videos/ibis-one-library-to-query-any-backend/"
  - title: "Run TPC-DS Models on DuckLake with dbt | MotherDuck Docs"
    url: "https://motherduck.com/docs/cookbook/dbt-ducklake/"
---

# Ibis

> Ibis is a Python dataframe library that lets you write a single portable API and execute it against 20+ backends, with DuckDB as its default local engine.

## Overview

Ibis is a Python library that provides a deferred, expression-based dataframe API: you write Python code that looks like pandas or SQL, and Ibis compiles it into the native query language of whichever backend you're targeting. The same Ibis code can run against DuckDB, Snowflake, BigQuery, Postgres, Spark, Trino, Polars, and many others without rewriting the query, which makes it useful for portable analytics code and for prototyping locally before pointing at a production warehouse.

## Why DuckDB is the default backend

DuckDB is Ibis's default backend for local, in-memory execution. When you call `ibis.connect()` or `ibis.duckdb.connect()` with no other backend configured, Ibis spins up an in-process DuckDB database. This pairing works well because DuckDB is fast, embeddable, and has no server to manage, so Ibis expressions can be developed and tested instantly on a laptop and later pointed at a distributed backend with the same code.

```python
import ibis

con = ibis.duckdb.connect()  # in-memory DuckDB
t = con.read_parquet("orders.parquet")

result = (
    t.filter(t.status == "completed")
     .group_by(t.customer_id)
     .aggregate(total=t.amount.sum())
     .order_by(ibis.desc("total"))
)

df = result.to_pandas()
```

Because Ibis expressions are lazy, nothing executes until you call a method like `.to_pandas()`, `.execute()`, or `.to_pyarrow()` — Ibis pushes the whole computation down into DuckDB's query engine rather than pulling rows into Python row by row.

## Practical use

Ibis is popular for building backend-agnostic analytics libraries, for interactively exploring large tables without loading them fully into memory, and for teams that want pandas-like ergonomics with SQL-engine performance and scale.
