Repository: Ouranosinc/xclim
Pull request #59 (2018-10-11 17:31:58)

rolling n-day max precip; minor modifications to max_1_day
Added Max n-day cumulated precip.  Minor changes to max_1_day
Note- strict Nan-behvior when using rolling calculations cannot rely on 'skipna=False' as in max_1_day index because rolling calculations results in nans to the first n-1 values in a series (n = window size) 

codecov[bot]: # [Codecov](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=h1) Report
> Merging [#59](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=desc) into [master](https://codecov.io/gh/Ouranosinc/xclim/commit/fc6d030274ed7cadfe42a63a2296aee9786abad9?src=pr&el=desc) will **increase** coverage by `0.55%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/Ouranosinc/xclim/pull/59/graphs/tree.svg?width=650&token=1clOxQaUPR&height=150&src=pr)](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #59      +/-   ##
==========================================
+ Coverage   66.02%   66.58%   +0.55%     
==========================================
  Files           7        7              
  Lines         418      425       +7     
==========================================
+ Hits          276      283       +7     
  Misses        142      142
```


| [Impacted Files](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [xclim/indices.py](https://codecov.io/gh/Ouranosinc/xclim/pull/59/diff?src=pr&el=tree#diff-eGNsaW0vaW5kaWNlcy5weQ==) | `64.24% <100%> (+1.34%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=footer). Last update [fc6d030...11b7ae7](https://codecov.io/gh/Ouranosinc/xclim/pull/59?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

coveralls: ## Pull Request Test Coverage Report for [Build 162](https://coveralls.io/builds/19474136)

* **9** of **9**   **(100.0%)**  changed or added relevant lines in **1** file are covered.
* **3** unchanged lines in **1** file lost coverage.
* Overall coverage decreased (**-0.07%**) to **66.118%**

---


|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [xclim/indices.py](https://coveralls.io/builds/19474136/source?filename=xclim%2Findices.py#L21) | 3 | 64.25% |
<!-- | **Total:** | **3** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/19474136/badge)](https://coveralls.io/builds/19474136) |
| :-- | --: |
| Change from base [Build 161](https://coveralls.io/builds/19454728): |  -0.07% |
| Covered Lines: | 281 |
| Relevant Lines: | 425 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/README.rst b/README.rst
index 4707f890..a55a7a58 100644
--- a/README.rst
+++ b/README.rst
@@ -24,10 +24,6 @@ xclim
         :target: https://www.codefactor.io/repository/github/ouranosinc/xclim
         :alt: CodeFactor
 
-.. image:: https://www.codefactor.io/repository/github/ouranosinc/xclim/badge
-   :target: https://www.codefactor.io/repository/github/ouranosinc/xclim
-   :alt: CodeFactor
-
 .. image:: https://readthedocs.org/projects/xclim/badge/?version=latest
         :target: https://xclim.readthedocs.io/en/latest/?badge=latest
         :alt: Documentation Status
diff --git a/tox.ini b/tox.ini
index f9f97799..41310814 100644
--- a/tox.ini
+++ b/tox.ini
@@ -38,12 +38,10 @@ deps =
      -r{toxinidir}/requirements.txt
      -r{toxinidir}/requirements_dev.txt
       coveralls
-      codecov
       pytest-cov
 commands =
     pip install -U pip
     py.test --cov xclim --basetemp={envtmpdir}
     - coveralls
-    - codecov
 
 
diff --git a/xclim/indices.py b/xclim/indices.py
index b02d4e69..ec4854ff 100644
--- a/xclim/indices.py
+++ b/xclim/indices.py
@@ -3,17 +3,16 @@
 """
 Main module
 """
+import re
 from functools import wraps
 
-import six
 import numpy as np
+import six
 import xarray as xr
-import re
 
 from . import run_length as rl
 from .checks import valid_daily_mean_temperature, valid_daily_max_min_temperature, valid_daily_min_temperature, \
     valid_daily_max_temperature, valid_daily_mean_discharge
-from .utils import daily_downsampler as dds
 
 xr.set_options(enable_cftimeindex=True)  # Set xarray to use cftimeindex
 
@@ -697,12 +696,60 @@ def tn_min(tasmin, freq='YS'):
     return tasmin.resample(time=freq).min(dim='time')
 
 
+# add 'n_window_size' dynamic attribute e.g. n_window_size="%s%s" % (str(window), ' day window')
+@with_attrs(standard_name='maximum_n_day_total_precipitation',
+            long_name='maximum n day total precipitation')
+def max_n_day_precipitation_amount(da, window, freq='YS'):
+    """Highest precipitation amount cumulated over a n-day moving window for a given period (frequency).
+
+    Calculate the N-day rolling sum of the original daily total precipitation series
+     and determine the maximum value for each period.
+
+    Parameters
+    ----------
+    da : xarray.DataArray
+      daily precipitation values.
+    window : int
+      window size in days
+    freq : str, optional
+      Resampling frequency : Default 'YS' (yearly)
+
+    Returns
+    -------
+    xarray.DataArray
+      The highest cumulated N-day precipitation value at the given time frequency.
+
+
+    Examples
+    --------
+    The following would compute for each grid cell of file `pr.day.nc` the highest 5-day total precipitation
+    at an annual frequency.
+
+    >>> da = xr.open_dataset('pr.day.nc')
+    >>> window = 5
+    >>> output = max_n_day_precipitation_amount(da, window, freq="YS")
+
+    """
+
+    # rolling sum of the values
+    arr = da.rolling(time=window, center=False).sum(dim='time')
+    output = arr.resample(time=freq).max(dim='time')
+    # 'keep_attrs=True' does not seem to work with rolling? copy original
+    output.attrs.update(da.attrs)
+
+    # rename variable name to "%s%s%s" % ('rx',str(window),'day')
+
+    return output
+
+
 # @check_daily_monotonic # TODO create daily timestep check
 # @convert_precip_units   # TODO create units checker / converter
+@with_attrs(standard_name='maximum_1_day_total_precipitation',
+            long_name='maximum 1 day total precipitation')
 def max_1day_precipitation_amount(da, freq='YS', skipna=False):
-    """Highest 1-day precipitation amount for a period (frequency).
+    """Highest 1-day precipitation amount for the provided frequency.
 
-    Resample the original daily total precipitaiton temperature series by taking the max over each period.
+    Resample the original daily total precipitaiton series by taking the max over each period.
 
     Parameters
     ----------
@@ -711,7 +758,8 @@ def max_1day_precipitation_amount(da, freq='YS', skipna=False):
     freq : str, optional
       Resampling frequency : Default 'YS' (yearly)
     skipna : boolean, optional
-      NaN value treatment flag, default=False : where NaN values are not ignored in the operation (results in NaN value for any period where a NaN is present)
+      NaN value treatment flag, default=False :
+      where NaN values are not ignored in the operation (results in NaN value for any period where a NaN is present)
 
     Returns
     -------
@@ -730,7 +778,10 @@ def max_1day_precipitation_amount(da, freq='YS', skipna=False):
     """
 
     # resample the values
-    output = da.resample(time=freq,keep_attrs=True).max(dim='time',skipna=skipna)
+    arr = da.resample(time=freq)
+    output = arr.max(dim='time', skipna=skipna, keep_attrs=True)
+
+    # rename variable to 'rx1day' in indicator Class
 
     return output
 


Test patch:
diff --git a/tests/test_indices.py b/tests/test_indices.py
index 6ac1f40c..a8a187da 100644
--- a/tests/test_indices.py
+++ b/tests/test_indices.py
@@ -35,6 +35,38 @@ TESTS_DATA = os.path.join(TESTS_HOME, 'testdata')
 K2C = 273.15
 
 
+class Test_max_n_day_precipitation_amount():
+
+    def time_series(self, values):
+        coords = pd.date_range('7/1/2000', periods=len(values), freq=pd.DateOffset(days=1))
+        return xr.DataArray(values, coords=[coords, ], dims='time',
+                            attrs={'standard_name': 'precipitation_flux',
+                                   'cell_methods': 'time: sum (interval: 1 day)',
+                                   'units': 'mm'})
+
+    # test 2 day max precip
+    def test_single_max(self):
+        a = self.time_series(np.array([3, 4, 20, 20, 0, 6, 9, 25, 0, 0]))
+        rxnday = xci.max_n_day_precipitation_amount(a, 2)
+        assert rxnday == 40
+        assert rxnday.time.dt.year == 2000
+
+    # test whether sum over entire length is resolved
+    def test_sumlength_max(self):
+        a = self.time_series(np.array([3, 4, 20, 20, 0, 6, 9, 25, 0, 0]))
+        rxnday = xci.max_n_day_precipitation_amount(a, len(a))
+        assert rxnday == a.sum('time')
+        assert rxnday.time.dt.year == 2000
+
+    # test whether non-unique maxes are resolved
+    def test_multi_max(self):
+        a = self.time_series(np.array([3, 4, 20, 20, 0, 6, 15, 25, 0, 0]))
+        rxnday = xci.max_n_day_precipitation_amount(a, 2)
+        assert rxnday == 40
+        assert len(rxnday) == 1
+        assert rxnday.time.dt.year == 2000
+
+
 class Test_max_1day_precipitation_amount():
 
     def time_series(self, values):