xarray.DataArray support layer for funcexpr
Project description
funcexpr-xr
xarray.DataArray support layer for funcexpr.
funcexpr-xr xarray DataArray — axes alignment, interpolation
↓
funcexpr callable registration, type normalization
↓
numexpr fast array evaluation
Installation
pip install funcexpr_xr
Usage
Basic
Pass xarray.DataArray objects directly in ctx. funcexpr-xr handles alignment and delegates to funcexpr with raw ndarrays. The result is returned as a DataArray with aligned coordinates.
import numpy as np
import xarray as xr
import funcexpr_xr as fxr
da1 = xr.DataArray([1.0, 2.0, 3.0], dims=["x"], coords={"x": [1.0, 2.0, 3.0]})
da2 = xr.DataArray([4.0, 5.0, 6.0], dims=["x"], coords={"x": [1.0, 2.0, 3.0]})
result = fxr.evaluate("a + b * 2", ctx={"a": da1, "b": da2})
# <xarray.DataArray (x: 3)>
# array([ 9., 12., 15.])
# Coordinates:
# * x (x) float64 1.0 2.0 3.0
Scalars and ndarrays can be mixed in ctx alongside DataArrays.
result = fxr.evaluate("a * scale + offset", ctx={"a": da1, "scale": 2.0, "offset": 1.0})
Custom callables work the same way as in funcexpr.
def clip(x, lo, hi):
return np.clip(x, lo, hi)
result = fxr.evaluate(
"clip(a, 0.0, 2.5) + b",
ctx={"a": da1, "b": da2},
funcs={"clip": clip},
)
Alignment strategies
fxr.evaluate requires all DataArrays to share the same dims and coordinate values. The alignment parameter controls what happens when coordinates don't match.
fxr.evaluate("a + b", ctx={"a": da1, "b": da2}, alignment="exact") # default
fxr.evaluate("a + b", ctx={"a": da1, "b": da2}, alignment="inner")
fxr.evaluate("a + b", ctx={"a": da1, "b": da2}, alignment="outer")
| Strategy | Behavior |
|---|---|
"exact" |
Requires identical coordinate values. Raises on any mismatch. |
"inner" |
Intersects coordinate values. Raises if intersection is empty. |
"outer" |
Unions coordinate values, fills gaps with NaN. |
Floating-point coordinate errors
Coordinates loaded from CSV or Excel may differ in their binary float representation even when semantically identical (e.g. 0.1 + 0.2 != 0.3). The digits parameter rounds all coordinates to a fixed number of decimal places before alignment.
# coords from two different CSV files may not match exactly
result = fxr.evaluate("a + b", ctx={"a": da1, "b": da2}, digits=10) # default
result = fxr.evaluate("a + b", ctx={"a": da1, "b": da2}, digits=None) # disable
All coordinates are cast to float64 before rounding. The returned DataArray carries the rounded coordinates.
Interpolation
When DataArrays share the same dims but have different coordinate grids, use evaluate_with_interp. All DataArrays are interpolated onto the grid of interp_ref.
da_fine = xr.DataArray([1.0, 2.0, 3.0, 4.0], dims=["x"], coords={"x": [1.0, 2.0, 3.0, 4.0]})
da_coarse = xr.DataArray([10.0, 30.0], dims=["x"], coords={"x": [1.0, 3.0]})
result = fxr.evaluate_with_interp(
"a + b",
ctx={"a": da_fine, "b": da_coarse},
interp_ref="a", # required — no default
digits=10,
)
# result carries the (rounded) coordinates of da_fine
interp_ref is required. There is no default; the choice of reference grid is always explicit.
The returned DataArray carries the rounded coordinates of interp_ref, not the original pre-rounding values. Rounding defines the canonical grid.
Reindex
When DataArrays have different coordinate grids and you want grid-aligned points taken as-is (rather than interpolated), use evaluate_with_reindex. Points that exist in both source and target are taken directly; points that exist only in the target are filled with NaN.
da_fine = xr.DataArray([1.0, 2.0, 3.0, 4.0], dims=["x"], coords={"x": [1.0, 2.0, 3.0, 4.0]})
da_coarse = xr.DataArray([10.0, 30.0], dims=["x"], coords={"x": [1.0, 3.0]})
# reindex only: x=2.0 and x=4.0 have no match in da_coarse -> NaN
result = fxr.evaluate_with_reindex(
"a + b",
ctx={"a": da_fine, "b": da_coarse},
reindex_ref="a", # required — no default
)
When interp=True, interpolation is performed first and the reindexed values are used to fill any NaN that interpolation could not resolve. This guarantees that grid-aligned points are always taken from the source as-is, even when NaN values in the source would otherwise corrupt the interpolation result.
# interp=True: non-matching points are interpolated;
# grid-aligned points are always taken from source exactly
result = fxr.evaluate_with_reindex(
"a + b",
ctx={"a": da_fine, "b": da_coarse},
reindex_ref="a",
interp=True,
)
This is particularly useful when source DataArrays contain NaN at some grid points — interp_like alone would propagate those NaN values into the interpolated result, but evaluate_with_reindex(interp=True) recovers any grid-aligned values that interpolation missed.
Custom alignment strategies
Alignment strategies are stored in a registry and can be extended at runtime.
from funcexpr_xr.alignment import register
import xarray as xr
def my_strategy(
arrays: dict[str, xr.DataArray],
digits: int | None = 10,
) -> dict[str, xr.DataArray]:
...
register("my_strategy", my_strategy)
fxr.evaluate("a + b", ctx={...}, alignment="my_strategy")
Design
funcexpr-xr is intentionally a thin layer. It does not reimplement xarray's alignment or interpolation logic — it wraps xr.align, xr.DataArray.interp_like, and xr.DataArray.reindex_like directly. Custom alignment rules beyond what xarray provides are out of scope.
If your ctx contains no DataArray values, use funcexpr.evaluate directly.
Dependencies
xarrayfuncexprnumexprnumpy
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file funcexpr_xr-0.1.1.tar.gz.
File metadata
- Download URL: funcexpr_xr-0.1.1.tar.gz
- Upload date:
- Size: 15.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1a0dff5e1d9d9cfa4c282cfaec9ddfde787ef57ce11b52a93c2609aeabd0d2a
|
|
| MD5 |
d5643821e242afecb86df66f9de5bf28
|
|
| BLAKE2b-256 |
1b46cf5216078fcf5f85477b64b56ea92a594c7b9a3289d2f595645b668a84eb
|
File details
Details for the file funcexpr_xr-0.1.1-py3-none-any.whl.
File metadata
- Download URL: funcexpr_xr-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a98804d753449ce8c4347c80fd6e7d4ef807c1113f64020ef68561596a211969
|
|
| MD5 |
4d3cb24302a327fe4a25f0a1a8c71fd0
|
|
| BLAKE2b-256 |
72ad9845ed4d5fcbb3c5e0da09b6eaf9079bc87df6ef9094e737a0c3de84db94
|