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

Redo xarray with MetPy tutorial for 1.0 #1605

Merged
merged 5 commits into from Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -20,6 +20,7 @@ build/*
MANIFEST
.eggs/

tutorials/*.nc
docs/build/
docs/examples
docs/tutorials
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Expand Up @@ -85,6 +85,7 @@ select = A B C D E F H I M Q RST S T W B902
ignore = F405 W503 RST902
per-file-ignores = examples/*.py: D T003 T001
tutorials/*.py: D T003 T001
tutorials/xarray_tutorial.py: D RST304
src/metpy/xarray.py: RST304
src/metpy/deprecation.py: C801
src/metpy/calc/*.py: RST306
Expand Down
96 changes: 78 additions & 18 deletions src/metpy/xarray.py
Expand Up @@ -146,7 +146,8 @@ def unit_array(self):
Notes
-----
If not already existing as a `pint.Quantity` or Dask array, the data of this DataArray
will be loaded into memory by this operation.
will be loaded into memory by this operation. Do not utilize on moderate- to
large-sized remote datasets before subsetting!
"""
if isinstance(self._data_array.data, units.Quantity):
return self._data_array.data
Expand All @@ -161,16 +162,27 @@ def convert_units(self, units):
Any cached/lazy-loaded data (except that in a Dask array) will be loaded into memory
by this operation. Do not utilize on moderate- to large-sized remote datasets before
subsetting!

See Also
--------
convert_coordinate_units
"""
return self.quantify().copy(data=self.unit_array.to(units))

def convert_coordinate_units(self, coord, units):
"""Return new DataArray with coordinate converted to different units.
"""Return new DataArray with specified coordinate converted to different units.

This operation differs from ``.convert_units`` since xarray coordinate indexes do not
yet support unit-aware arrays (even though unit-aware *data* arrays are).

Notes
-----
Any cached/lazy-loaded coordinate data (except that in a Dask array) will be loaded
into memory by this operation.

See Also
--------
convert_units
"""
new_coord_var = self._data_array[coord].copy(
data=self._data_array[coord].metpy.unit_array.m_as(units)
Expand All @@ -179,7 +191,7 @@ def convert_coordinate_units(self, coord, units):
return self._data_array.assign_coords(coords={coord: new_coord_var})

def quantify(self):
"""Return a DataArray with the data converted to a `pint.Quantity`.
"""Return a new DataArray with the data converted to a `pint.Quantity`.

Notes
-----
Expand All @@ -200,7 +212,7 @@ def quantify(self):
return quantified_dataarray

def dequantify(self):
"""Return a DataArray with the data as magnitude and the units as an attribute."""
"""Return a new DataArray with the data as magnitude and the units as an attribute."""
if isinstance(self._data_array.data, units.Quantity):
# Only dequantify if quantified
dequantified_dataarray = self._data_array.copy(
Expand Down Expand Up @@ -230,7 +242,7 @@ def cartopy_globe(self):

@property
def cartopy_geodetic(self):
"""Return the Geodetic CRS associated with the native CRS globe."""
"""Return the cartopy Geodetic CRS associated with the native CRS globe."""
return self.crs.cartopy_geodetic

@property
Expand Down Expand Up @@ -541,6 +553,15 @@ def sel(self, indexers=None, method=None, tolerance=None, drop=False, **indexers
def assign_crs(self, cf_attributes=None, **kwargs):
"""Assign a CRS to this DataArray based on CF projection attributes.

Specify a coordinate reference system/grid mapping following the Climate and
Forecasting (CF) conventions (see `Appendix F: Grid Mappings
<http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#appendix-grid-mappings>`_
) and store in the ``metpy_crs`` coordinate.

This method is only required if your data do not come from a dataset that follows CF
conventions with respect to grid mappings (in which case the ``.parse_cf`` method will
parse for the CRS metadata automatically).

Parameters
----------
cf_attributes : dict, optional
Expand All @@ -562,7 +583,7 @@ def assign_crs(self, cf_attributes=None, **kwargs):
return _assign_crs(self._data_array, cf_attributes, kwargs)

def assign_latitude_longitude(self, force=False):
"""Assign latitude and longitude coordinates derived from y and x coordinates.
"""Assign 2D latitude and longitude coordinates derived from 1D y and x coordinates.

Parameters
----------
Expand All @@ -577,8 +598,8 @@ def assign_latitude_longitude(self, force=False):

Notes
-----
A valid CRS coordinate must be present. PyProj is used for the coordinate
transformations.
A valid CRS coordinate must be present (as assigned by ``.parse_cf`` or
``.assign_crs``). PyProj is used for the coordinate transformations.

"""
# Check for existing latitude and longitude coords
Expand All @@ -595,7 +616,7 @@ def assign_latitude_longitude(self, force=False):
return new_dataarray.metpy.assign_coordinates(None)

def assign_y_x(self, force=False, tolerance=None):
"""Assign y and x dimension coordinates derived from 2D latitude and longitude.
"""Assign 1D y and x dimension coordinates derived from 2D latitude and longitude.

Parameters
----------
Expand All @@ -613,7 +634,8 @@ def assign_y_x(self, force=False, tolerance=None):

Notes
-----
A valid CRS coordinate must be present. PyProj is used for the coordinate
A valid CRS coordinate must be present (as assigned by ``.parse_cf`` or
``.assign_crs``) for the y/x projection space. PyProj is used for the coordinate
transformations.

"""
Expand All @@ -635,7 +657,8 @@ def assign_y_x(self, force=False, tolerance=None):
class MetPyDatasetAccessor:
"""Provide custom attributes and methods on XArray Datasets for MetPy functionality.

This accessor provides parsing of CF metadata and unit-/coordinate-type-aware selection.
This accessor provides parsing of CF grid mapping metadata, generating missing coordinate
types, and unit-/coordinate-type-aware operations.

>>> import xarray as xr
>>> from metpy.cbook import get_test_data
Expand All @@ -650,7 +673,20 @@ def __init__(self, dataset): # noqa: D107
self._dataset = dataset

def parse_cf(self, varname=None, coordinates=None):
"""Parse Climate and Forecasting (CF) convention metadata.
"""Parse dataset for coordinate system metadata according to CF conventions.

Interpret the grid mapping metadata in the dataset according to the Climate and
Forecasting (CF) conventions (see `Appendix F: Grid Mappings
<http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#appendix-grid-mappings>`_
) and store in the ``metpy_crs`` coordinate. Also, gives option to manually specify
coordinate types with the ``coordinates`` keyword argument.

If your dataset does not follow the CF conventions, you can manually supply the grid
mapping metadata with the ``.assign_crs`` method.

This method operates on individual data variables within the dataset, so do not be
suprised if information not associated with individual data variables is not
preserved.

Parameters
----------
Expand All @@ -666,6 +702,10 @@ def parse_cf(self, varname=None, coordinates=None):
`xarray.DataArray` or `xarray.Dataset`
Parsed DataArray (if varname is a string) or Dataset

See Also
--------
assign_crs

"""
from .plots.mapping import CFProjection

Expand Down Expand Up @@ -778,6 +818,15 @@ def sel(self, indexers=None, method=None, tolerance=None, drop=False, **indexers
def assign_crs(self, cf_attributes=None, **kwargs):
"""Assign a CRS to this Datatset based on CF projection attributes.

Specify a coordinate reference system/grid mapping following the Climate and
Forecasting (CF) conventions (see `Appendix F: Grid Mappings
<http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#appendix-grid-mappings>`_
) and store in the ``metpy_crs`` coordinate.

This method is only required if your dataset does not already follow CF conventions
with respect to grid mappings (in which case the ``.parse_cf`` method will parse for
the CRS metadata automatically).

Parameters
----------
cf_attributes : dict, optional
Expand All @@ -795,6 +844,10 @@ def assign_crs(self, cf_attributes=None, **kwargs):
CF projection arguments should be supplied as a dictionary or collection of kwargs,
but not both.

See Also
--------
parse_cf

"""
return _assign_crs(self._dataset, cf_attributes, kwargs)

Expand All @@ -815,8 +868,8 @@ def assign_latitude_longitude(self, force=False):

Notes
-----
A valid CRS coordinate must be present. PyProj is used for the coordinate
transformations.
A valid CRS coordinate must be present (as assigned by ``.parse_cf`` or
``.assign_crs``). PyProj is used for the coordinate transformations.

"""
# Determine if there is a valid grid prototype from which to compute the coordinates,
Expand Down Expand Up @@ -860,8 +913,8 @@ def assign_y_x(self, force=False, tolerance=None):

Notes
-----
A valid CRS coordinate must be present. PyProj is used for the coordinate
transformations.
A valid CRS coordinate must be present (as assigned by ``.parse_cf`` or
``.assign_crs``). PyProj is used for the coordinate transformations.

"""
# Determine if there is a valid grid prototype from which to compute the coordinates,
Expand Down Expand Up @@ -930,7 +983,14 @@ def mapping_func(da):
)

def quantify(self):
"""Return new dataset with all numeric variables quantified and cached data loaded."""
"""Return new dataset with all numeric variables quantified and cached data loaded.

Notes
-----
Any cached/lazy-loaded data (except that in a Dask array) will be loaded into memory
by this operation. Do not utilize on moderate- to large-sized remote datasets before
subsetting!
"""
return self._dataset.map(lambda da: da.metpy.quantify())

def dequantify(self):
Expand Down Expand Up @@ -1271,7 +1331,7 @@ def grid_deltas_from_dataarray(f, kind='default'):
Parameters
----------
f : `xarray.DataArray`
Parsed DataArray (MetPy's crs coordinate must be available for kind="actual")
Parsed DataArray (``metpy_crs`` coordinate must be available for kind="actual")
kind : str
Type of grid delta to calculate. "actual" returns true distances as calculated from
longitude and latitude via `lat_lon_grid_deltas`. "nominal" returns horizontal
Expand Down