Commit 32599a37 authored by Lars Bärring's avatar Lars Bärring Committed by Klaus Zimmermann
Browse files

Add index function last_occurrence (closes #97)

parent 892b52b0
......@@ -34,6 +34,20 @@ index_functions:
kind: operator
description: |
Calculates the last time some condition is met.
First, the threshold is transformed to the same standard_name and units as
the input data.
Then the thresholding is performed as condition(data, threshold), ie
if condition is <, data < threshold.
Finally, locate the last occurrence when condition is met.
kind: quantity
kind: operator
description: |
Calculates statistics on lengths of spells.
......@@ -129,6 +129,53 @@ class FirstOccurrence:
return collapsed_cube
class LastOccurrence:
def __init__(self, threshold, condition):
self.threshold = threshold
self.condition = NUMPY_OPERATORS[condition]
self.lazy_condition = DASK_OPERATORS[condition]
self.standard_name = None
self.units = Unit('days')
self.NO_OCCURRENCE = -np.inf
def prepare(self, input_cube):
change_units(self.threshold, input_cube.units, input_cube.standard_name)
def call_func(self, data, axis, **kwargs):
axis = normalize_axis(axis, data.ndim)
cond = self.condition(np.flip(data, axis=axis),
ndays = data.shape[axis]
res =,
ndays - cond.argmax(axis=axis),
return res.astype('float32')
def lazy_func(self, data, axis, **kwargs):
axis = normalize_axis(axis, data.ndim)
mask =
cond = self.lazy_condition(da.flip(data, axis),
ndays = data.shape[axis]
res = da.where(cond.any(axis=axis),
ndays - cond.argmax(axis=axis),
res =, mask)
return res.astype('float32')
def post_process(self, collapsed_cube, data_result, coords,
period, **kwargs):
time = collapsed_cube.coord('time')
calendar = time.units.calendar
offsets = np.empty_like(time.points, dtype=data_result.dtype)
for i, representative_date in enumerate(time.cells()):
year = representative_date.point.year
start_date = datetime(year, period.first_month_number, 1)
units = Unit('days since {}-01-01'.format(year), calendar=calendar)
offsets[i] = units.date2num(start_date) = (collapsed_cube.core_data()
+ offsets[:, None, None])
return collapsed_cube
class SpellLength:
def __init__(self, threshold, condition, reducer):
self.threshold = threshold
......@@ -51,6 +51,7 @@ setuptools.setup(
'climix.index_functions': [
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment