Repository: statsmodels/statsmodels

Issue:
Timeseries Frequencies
Need to make sure that tsa.base.datetools._pandas_mapping contains all possible pandas mappings. And need to test that forecasting keeps the right periodicity - Eg., Q-DEC vs. Q-MAR. To do so we'll likely need to keep around the same frequency strings and get rid of the mapping completely.



Discussion:
This is partially addressed by #335.

Mentioned in another thread on a different issue #247.

It looks like we get almost everything for free, which is nice. Maybe a few more helpful errors here and there. I'll check it as a write some tests.

The only problem we have now is that the pandas default for "Q" does not map to the one we had chosen, which is unfortunate. I don't know that we can just change this without some kind of deprecation, so I'll add in the deprecation warning for "Q" that we will be moving to pandas behavior.


Resolving patch:
diff --git a/statsmodels/tsa/base/datetools.py b/statsmodels/tsa/base/datetools.py
--- a/statsmodels/tsa/base/datetools.py
+++ b/statsmodels/tsa/base/datetools.py
@@ -257,7 +257,7 @@ def _infer_freq(dates):
     try:
         from pandas.tseries.api import infer_freq
         freq = infer_freq(dates)
-        return _pandas_mapping.get(freq, freq)
+        return freq
     except ImportError:
         pass
 
@@ -284,11 +284,3 @@ def _infer_freq(dates):
         return 'A'
     else:
         return
-
-_pandas_mapping = {
-    'A-DEC': 'A',
-    'Q-DEC': 'Q',
-    'W-SUN': 'W',
-    'Q-MAR' : 'Q'
-}
-
diff --git a/statsmodels/tsa/base/tsa_model.py b/statsmodels/tsa/base/tsa_model.py
--- a/statsmodels/tsa/base/tsa_model.py
+++ b/statsmodels/tsa/base/tsa_model.py
@@ -7,15 +7,8 @@
 from pandas import datetools as pandas_datetools
 import datetime
 
-_freqs = ['B','D','W','M','A', 'Q']
-
 _freq_to_pandas = datetools._freq_to_pandas
 
-def _check_freq(freq):
-    if freq and freq not in _freqs:
-        raise ValueError("freq %s not understood" % freq)
-    return freq
-
 _tsa_doc = """
     %(model)s
 
@@ -25,8 +18,9 @@ def _check_freq(freq):
     dates : array-like of datetime, optional
         An array-like object of datetime objects. If a pandas object is given
         for endog or exog, it is assumed to have a DateIndex.
-    freq : str, {'B', 'D', 'W', 'M', 'A', 'Q'}, optional
-        The frequency of the time-series. This is optional if dates are given.
+    freq : str, optional
+        The frequency of the time-series. A Pandas offset or 'B', 'D', 'W',
+        'M', 'A', or 'Q'. This is optional if dates are given.
     %(extra_params)s
 """
 
@@ -35,7 +29,6 @@ def _check_freq(freq):
 _generic_params = base._model_params_doc
 _missing_param_doc = base._missing_param_doc
 
-#REPLACE frequencies with either timeseries or pandas conventions
 class TimeSeriesModel(base.LikelihoodModel):
 
     __doc__ = _tsa_doc % {"model" : _model_doc, "params" : _generic_params,
@@ -62,9 +55,12 @@ def _init_dates(self, dates, freq):
                             "keyword.")
             dates = Index(dates)
         self.data.dates = dates
-        self.data.freq = _check_freq(freq) #TODO: drop if can get info from dates
-        #TODO: still gonna need some more sophisticated handling here
-
+        if freq:
+            try: #NOTE: Can drop this once we move to pandas >= 0.8.x
+                _freq_to_pandas[freq]
+            except:
+                raise ValueError("freq %s not understood" % freq)
+        self.data.freq = freq
 
     def _get_exog_names(self):
         return self.data.xnames


Test patch:
diff --git a/statsmodels/tsa/base/tests/test_base.py b/statsmodels/tsa/base/tests/test_base.py
--- a/statsmodels/tsa/base/tests/test_base.py
+++ b/statsmodels/tsa/base/tests/test_base.py
@@ -1,8 +1,67 @@
+import numpy as np
 from pandas import Series
+from pandas.util import testing as ptesting
 from statsmodels.tsa.base.tsa_model import TimeSeriesModel
+from statsmodels.tsa.base.datetools import dates_from_range
 import numpy.testing as npt
 
+try:
+    from pandas import DatetimeIndex
+    _pandas_08x = True
+except ImportError:
+    _pandas_08x = False
+
 def test_pandas_nodates_index():
     from statsmodels.datasets import sunspots
     y = sunspots.load_pandas().data.SUNACTIVITY
     npt.assert_raises(ValueError, TimeSeriesModel, y)
+
+def test_predict_freq():
+    # test that predicted dates have same frequency
+    x = np.arange(1,36.)
+
+    if _pandas_08x:
+        from pandas import date_range
+
+        # there's a bug in pandas up to 0.10.2 for YearBegin
+        #dates = date_range("1972-4-1", "2007-4-1", freq="AS-APR")
+        dates = date_range("1972-4-30", "2006-4-30", freq="A-APR")
+        series = Series(x, index=dates)
+        model = TimeSeriesModel(series)
+        #npt.assert_(model.data.freq == "AS-APR")
+        npt.assert_(model.data.freq == "A-APR")
+
+        start = model._get_predict_start("2006-4-30")
+        end = model._get_predict_end("2016-4-30")
+        model._make_predict_dates()
+
+        predict_dates = model.data.predict_dates
+
+        #expected_dates = date_range("2006-12-31", "2016-12-31",
+        #                            freq="AS-APR")
+        expected_dates = date_range("2006-4-30", "2016-4-30", freq="A-APR")
+        npt.assert_equal(predict_dates, expected_dates)
+        #ptesting.assert_series_equal(predict_dates, expected_dates)
+
+    else:
+        from pandas import DateRange, datetools
+        dates = DateRange("1972-1-1", "2007-1-1", offset=datetools.yearEnd)
+        series = Series(x, index=dates)
+        model = TimeSeriesModel(series)
+        npt.assert_(model.data.freq == "A")
+
+        start = model._get_predict_start("2006-12-31")
+        end = model._get_predict_end("2016-12-31")
+        model._make_predict_dates()
+
+        predict_dates = model.data.predict_dates
+
+        expected_dates = DateRange("2006-12-31", "2016-12-31",
+                                    offset=datetools.yearEnd)
+        npt.assert_array_equal(predict_dates, expected_dates)
+
+
+
+
+
+
diff --git a/statsmodels/tsa/base/tests/test_datetools.py b/statsmodels/tsa/base/tests/test_datetools.py
--- a/statsmodels/tsa/base/tests/test_datetools.py
+++ b/statsmodels/tsa/base/tests/test_datetools.py
@@ -85,6 +85,12 @@ def test_infer_freq():
         m = DatetimeIndex(start=d1, end=d2, freq=_freq_to_pandas['M']).values
         a = DatetimeIndex(start=d1, end=d2, freq=_freq_to_pandas['A']).values
         q = DatetimeIndex(start=d1, end=d2, freq=_freq_to_pandas['Q']).values
+        assert _infer_freq(w) == 'W-SUN'
+        assert _infer_freq(a) == 'A-DEC'
+        assert _infer_freq(q) == 'Q-DEC'
+        assert _infer_freq(w[:3]) == 'W-SUN'
+        assert _infer_freq(a[:3]) == 'A-DEC'
+        assert _infer_freq(q[:3]) == 'Q-DEC'
     else:
         from pandas import DateRange
 
@@ -94,19 +100,18 @@ def test_infer_freq():
         m = DateRange(d1, d2, offset=_freq_to_pandas['M']).values
         a = DateRange(d1, d2, offset=_freq_to_pandas['A']).values
         q = DateRange(d1, d2, offset=_freq_to_pandas['Q']).values
+        assert _infer_freq(w) == 'W'
+        assert _infer_freq(a) == 'A'
+        assert _infer_freq(q) == 'Q'
+        assert _infer_freq(w[:3]) == 'W'
+        assert _infer_freq(a[:3]) == 'A'
+        assert _infer_freq(q[:3]) == 'Q'
 
     assert _infer_freq(b[2:5]) == 'B'
     assert _infer_freq(b[:3]) == 'D'
-
     assert _infer_freq(b) == 'B'
     assert _infer_freq(d) == 'D'
-    assert _infer_freq(w) == 'W'
     assert _infer_freq(m) == 'M'
-    assert _infer_freq(a) == 'A'
-    assert _infer_freq(q) == 'Q'
     assert _infer_freq(d[:3]) == 'D'
-    assert _infer_freq(w[:3]) == 'W'
     assert _infer_freq(m[:3]) == 'M'
-    assert _infer_freq(a[:3]) == 'A'
-    assert _infer_freq(q[:3]) == 'Q'