Skip to content
kd
25 Aug 2024
Back to blog

Pandas vs Polars vs ibis

3 min read (472words)

Table of contents

This is a short comparison of idiomatic pandas, polars and ibis, all using method chaining.

import sys
sys.version_info

:::div{.cell-output .cell-output-display} sys.version_info(major=3, minor=12, micro=5, releaselevel='final', serial=0) ::: ::::

import ibis
from ibis import _
ibis.options.interactive = True
ibis.__version__

:::div{.cell-output .cell-output-display} '9.4.0' ::: ::::

df = ibis.examples.starwars.fetch()
df
┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name               ┃ height ┃ mass    ┃ hair_color    ┃ skin_color  ┃ eye_color ┃ birth_year ┃ sex    ┃ gender    ┃ homeworld ┃ species ┃ films  ┃ vehicles ┃ starships ┃
┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
│ string             │ int64  │ float64 │ string        │ string      │ string    │ float64    │ string │ string    │ string    │ string  │ string │ string   │ string    │
├────────────────────┼────────┼─────────┼───────────────┼─────────────┼───────────┼────────────┼────────┼───────────┼───────────┼─────────┼────────┼──────────┼───────────┤
│ Luke Skywalker     │    172 │    77.0 │ blond         │ fair        │ blue      │       19.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ C-3PO              │    167 │    75.0 │ NULL          │ gold        │ yellow    │      112.0 │ none   │ masculine │ Tatooine  │ Droid   │ NULL   │ NULL     │ NULL      │
│ R2-D2              │     96 │    32.0 │ NULL          │ white, blue │ red       │       33.0 │ none   │ masculine │ Naboo     │ Droid   │ NULL   │ NULL     │ NULL      │
│ Darth Vader        │    202 │   136.0 │ none          │ white       │ yellow    │       41.9 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Leia Organa        │    150 │    49.0 │ brown         │ light       │ brown     │       19.0 │ female │ feminine  │ Alderaan  │ Human   │ NULL   │ NULL     │ NULL      │
│ Owen Lars          │    178 │   120.0 │ brown, grey   │ light       │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Beru Whitesun Lars │    165 │    75.0 │ brown         │ light       │ blue      │       47.0 │ female │ feminine  │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ R5-D4              │     97 │    32.0 │ NULL          │ white, red  │ red       │       NULL │ none   │ masculine │ Tatooine  │ Droid   │ NULL   │ NULL     │ NULL      │
│ Biggs Darklighter  │    183 │    84.0 │ black         │ light       │ brown     │       24.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Obi-Wan Kenobi     │    182 │    77.0 │ auburn, white │ fair        │ blue-gray │       57.0 │ male   │ masculine │ Stewjon   │ Human   │ NULL   │ NULL     │ NULL      │
│ …                  │      … │       … │ …             │ …           │ …         │          … │ …      │ …         │ …         │ …       │ …      │ …        │ …         │
└────────────────────┴────────┴─────────┴───────────────┴─────────────┴───────────┴────────────┴────────┴───────────┴───────────┴─────────┴────────┴──────────┴───────────┘

::: ::::

Pandas

import re
M = 100
_df = (
df
.to_pandas()
.rename(lambda col: re.sub(r'(?<!^)(?=[A-Z])', '_', col).lower(), axis=1)
.query("height > 100")
.query("mass > @M")
.query('hair_color.str.contains("brown") == True') # some values can be `None` so explicit True required
.query("sex.notna()")
.assign(hair_color = lambda c: c.hair_color.str.split(','))
.explode("hair_color")
.query("birth_year.notna()")
)
ibis.pandas.connect({'_df': _df}).table('_df')
┏━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name      ┃ height  ┃ mass    ┃ hair_color ┃ skin_color ┃ eye_color ┃ birth_year ┃ sex    ┃ gender    ┃ homeworld ┃ species ┃ films ┃ vehicles ┃ starships ┃
┡━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
│ string    │ float64 │ float64 │ string     │ string     │ string    │ float64    │ string │ string    │ string    │ string  │ null  │ null     │ null      │
├───────────┼─────────┼─────────┼────────────┼────────────┼───────────┼────────────┼────────┼───────────┼───────────┼─────────┼───────┼──────────┼───────────┤
│ Owen Lars │   178.0 │   120.0 │ brown      │ light      │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL  │ NULL     │ NULL      │
│ Owen Lars │   178.0 │   120.0 │  grey      │ light      │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL  │ NULL     │ NULL      │
│ Chewbacca │   228.0 │   112.0 │ brown      │ unknown    │ blue      │      200.0 │ male   │ masculine │ Kashyyyk  │ Wookiee │ NULL  │ NULL     │ NULL      │
└───────────┴─────────┴─────────┴────────────┴────────────┴───────────┴────────────┴────────┴───────────┴───────────┴─────────┴───────┴──────────┴───────────┘

::: ::::

Polars

import re
import polars as pl
M = 100
_df = (
df
.to_polars()
.rename(lambda col: re.sub(r'(?<!^)(?=[A-Z])', '_', col).lower())
.filter(pl.col("height") > 100)
.filter(pl.col("mass") > M)
.filter(pl.col("hair_color").str.contains("brown"))
.filter(pl.col("sex").is_not_null())
.with_columns(pl.col("hair_color").str.split(",").alias("hair_color"))
.explode(pl.col("hair_color"))
.filter(pl.col("birth_year").is_not_null())
)
ibis.polars.connect({'_df': _df}).table('_df')
┏━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name      ┃ height ┃ mass    ┃ hair_color ┃ skin_color ┃ eye_color ┃ birth_year ┃ sex    ┃ gender    ┃ homeworld ┃ species ┃ films  ┃ vehicles ┃ starships ┃
┡━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
│ string    │ int64  │ float64 │ string     │ string     │ string    │ float64    │ string │ string    │ string    │ string  │ string │ string   │ string    │
├───────────┼────────┼─────────┼────────────┼────────────┼───────────┼────────────┼────────┼───────────┼───────────┼─────────┼────────┼──────────┼───────────┤
│ Owen Lars │    178 │   120.0 │ brown      │ light      │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Owen Lars │    178 │   120.0 │  grey      │ light      │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Chewbacca │    228 │   112.0 │ brown      │ unknown    │ blue      │      200.0 │ male   │ masculine │ Kashyyyk  │ Wookiee │ NULL   │ NULL     │ NULL      │
└───────────┴────────┴─────────┴────────────┴────────────┴───────────┴────────────┴────────┴───────────┴───────────┴─────────┴────────┴──────────┴───────────┘

::: ::::

ibis

M = 100
_df = (
df
.rename("snake_case")
.filter(_.height > 100)
.filter(_.mass > M)
.filter(_.hair_color.contains("brown"))
.filter(_.sex.notnull())
.mutate(hair_color = _.hair_color.split(","))
.unnest(_.hair_color)
.drop_null(_.birth_year)
)
_df
┏━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━┓
┃ name      ┃ height ┃ mass    ┃ hair_color ┃ skin_color ┃ eye_color ┃ birth_year ┃ sex    ┃ gender    ┃ homeworld ┃ species ┃ films  ┃ vehicles ┃ starships ┃
┡━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━┩
│ string    │ int64  │ float64 │ string     │ string     │ string    │ float64    │ string │ string    │ string    │ string  │ string │ string   │ string    │
├───────────┼────────┼─────────┼────────────┼────────────┼───────────┼────────────┼────────┼───────────┼───────────┼─────────┼────────┼──────────┼───────────┤
│ Owen Lars │    178 │   120.0 │ brown      │ light      │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Owen Lars │    178 │   120.0 │  grey      │ light      │ blue      │       52.0 │ male   │ masculine │ Tatooine  │ Human   │ NULL   │ NULL     │ NULL      │
│ Chewbacca │    228 │   112.0 │ brown      │ unknown    │ blue      │      200.0 │ male   │ masculine │ Kashyyyk  │ Wookiee │ NULL   │ NULL     │ NULL      │
└───────────┴────────┴─────────┴────────────┴────────────┴───────────┴────────────┴────────┴───────────┴───────────┴─────────┴────────┴──────────┴───────────┘

::: ::::


Citation

@online{krishnamurthy2024pandasvspolarsvsibis,
  author = {Dheepak Krishnamurthy},
  title = {Pandas vs Polars vs ibis},
  year = {2024},
  date = {2024-08-25},
  url = {https://kdheepak.com/blog/pandas-vs-polars-vs-ibis/},
  langid = {en},
}

For attribution, please cite this work as:

Dheepak Krishnamurthy, "Pandas vs Polars vs ibis", August 25, 2024https://kdheepak.com/blog/pandas-vs-polars-vs-ibis/


Power Lines with `ibis`
The Basic Building blocks of Ratatui - Part 5