Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add neighborhood filter to remap #923

Closed
wants to merge 14 commits into from

Conversation

ahijevyc
Copy link
Collaborator

@ahijevyc ahijevyc commented Aug 30, 2024

Apply a neighborhood filter within a circular radius r (in degrees) to a UxDataset or UxDataArray.

Issue #930

Overview

This neighborhood filter is written under remap so it has the ability to remap, but perhaps the neighborhood filter could be moved up to its own function, like UxDataset.neighborhold_filter. One doesn't always need to remap. As a workaround, if you don't need to remap, just supply the same destination UxGrid as the source.

This is kind of like uxarray.UxDataArray.inverse_distance_weighted_remap , but the neighborhood is defined by distance, not a number of nearest neighbors. This is ideally suited for a variable resolution mesh, in which a constant of neighbors doesn't have a constant sized neighborhood. Another difference is that this neighborhood filter does not weight data by inverse distance.

This function shares the same logic as uxarray.UxDataArray.subset.bounding_circle to select grid elements in a circular neighborhood, but this function applies it to all elements in grid, not just one center_coordinate.

The filter function func may be a user-defined function, but uses np.mean by default. It could be min, max, np.median. It can even use np.percentile if you supply a single quantile argument with functools.partial (see below)

func = np.percentile
from functools import partial
func = partial(func, q=90)

The new function remap.apply_func.py used remap.inverse_distance_weighted.py and remap.nearest_neighbor.py as a starting point. inverse_distance_weighted.py and nearest_neighbor.py have a lot of the same code but have different documentation style. Their formatting styles should probably be brought back into agreement before this pull request is completed.

Expected Usage

import numpy as np

import uxarray

grid_path = "/glade/campaign/mmm/wmr/weiwang/cps/irma3/2020/tk707_conus/init.nc"
data_path = "/glade/campaign/mmm/wmr/weiwang/cps/irma3/mp6/tk707/diag.2017-09-07_09.00.00.nc"
uxds = uxarray.open_mfdataset(
    grid_path,
    data_path
)

# Trim domain
lon_bounds = (-74, -64)
lat_bounds = (18, 24)
uxda = uxds["refl10cm_max"].isel(Time=0).subset.bounding_box(lon_bounds, lat_bounds)
uxda


# this is how you use this function to smooth with 0.25-deg filter.
uxda_smoothed_mean = uxda.remap.apply_func(uxda.uxgrid, func=np.mean, r=0.25)


# this is another way to use this function with a maximum filter
uxda_smoothed_max = uxda.remap.apply_func(uxda.uxgrid, func=max, r=0.25)

PR Checklist

General

  • An issue is linked created and linked
  • Add appropriate labels
  • Filled out Overview and Expected Usage (if applicable) sections

Testing

  • Adequate tests are created if there is new functionality
  • Tests cover all possible logical paths in your function
  • Tests are not too basic (such as simply calling a function and nothing else)

Documentation

  • Docstrings have been added to all new functions
  • Docstrings have updated with any function changes
  • Internal functions have a preceding underscore (_) and have been added to docs/internal_api/index.rst
  • User functions have been added to docs/user_api/index.rst

Examples

  • Any new notebook examples added to docs/examples/ folder
  • Clear the output of all cells before committing
  • New notebook files added to docs/examples.rst toctree
  • New notebook files added to new entry in docs/gallery.yml with appropriate thumbnail photo in docs/_static/thumbnails/

Apply a function like np.mean or np.max to neighborhood around all
points. It is like inverse distance weighting with power=0 but there is
no weighting. All points in neighborhood are treated the same.

The neighborhood is at a fixed radius instead of a fixed number of
neighbors.

Eliminated depreciated destination_obj argument.
default r=1 (degrees)

No more destination_obj, only destination_grid

Don't think we need to add remapped variable to existing UxDataset or
construct a UxDataset from remapped variable and existing variable.

Use [var_name] when constructing destination_uxds.
@philipc2
Copy link
Member

Hi @ahijevyc

This is a great contribution. Can you create an issue & link it to this PR (can restate parts from the description). I'll give this a detailed review after this weekend, however a couple initial thoughts:

We could have two forms of this function:

  • UxDataArray.remap.neighborhood_filter() would act the same as UxDataArray.remap.apply_func() that you've implemented.
  • UxDataArray.apply_neighborhood_filter() could apply the filter to the same Grid

This reminds me of our Topological Aggregations, except instead of using the connectivity to determine the "neighborhood", it uses the number of neighbors returned from a nearest neighbor query.

Could another name for this type of functionality be referred to as a "Spatial Aggregation"? A sample taking from another discussion:

"spatial" average should refer in my opinion to some sort of distance from the node to the center of the faces that fall within a radius; again, some sort of weighting based on the distance should be involved ( RBF is radial basis function , etc)

#724 (comment)

The new function remap.apply_func.py used remap.inverse_distance_weighted.py and remap.nearest_neighbor.py as a starting point. inverse_distance_weighted.py and nearest_neighbor.py have a lot of the same code but have different documentation style. Their formatting styles should probably be brought back into agreement before this pull request is completed.

Noted! I'll take a look into it.

@ahijevyc
Copy link
Collaborator Author

ahijevyc commented Sep 3, 2024 via email

@ahijevyc ahijevyc self-assigned this Sep 3, 2024
@aaronzedwick
Copy link
Member

"By the way, should we use this call structure with the remap accessor
UxDataArray.remap.nearest_neighbor()
or this call structure
UxDataArray.nearest_neighbor_remap()?"

To comment on this, you should use UxDataArray.remap.nearest_neighbor(). You can directly call UxDataArray.nearest_neighbor_remap(), but it will give a deprecation warning, and actually will not work anymore since we added some updates to remap, which weren't added to UxDataArray.nearest_neighbor_remap() as it was supposed to be removed awhile ago. Thanks for bringing this up! The deprecation warning has been there for several months so I think it is safe to delete this code altogether. I will put together a pr for this.

@ahijevyc ahijevyc closed this Sep 7, 2024
@ahijevyc
Copy link
Collaborator Author

ahijevyc commented Sep 7, 2024

I am preparing a new pull request #941 with the neighborhood filter on its own. It is not a part of remap.

@ahijevyc ahijevyc deleted the ahijevyc/apply_remap_func branch October 29, 2024 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants