meteor_maths.sampling.sampling module#

Module of resampling functions

class meteor_maths.sampling.sampling.RegridCache(x_source: ndarray, y_source: ndarray, x_target: ndarray, y_target: ndarray, regular_grid: bool = False)[source]#

Bases: object

Nearest-neighbour Resampler: precomputed regridding state for a fixed pair of source/target grids - the KDTree over the target grid, the mapping of each source pixel to its nearest target pixel, and the expected-source-count threshold (n_min_source) used to flag under-sampled target pixels. Note that which pixels actually end up flagged invalid also depends on the data being regridded (a NaN source pixel doesn’t count towards a bin’s contributing-pixel count), so that final mask is computed per data array in regrid(), not cached here.

Building a RegridCache is the expensive part of nearest-neighbour resampling. Build one instance for a given (x_source, y_source, x_target, y_target) and reuse it via regrid() to resample multiple data arrays (e.g. different variables, or repeated calls in a loop) on the same grids, instead of recomputing the KDTree and index mapping each time - including across separate calls to resample(), by building a RegridCache upfront and passing it as resampler=.

Two ways of deciding a target pixel’s own footprint (used both to decide which source pixels contribute to it, and how many are required for it to be considered valid) are available, via regular_grid:

  • regular_grid=True: a single grid-wide spacing estimate (median pixel step along each axis) is used for every target pixel. Cheap, but only accurate if both grids really do have uniform, axis-aligned spacing throughout.

  • regular_grid=False (default): each target pixel’s footprint is estimated from its own array-adjacent neighbours instead of a single grid-wide value, and its expected source count from the local source point density around it. Correct for grids whose resolution varies across their extent (e.g. finer in one region than another, or anisotropic - different spacing in x than in y), at the cost of an extra KDTree query. Note this still assumes both grids are laid out as a structured 2D grid (as from numpy.meshgrid) - a genuinely rotated or scattered grid is not fully accounted for, since the footprint check is still axis-aligned.

Both modes fall back gracefully on a grid that’s degenerate (has only one distinct coordinate) along one axis - e.g. a single-column/single-row source or target - by treating that axis as carrying no width/density information rather than as zero width (see _expected_source_count()).

The (more expensive, source-density) part of this estimate is only computed lazily, the first time validity is actually needed - so it’s never paid for if you only ever call regrid() with mask_invalid=False.

property n_min_source#
regrid(data: ndarray, mask_invalid: bool = True) tuple[ndarray, ndarray | None][source]#

Resample 2D data onto this cache’s target grid, by averaging the source pixels nearest to each target pixel.

Parameters:
  • data – source data as a 2D array, matching the source grid shape this cache was built from

  • mask_invalid – boolean for setting invalid target pixels (fewer than n_min_source non-NaN contributing source pixels) to nan. If False, validity is not computed at all (it can be the expensive part of the estimate for irregular grids), and None is returned in its place.

Returns:

resampled 2D data, boolean mask that is True where a target pixel is valid (or None if mask_invalid is False)

class meteor_maths.sampling.sampling.Resampler(*args, **kwargs)[source]#

Bases: Protocol

Common interface for resampling backends usable by resample().

A resampler is built once for a fixed pair of source/target grids (via whatever constructor arguments that particular algorithm needs), and can then be reused to regrid several 2D data arrays sharing those grids - e.g. different variables, or repeated calls in a loop - without redoing the expensive part (KDTree construction, triangulation, or whatever else a given algorithm needs to set up) each time.

resample() only ever calls regrid(). It has no knowledge of which concrete algorithm is in play, or of that algorithm’s own constructor arguments (e.g. RegridCache’s regular_grid) - so adding a new resampling method (e.g. a scipy-based interpolator) means writing a class that satisfies this interface and registering it in _RESAMPLERS, with no changes to resample() itself. Structural (Protocol) typing is used rather than a base class, so a resampler doesn’t need to inherit from anything - it just needs a matching regrid method.

regrid(data: ndarray, mask_invalid: bool = True) tuple[ndarray, ndarray | None][source]#

Resample 2D source data (matching the source grid this resampler was built for) onto its target grid.

Parameters:
  • data – source data as a 2D array

  • mask_invalid – whether to compute and apply a validity mask, setting invalid target pixels to nan. Implementations should skip computing validity entirely when this is False, if that’s the expensive part of the algorithm.

Returns:

resampled 2D data, and a boolean mask that is True where a target pixel is valid (or None if mask_invalid is False)

meteor_maths.sampling.sampling.nearest_neighbour_resample(data: ndarray, x_source: ndarray, y_source: ndarray, x_target: ndarray, y_target: ndarray, mask_invalid: bool = True, regular_grid: bool = False) tuple[ndarray, ndarray | None][source]#

Resample 2D data by averaging nearest neighbour values. Invalid pixels set to nan if mask=True - invalid pixels defined as those with fewer than the automatically estimated expected number of source pixels binned to form the sample.

For repeated resampling of different data on the same source/target grids (e.g. multiple variables, or in a loop), build a RegridCache once and use its regrid() method instead, to avoid rebuilding the KDTree and nearest-neighbour mapping on every call.

Parameters:
  • data – data as 2D array

  • x_source – x coordinates of source grid

  • y_source – y coordinates of source grid

  • x_target – x coordinates of target grid

  • y_target – y coordinates of target grid

  • mask_invalid – boolean for setting invalid edge pixels to nan. If False, validity is never computed (skipping the more expensive part of the estimate for irregular grids)

  • regular_grid – whether both grids have uniform, axis-aligned pixel spacing - see RegridCache

Returns:

resampled 2D data, standard deviation of samples

meteor_maths.sampling.sampling.resample(var: str, ds: Dataset, x_source: ndarray, y_source: ndarray, x_target: ndarray, y_target: ndarray, mask_invalid: bool = True, method: str = 'nearest_neighbour', resampler: Resampler | None = None) ndarray[source]#

Resample variable data.

This function itself is resampling-algorithm-agnostic: it only handles picking out the right 2D slices of var (for 2D/3D/4D data), lining their axes up with x_source/y_source’s own layout, and building the output array - the actual resampling for each 2D slice is delegated to a Resampler (see method/resampler below). To add a new resampling algorithm, write a class satisfying the Resampler interface and register it in _RESAMPLERS; this function does not need to change.

Parameters:
  • var – variable to resample

  • ds – dataset containing data to resample

  • x_source – x coordinates for source data

  • y_source – y coordinates for source data

  • x_target – x coordinates for target data

  • y_target – y coordinates for target data

  • mask_invalid – boolean for setting invalid edge pixels to nan

  • method – name of a registered resampling algorithm to use (see _RESAMPLERS), built with its default settings. Ignored if resampler is given.

  • resampler – an optional, already-built Resampler (e.g. a RegridCache) for the given source/target grids. Passing one in is how to use non-default settings for a given algorithm (e.g. RegridCache(..., regular_grid=True)) - those settings are specific to each algorithm, so aren’t exposed as arguments here. Building one once and reusing it also avoids recomputing e.g. a KDTree on every call, when resampling several variables that share the same grids.

Returns:

array of resampled variable data