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})

Coordinate values are automatically available in expressions by their coordinate name.

da = xr.DataArray([1.0, 2.0, 3.0], dims=["x"], coords={"x": [10.0, 20.0, 30.0]})
result = fxr.evaluate("a * x", ctx={"a": da})
# x coord is injected automatically
# array([ 10.,  40.,  90.])

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.2.tar.gz (15.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.2-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: funcexpr_xr-0.1.2.tar.gz
  • Upload date:
  • Size: 15.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.2.tar.gz
Algorithm Hash digest
SHA256 f2d14d07ab06d6b70810ff452d22315bac11fde2401804a16a4334a676bbf2ee
MD5 0a95dea7912f1fcc0e0d4e99b4a92502
BLAKE2b-256 bd18d097a4be8e792312a2b494f026d695bf7bcb0f5efd0b2dc2e32e75608b19

See more details on using hashes here.

File details

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

File metadata

  • Download URL: funcexpr_xr-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 17.0 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.2-py3-none-any.whl
Algorithm Hash digest
SHA256 c7e4e61aa92a1697f535f71dc1f13cced968b01b0474cba673d0ffaaf3841402
MD5 8696b8c46cdb7ad153b4dd79e1ecd23f
BLAKE2b-256 60de609c178c573947d4b1db66640501c2f6ce6cd52095bb65f3a6a9690101cc

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