Ibis Data Interface
Ibis provides a database-agnostic Python DataFrame API. Write queries once in Python; Ibis translates them to optimized SQL for the connected backend (DuckDB, PostgreSQL, SQLite, etc.).
Why Ibis
- Portability: Develop with DuckDB, deploy against PostgreSQL -- change only the connection
- Lazy evaluation: Operations build an expression tree; nothing executes until
.execute()
- Full SQL power: Window functions, CTEs, joins, aggregations -- all through Python
- No ORM: You get SQL performance without SQL strings
Connecting
import ibis
# DuckDB (default for local/parquet work)
con = ibis.duckdb.connect()
con = ibis.duckdb.connect("my.duckdb")
# PostgreSQL
con = ibis.postgres.connect(host="localhost", database="mydb", user="user", password="pass")
# SQLite
con = ibis.sqlite.connect("my.sqlite")
# Read files directly
table = con.read_parquet("data.parquet")
table = con.read_csv("data.csv")
Core Operations
# Explore
table.schema() # column names and types
table.head(10) # preview rows
table.describe() # summary statistics
table.count().execute() # row count
# Select and filter
selected = table.select("id", "amount", "date")
filtered = table.filter((table.amount > 100) & (table.date >= "2024-01-01"))
sorted_data = table.order_by(table.amount.desc())
# Transform
enriched = table.mutate(
revenue=table.quantity * table.unit_price,
year=table.date.year(),
size=ibis.case()
.when(table.amount < 100, "small")
.when(table.amount < 1000, "medium")
.else_("large")
.end()
)
# Aggregate
summary = (
table.group_by("category")
.aggregate(
total=table.amount.sum(),
avg=table.amount.mean(),
count=table.count()
)
)
# Join
joined = (
orders
.join(customers, orders.customer_id == customers.id, how="left")
.select(orders.order_id, orders.amount, customers.name)
)
# Window functions
ranked = table.mutate(
rank=table.amount.rank().over(
ibis.window(group_by="category", order_by=table.amount.desc())
)
)
# Execute and export
df = summary.execute() # -> pandas DataFrame
con.to_parquet(summary, "out.parquet")
df.to_csv("out.csv", index=False)
API Reference
| API |
What it covers |
| Table expressions |
select, filter, mutate, group_by, agg, join, order_by |
| Selectors |
Choose columns by name, type, or regex |
| Generic expressions |
.cast(), .isnull(), .fillna(), case(), .ifelse() |
| Numeric expressions |
sum(), mean(), std(), rounding, logarithms |
| String expressions |
Slicing, regex, case conversion, stripping |
| Temporal expressions |
.year(), .month(), interval arithmetic, formatting |
| Collection expressions |
Array/map operations, unnesting |
| JSON expressions |
Path-based extraction from JSON columns |
Best Practices
- Filter early: Reduce data volume before aggregations
- Stay lazy: Chain operations before calling
.execute()
- Use selectors: Apply operations to multiple columns programmatically
- Handle nulls: Check with
.isnull() and handle with .fillna() explicitly
- Check SQL: Use
ibis.to_sql(expr) to inspect generated queries
Installation
uv add "ibis-framework[duckdb]" # DuckDB backend
uv add "ibis-framework[postgres]" # PostgreSQL backend
1---2name: ibis-data3description: Use Ibis for database-agnostic data access in Python. Use when writing data queries, connecting to databases (DuckDB, PostgreSQL, SQLite), or building portable data pipelines that should work across backends.4---56# Ibis Data Interface78[Ibis](https://ibis-project.org) provides a database-agnostic Python DataFrame API. Write queries once in Python; Ibis translates them to optimized SQL for the connected backend (DuckDB, PostgreSQL, SQLite, etc.).910## Why Ibis1112- **Portability**: Develop with DuckDB, deploy against PostgreSQL -- change only the connection13- **Lazy evaluation**: Operations build an expression tree; nothing executes until `.execute()`14- **Full SQL power**: Window functions, CTEs, joins, aggregations -- all through Python15- **No ORM**: You get SQL performance without SQL strings1617## Connecting1819```python20import ibis2122# DuckDB (default for local/parquet work)23con = ibis.duckdb.connect()24con = ibis.duckdb.connect("my.duckdb")2526# PostgreSQL27con = ibis.postgres.connect(host="localhost", database="mydb", user="user", password="pass")2829# SQLite30con = ibis.sqlite.connect("my.sqlite")3132# Read files directly33table = con.read_parquet("data.parquet")34table = con.read_csv("data.csv")35```3637## Core Operations3839```python40# Explore41table.schema() # column names and types42table.head(10) # preview rows43table.describe() # summary statistics44table.count().execute() # row count4546# Select and filter47selected = table.select("id", "amount", "date")48filtered = table.filter((table.amount > 100) & (table.date >= "2024-01-01"))49sorted_data = table.order_by(table.amount.desc())5051# Transform52enriched = table.mutate(53 revenue=table.quantity * table.unit_price,54 year=table.date.year(),55 size=ibis.case()56 .when(table.amount < 100, "small")57 .when(table.amount < 1000, "medium")58 .else_("large")59 .end()60)6162# Aggregate63summary = (64 table.group_by("category")65 .aggregate(66 total=table.amount.sum(),67 avg=table.amount.mean(),68 count=table.count()69 )70)7172# Join73joined = (74 orders75 .join(customers, orders.customer_id == customers.id, how="left")76 .select(orders.order_id, orders.amount, customers.name)77)7879# Window functions80ranked = table.mutate(81 rank=table.amount.rank().over(82 ibis.window(group_by="category", order_by=table.amount.desc())83 )84)8586# Execute and export87df = summary.execute() # -> pandas DataFrame88con.to_parquet(summary, "out.parquet")89df.to_csv("out.csv", index=False)90```9192## API Reference9394| API | What it covers |95| --- | --- |96| [Table expressions](https://ibis-project.org/reference/expression-tables) | `select`, `filter`, `mutate`, `group_by`, `agg`, `join`, `order_by` |97| [Selectors](https://ibis-project.org/reference/selectors) | Choose columns by name, type, or regex |98| [Generic expressions](https://ibis-project.org/reference/expression-generic) | `.cast()`, `.isnull()`, `.fillna()`, `case()`, `.ifelse()` |99| [Numeric expressions](https://ibis-project.org/reference/expression-numeric) | `sum()`, `mean()`, `std()`, rounding, logarithms |100| [String expressions](https://ibis-project.org/reference/expression-string) | Slicing, regex, case conversion, stripping |101| [Temporal expressions](https://ibis-project.org/reference/expression-temporal) | `.year()`, `.month()`, interval arithmetic, formatting |102| [Collection expressions](https://ibis-project.org/reference/expression-collection) | Array/map operations, unnesting |103| [JSON expressions](https://ibis-project.org/reference/expression-json) | Path-based extraction from JSON columns |104105## Best Practices106107- **Filter early**: Reduce data volume before aggregations108- **Stay lazy**: Chain operations before calling `.execute()`109- **Use selectors**: Apply operations to multiple columns programmatically110- **Handle nulls**: Check with `.isnull()` and handle with `.fillna()` explicitly111- **Check SQL**: Use `ibis.to_sql(expr)` to inspect generated queries112113## Installation114115```bash116uv add "ibis-framework[duckdb]" # DuckDB backend117uv add "ibis-framework[postgres]" # PostgreSQL backend118```