Repository: pandas-dev/pandas

Issue:
Poisson (exponential) window for pandas.Series.rolling
#### Problem Description
In [`pandas.Series.rolling`](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.rolling.html), the [SciPy window type `exponential`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.windows.exponential.html#scipy.signal.windows.exponential) has not been implemented. This `win_type` would be extremely useful for analysing [biological processes](https://en.wikipedia.org/wiki/Time_constant#Time_constants_in_neuroscience). It should be pretty easy to add.

Please, note that despite the similar name, the proposed method is mathematically different from the implemented [`pandas.Series.ewm`](https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.ewm.html) method.

#### Enhancement Request
Taking for example `df.rolling(51, win_type='exponential', tau=10)`, the implemented SciPy window would be the following asymmetrical window:
```
from scipy import signal
window = 51
tau = 10
w = signal.exponential(window, tau=tau, sym=False, center=0)
array([1.        , 0.90483742, 0.81873075, 0.74081822, 0.67032005,
       0.60653066, 0.54881164, 0.4965853 , 0.44932896, 0.40656966,
       0.36787944, 0.33287108, 0.30119421, 0.27253179, 0.24659696,
       0.22313016, 0.20189652, 0.18268352, 0.16529889, 0.14956862,
       0.13533528, 0.12245643, 0.11080316, 0.10025884, 0.09071795,
       0.082085  , 0.07427358, 0.06720551, 0.06081006, 0.05502322,
       0.04978707, 0.0450492 , 0.0407622 , 0.03688317, 0.03337327,
       0.03019738, 0.02732372, 0.02472353, 0.02237077, 0.02024191,
       0.01831564, 0.01657268, 0.01499558, 0.01356856, 0.01227734,
       0.011109  , 0.01005184, 0.00909528, 0.00822975, 0.00744658,
       0.00673795])
```


#### Expected Output
```
>>> df = pd.DataFrame({'B': [1, 0, 0, 0, 0]})
>>> df.rolling(51, win_type='exponential', tau=10)
     B
0  1.0
1  0.90483742
2  0.81873075
3  0.74081822
4  0.67032005
```



Discussion:
we already support scipy types so this would be very easy to add - a pull request would make this happen
http://pandas.pydata.org/pandas-docs/stable/computation.html#rolling-windows

As far as I can see, only these files need to be amended:

- ` pandas/pandas/core/window.py`
- ` pandas/pandas/tests/test_window.py`
- ` pandas/doc/source/computation.rst`

Resolving patch:
diff --git a/pandas/core/window.py b/pandas/core/window.py
--- a/pandas/core/window.py
+++ b/pandas/core/window.py
@@ -504,7 +504,8 @@ class Window(_Window):
     * ``kaiser`` (needs beta)
     * ``gaussian`` (needs std)
     * ``general_gaussian`` (needs power, width)
-    * ``slepian`` (needs width).
+    * ``slepian`` (needs width)
+    * ``exponential`` (needs tau), center is set to None.
 
     If ``win_type=None`` all points are evenly weighted. To learn more about
     different window types see `scipy.signal window functions
@@ -623,11 +624,19 @@ def _validate_win_type(win_type, kwargs):
                 arg_map = {'kaiser': ['beta'],
                            'gaussian': ['std'],
                            'general_gaussian': ['power', 'width'],
-                           'slepian': ['width']}
+                           'slepian': ['width'],
+                           'exponential': ['tau'],
+                           }
+
                 if win_type in arg_map:
-                    return tuple([win_type] + _pop_args(win_type,
-                                                        arg_map[win_type],
-                                                        kwargs))
+                    win_args = _pop_args(win_type, arg_map[win_type], kwargs)
+                    if win_type == 'exponential':
+                        # exponential window requires the first arg (center)
+                        # to be set to None (necessary for symmetric window)
+                        win_args.insert(0, None)
+
+                    return tuple([win_type] + win_args)
+
                 return win_type
 
             def _pop_args(win_type, arg_names, kwargs):


Test patch:
diff --git a/pandas/tests/test_window.py b/pandas/tests/test_window.py
--- a/pandas/tests/test_window.py
+++ b/pandas/tests/test_window.py
@@ -42,7 +42,8 @@ def win_types(request):
     return request.param
 
 
-@pytest.fixture(params=['kaiser', 'gaussian', 'general_gaussian'])
+@pytest.fixture(params=['kaiser', 'gaussian', 'general_gaussian',
+                        'exponential'])
 def win_types_special(request):
     return request.param
 
@@ -1260,7 +1261,8 @@ def test_cmov_window_special(self, win_types_special):
         kwds = {
             'kaiser': {'beta': 1.},
             'gaussian': {'std': 1.},
-            'general_gaussian': {'power': 2., 'width': 2.}}
+            'general_gaussian': {'power': 2., 'width': 2.},
+            'exponential': {'tau': 10}}
 
         vals = np.array([6.95, 15.21, 4.72, 9.12, 13.81, 13.49, 16.68, 9.48,
                          10.63, 14.48])
@@ -1271,7 +1273,9 @@ def test_cmov_window_special(self, win_types_special):
             'general_gaussian': [np.nan, np.nan, 9.85011, 10.71589, 11.73161,
                                  13.08516, 12.95111, 12.74577, np.nan, np.nan],
             'kaiser': [np.nan, np.nan, 9.86851, 11.02969, 11.65161, 12.75129,
-                       12.90702, 12.83757, np.nan, np.nan]
+                       12.90702, 12.83757, np.nan, np.nan],
+            'exponential': [np.nan, np.nan, 9.83364, 11.10472, 11.64551,
+                            12.66138, 12.92379, 12.83770, np.nan, np.nan],
         }
 
         xp = Series(xps[win_types_special])
@@ -1287,7 +1291,8 @@ def test_cmov_window_special_linear_range(self, win_types_special):
             'kaiser': {'beta': 1.},
             'gaussian': {'std': 1.},
             'general_gaussian': {'power': 2., 'width': 2.},
-            'slepian': {'width': 0.5}}
+            'slepian': {'width': 0.5},
+            'exponential': {'tau': 10}}
 
         vals = np.array(range(10), dtype=np.float)
         xp = vals.copy()