Skip to content
This repository has been archived by the owner on Apr 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #76 from matt-long/master
Browse files Browse the repository at this point in the history
sel_time method added; change behavior of time_manager
  • Loading branch information
andersy005 authored Feb 25, 2019
2 parents eb650be + c95868e commit 38fdb6f
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 11 deletions.
2 changes: 2 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Utilities
.. autosummary::
utils.time.compute_time_var
utils.time.uncompute_time_var
utils.time.sel_time



Expand Down Expand Up @@ -80,3 +81,4 @@ Utilities

.. autofunction:: compute_time_var
.. autofunction:: uncompute_time_var
.. autofunction:: sel_time
3 changes: 3 additions & 0 deletions esmlab/climatology.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def compute_mon_climatology(dset, time_coord_name=None):
"""

tm = time_manager(dset, time_coord_name)
dset = tm.compute_time()
time_coord_name = tm.time_coord_name

static_variables = get_static_variables(dset, time_coord_name)
Expand Down Expand Up @@ -114,6 +115,7 @@ def compute_mon_anomaly(dset, slice_mon_clim_time=None, time_coord_name=None):
"""

tm = time_manager(dset, time_coord_name)
dset = tm.compute_time()
time_coord_name = tm.time_coord_name

static_variables = get_static_variables(dset, time_coord_name)
Expand Down Expand Up @@ -173,6 +175,7 @@ def compute_ann_mean(dset, weights=None, time_coord_name=None):
"""

tm = time_manager(dset, time_coord_name)
dset = tm.compute_time()
time_coord_name = tm.time_coord_name

static_variables = get_static_variables(dset, time_coord_name)
Expand Down
63 changes: 52 additions & 11 deletions esmlab/utils/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class time_manager(object):
"""Class to support managing the time coordinate of datasets.
"""

def __init__(self, ds, time_coord_name=None):
def __init__(self, ds, time_coord_name=None, year_offset=None):
"""
Parameters
----------
Expand All @@ -27,16 +27,19 @@ def __init__(self, ds, time_coord_name=None):
Name of the time coordinate of `ds`; if not provided, the
code will attempt to infer it.
"""
self._ds = ds
self._ds = xr.Dataset(ds)
self.orig_time_coord_name = None
self.orig_time_coord_decoded = None
self._time_computed = False

if time_coord_name is None:
self.time_coord_name = self._infer_time_coord_name()
else:
self.time_coord_name = time_coord_name

self.year_offset = year_offset

self._infer_time_bound_var()
self._compute_time()

@property
def time(self):
Expand Down Expand Up @@ -116,7 +119,7 @@ def get_time_undecoded(self):
self.orig_time_coord_decoded = True
return time

def get_time_decoded(self, midpoint=True, year_offset=np.nan):
def get_time_decoded(self, midpoint=True):
"""Return time decoded.
"""
# to compute a time midpoint, we need a time_bound variable
Expand All @@ -130,7 +133,7 @@ def get_time_decoded(self, midpoint=True, year_offset=np.nan):
# if time has already been decoded and there's no year_offset,
# just return the time as is
if self.time.dtype == np.dtype("O"):
if np.isnan(year_offset):
if self.year_offset is None:
return time_values

# if we need to un-decode time to apply the year_offset,
Expand All @@ -141,9 +144,9 @@ def get_time_decoded(self, midpoint=True, year_offset=np.nan):
else:
time_values = self.time

if not np.isnan(year_offset):
if self.year_offset is not None:
time_values += cftime.date2num(
datetime(int(year_offset), 1, 1),
datetime(int(self.year_offset), 1, 1),
units=self.time_attrs["units"],
calendar=self.time_attrs["calendar"],
)
Expand All @@ -156,9 +159,13 @@ def get_time_decoded(self, midpoint=True, year_offset=np.nan):
)
return time_out

def _compute_time(self):
def compute_time(self):
"""Compute the mid-point of the time bounds.
"""

if self._time_computed:
return self._ds

self.orig_time_coord_name = "t" + uuid.uuid4().hex

self._ds[self.orig_time_coord_name] = self.get_time_undecoded()
Expand All @@ -171,9 +178,15 @@ def _compute_time(self):

self._ds[self.time_coord_name].values = groupby_coord.values

self._time_computed = True

return self._ds

def restore_dataset(self, ds):
"""Return the original time variable.
"""
if not self._time_computed:
raise ValueError("time was not computed; cannot restore dataset")
time_values = ds[self.orig_time_coord_name].values
if self.orig_time_coord_decoded:
time_values = xr.CFTimeIndex(
Expand Down Expand Up @@ -230,7 +243,7 @@ def time_year_to_midyeardate(ds, time_coord_name):
return ds


def compute_time_var(ds, midpoint=True, year_offset=np.nan):
def compute_time_var(ds, midpoint=True, year_offset=None):
"""Compute the time coordinate of a dataset.
Parameters
Expand All @@ -247,8 +260,10 @@ def compute_time_var(ds, midpoint=True, year_offset=np.nan):
ds : `xarray.Dataset`
The dataset with time coordinate modified.
"""
tm = time_manager(ds)
ds[tm.time_coord_name] = tm.get_time_decoded(midpoint, year_offset)
tm = time_manager(ds, year_offset=year_offset)
ds = tm.compute_time()
ds[tm.time_coord_name] = tm.get_time_decoded(midpoint)
ds = ds.drop(tm.orig_time_coord_name)
return ds


Expand All @@ -266,5 +281,31 @@ def uncompute_time_var(ds):
The dataset with time coordinate modified.
"""
tm = time_manager(ds)
ds = tm.compute_time()
ds[tm.time_coord_name] = tm.get_time_undecoded()
ds = ds.drop(tm.orig_time_coord_name)
return ds


def sel_time(ds, indexer_val, year_offset=None):
"""Return dataset truncated to specified time range.
Parameters
----------
ds : `xarray.Dataset`
The dataset on which to operate
indexer_val : scalar, slice, or array_like
value passed to ds.isel(**{time_coord_name: indexer_val})
year_offset : numeric, optional
Integer year by which to offset the time axis.
Returns
-------
ds : `xarray.Dataset`
The dataset with time coordinate truncated.
"""

tm = time_manager(ds, year_offset=year_offset)
ds = tm.compute_time()
ds = ds.sel(**{tm.time_coord_name: indexer_val})
return tm.restore_dataset(ds)
7 changes: 7 additions & 0 deletions tests/utils/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ def test_uncompute_time_var(dset, time_coord_name="time"):
assert dset[time_coord_name].dtype == np.dtype("O")
dset_with_uncomputed_time = utils.time.uncompute_time_var(dset)
assert np.issubdtype(dset_with_uncomputed_time[time_coord_name].dtype, np.number)


def test_sel_time(dset):
dset = utils.time.sel_time(
dset, indexer_val=slice("1850-01-01", "1850-12-31"), year_offset=1850
)
assert len(dset.time) == 12

0 comments on commit 38fdb6f

Please sign in to comment.