Skip to main content

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

  • xarray
  • funcexpr
  • numexpr
  • numpy

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

funcexpr_xr-0.1.0.tar.gz (10.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

funcexpr_xr-0.1.0-py3-none-any.whl (15.5 kB view details)

Uploaded Python 3

File details

Details for the file funcexpr_xr-0.1.0.tar.gz.

File metadata

  • Download URL: funcexpr_xr-0.1.0.tar.gz
  • Upload date:
  • Size: 10.9 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

Hashes for funcexpr_xr-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2680040abf74ea9f580e16308137f954eff0aaef62b01c2f4c06cea7e5a514af
MD5 b0b9c61d8c36ab9da337f7a32de4b612
BLAKE2b-256 588467a78ca2f4587b93513438abff321695bbe1f1c348cf838660a36fd574d9

See more details on using hashes here.

File details

Details for the file funcexpr_xr-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: funcexpr_xr-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.5 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

Hashes for funcexpr_xr-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 29df7f86d623725e50e28098a00543f958981d26e4004e855b3a7e29b7d3a1d4
MD5 437f0ac01d94dfa43ab2d02e18bc67db
BLAKE2b-256 7fe28802e4e2fe026c4f8324900211c0ba7db57668471959b9d7d5c431e141a8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page