Skip to main content

XREXPR: Xarray Expression Rewriter

Imagine you have an xarray dataset that you want to do some analysis on. You might write something like this:

%%timeit
ds.mean(dim="lat").mean(dim="lon").isel(time=0).compute()

193 ms ± 49.6 ms per loop (mean ± std. dev. of 5 runs, 5 loops each)

However, it would be a lot faster if you instead wrote:

ds.isel(time=0).mean(dim="lat").mean(dim="lon").compute()

925 μs ± 401 μs per loop (mean ± std. dev. of 5 runs, 5 loops each)

In this instance, just reordering the operations makes a ~200x performance difference. We can see that these two expressions are equivalent, but unfortunately, xarray can't automatically reorder them for us (yet?).

from xarray.testing import assert_equal
assert_equal(
    ds.isel(time=0).mean(dim="lat").mean(dim="lon"),
    ds.mean(dim="lat").mean(dim="lon").isel(time=0),
)

# Does not raise an AssertionError

That's where xrexpr comes in. Importing it registers a .plan accessor on every Dataset. Chain your operations off ds.plan exactly as you would off ds — but instead of running eagerly, each call is recorded. Calling .collect() optimises the recorded plan (reordering and merging where it's provably safe) and replays it:

import xrexpr  # registers the ``.plan`` accessor

result = ds.plan.mean(dim="lat").mean(dim="lon").isel(time=0).collect()

(.compute() is a synonym for .collect(), if that's the terminal your fingers reach for.)

xrexpr pushes the isel in front of the reductions for you, so .collect() runs the fast ordering while you keep writing the readable one. The result is exactly what the eager chain would have produced:

assert_equal(result, ds.mean(dim="lat").mean(dim="lon").isel(time=0)).compute()

Seeing the rewrite

Use .explain() to see the optimised plan without running it:

>>> print(ds.plan.mean(dim="lat").mean(dim="lon").isel(time=0).explain())
plan (3 ops):
  1. isel(time=0)
  2. mean(dim='lat')
  3. mean(dim='lon')

The isel has been hoisted to the front — that's the reorder that buys the speed-up.

How it optimises

xrexpr records each call as a normalised operation against a cheap logical schema (dims and sizes, never the array data), then rewrites the plan to a fixpoint with a few local, result-preserving rules:

  • merge consecutive isel/sel selections into a single indexer;
  • push a selection left past any reduction (mean, sum, std, ...) whose dims it doesn't touch, so the reduction scans a smaller array.

A selection that indexes a dimension a reduction has already removed can never run — for example ds.plan.mean(dim="lon").isel(lon=0) — so xrexpr raises InvalidExpressionError at .collect() (or .explain()) instead of failing deep inside xarray:

from xrexpr import InvalidExpressionError

try:
    ds.plan.mean(dim="lon").isel(lon=0).collect()
except InvalidExpressionError:
    ...

Scans (cumsum, cumprod, diff) are order-sensitive, so a selection on the scanned dimension is left exactly where you put it.


This package is just making its way out of the proof-of-concept stage, so expect some issues. It is also unlikely to support the full range of xarray operations for some time. If it doesn't do anything for you, please open an issue!

Release files for xrexpr 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for xrexpr 0.1.1
File Size Uploaded
xrexpr-0.1.1.tar.gz 40.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for xrexpr 0.1.1
File Interpreter ABI Platform
xrexpr-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size:54.7 kB

Release files / xrexpr-0.1.1.tar.gz

Download URL xrexpr-0.1.1.tar.gz
Size 40.3 kB
Tags Source
SHA-256 checksum
How to use checksums
604da66c67081e9b98146095b6ec3aeb8a09b42e59d5f02c6b46826ede003c33
BLAKE2b-256 checksum
How to use checksums
e542df0513fce5883a0786b89058d078ccb8296025d4adc08cf3fc0756848f8a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.

Transparency log

Release files / xrexpr-0.1.1-py3-none-any.whl

Download URL xrexpr-0.1.1-py3-none-any.whl
Size 14.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b2f088fa842092b6d7e678d57bd5c42b3d51ab8a344b0859bbb5aacaab0eeb83
BLAKE2b-256 checksum
How to use checksums
b65acdda0b37f4184f6a9a9d98e03b28b814c4229c910dc9fcaf8baa7d262170
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.1 This release

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page