We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
seasonal_decompose() With Known Freq but Without DatetimeIndex()
In issue #3225, someone wrote "In 0.8, you should be able to specify freq as keyword argument to override the index." But I seem to still be having the same problems as the questioner there, with my data.

Here is a simple reproducible code example of my issue:
```
import statsmodels.api as sm
dta = pd.Series([x%3 for x in range(100)])
decomposed = sm.tsa.seasonal_decompose(dta, freq=3)
```

> AttributeError: 'RangeIndex' object has no attribute 'inferred_freq'

```
import statsmodels
print(statsmodels.__version__)
```

> 0.8.0

Should it be possible to use StatsModels seasonal_decompose() with a given frequency but without a DatetimeIndex? With my real data, I know it is of half-hour resolution, but I don't know the dates/times it corresponds to, so there's no neat/clean way of mapping it to a datetime index.

I've also posted this question on [StackOverflow](http://stackoverflow.com/questions/42425774/using-statsmodels-seasonal-decompose-without-datetimeindex-but-with-known-freq) and will add an answer there if I get one here. Thanks everyone for your time.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `statsmodels/tsa/seasonal.py`
Content:
```
1 """
2 Seasonal Decomposition by Moving Averages
3 """
4 from statsmodels.compat.python import lmap, range, iteritems
5 import numpy as np
6 from pandas.core.nanops import nanmean as pd_nanmean
7 from .filters._utils import _maybe_get_pandas_wrapper_freq
8 from .filters.filtertools import convolution_filter
9 from statsmodels.tsa.tsatools import freq_to_period
10 
11 
12 def seasonal_mean(x, freq):
13     """
14     Return means for each period in x. freq is an int that gives the
15     number of periods per cycle. E.g., 12 for monthly. NaNs are ignored
16     in the mean.
17     """
18     return np.array([pd_nanmean(x[i::freq]) for i in range(freq)])
19 
20 
21 def seasonal_decompose(x, model="additive", filt=None, freq=None, two_sided=True):
22     """
23     Seasonal decomposition using moving averages
24 
25     Parameters
26     ----------
27     x : array-like
28         Time series
29     model : str {"additive", "multiplicative"}
30         Type of seasonal component. Abbreviations are accepted.
31     filt : array-like
32         The filter coefficients for filtering out the seasonal component.
33         The concrete moving average method used in filtering is determined by two_sided.
34     freq : int, optional
35         Frequency of the series. Must be used if x is not  a pandas object.
36         Overrides default periodicity of x if x is a pandas
37         object with a timeseries index.
38     two_sided : bool
39         The moving average method used in filtering.
40         If True (default), a centered moving average is computed using the filt.
41         If False, the filter coefficients are for past values only.
42 
43     Returns
44     -------
45     results : obj
46         A object with seasonal, trend, and resid attributes.
47 
48     Notes
49     -----
50     This is a naive decomposition. More sophisticated methods should
51     be preferred.
52 
53     The additive model is Y[t] = T[t] + S[t] + e[t]
54 
55     The multiplicative model is Y[t] = T[t] * S[t] * e[t]
56 
57     The seasonal component is first removed by applying a convolution
58     filter to the data. The average of this smoothed series for each
59     period is the returned seasonal component.
60 
61     See Also
62     --------
63     statsmodels.tsa.filters.bk_filter.bkfilter
64     statsmodels.tsa.filters.cf_filter.xffilter
65     statsmodels.tsa.filters.hp_filter.hpfilter
66     statsmodels.tsa.filters.convolution_filter
67     """
68     _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)
69     x = np.asanyarray(x).squeeze()
70     nobs = len(x)
71 
72     if not np.all(np.isfinite(x)):
73         raise ValueError("This function does not handle missing values")
74     if model.startswith('m'):
75         if np.any(x <= 0):
76             raise ValueError("Multiplicative seasonality is not appropriate "
77                              "for zero and negative values")
78 
79     if freq is None:
80         if pfreq is not None:
81             pfreq = freq_to_period(pfreq)
82             freq = pfreq
83         else:
84             raise ValueError("You must specify a freq or x must be a "
85                              "pandas object with a timeseries index with"
86                              "a freq not set to None")
87 
88     if filt is None:
89         if freq % 2 == 0:  # split weights at ends
90             filt = np.array([.5] + [1] * (freq - 1) + [.5]) / freq
91         else:
92             filt = np.repeat(1./freq, freq)
93 
94     nsides = int(two_sided) + 1
95     trend = convolution_filter(x, filt, nsides)
96 
97     # nan pad for conformability - convolve doesn't do it
98     if model.startswith('m'):
99         detrended = x / trend
100     else:
101         detrended = x - trend
102 
103     period_averages = seasonal_mean(detrended, freq)
104 
105     if model.startswith('m'):
106         period_averages /= np.mean(period_averages)
107     else:
108         period_averages -= np.mean(period_averages)
109 
110     seasonal = np.tile(period_averages, nobs // freq + 1)[:nobs]
111 
112     if model.startswith('m'):
113         resid = x / seasonal / trend
114     else:
115         resid = detrended - seasonal
116 
117     results = lmap(_pandas_wrapper, [seasonal, trend, resid, x])
118     return DecomposeResult(seasonal=results[0], trend=results[1],
119                            resid=results[2], observed=results[3])
120 
121 
122 class DecomposeResult(object):
123     def __init__(self, **kwargs):
124         for key, value in iteritems(kwargs):
125             setattr(self, key, value)
126         self.nobs = len(self.observed)
127 
128     def plot(self):
129         from statsmodels.graphics.utils import _import_mpl
130         plt = _import_mpl()
131         fig, axes = plt.subplots(4, 1, sharex=True)
132         if hasattr(self.observed, 'plot'):  # got pandas use it
133             self.observed.plot(ax=axes[0], legend=False)
134             axes[0].set_ylabel('Observed')
135             self.trend.plot(ax=axes[1], legend=False)
136             axes[1].set_ylabel('Trend')
137             self.seasonal.plot(ax=axes[2], legend=False)
138             axes[2].set_ylabel('Seasonal')
139             self.resid.plot(ax=axes[3], legend=False)
140             axes[3].set_ylabel('Residual')
141         else:
142             axes[0].plot(self.observed)
143             axes[0].set_ylabel('Observed')
144             axes[1].plot(self.trend)
145             axes[1].set_ylabel('Trend')
146             axes[2].plot(self.seasonal)
147             axes[2].set_ylabel('Seasonal')
148             axes[3].plot(self.resid)
149             axes[3].set_ylabel('Residual')
150             axes[3].set_xlabel('Time')
151             axes[3].set_xlim(0, self.nobs)
152 
153         fig.tight_layout()
154         return fig
155 
156 
157 if __name__ == "__main__":
158     x = np.array([-50, 175, 149, 214, 247, 237, 225, 329, 729, 809,
159                   530, 489, 540, 457, 195, 176, 337, 239, 128, 102,
160                   232, 429, 3, 98, 43, -141, -77, -13, 125, 361, -45, 184])
161     results = seasonal_decompose(x, freq=4)
162 
163     from pandas import DataFrame, DatetimeIndex
164     data = DataFrame(x, DatetimeIndex(start='1/1/1951',
165                                       periods=len(x),
166                                       freq='Q'))
167 
168     res = seasonal_decompose(data)
169 
170
```

Path: `statsmodels/tsa/filters/_utils.py`
Content:
```
1 from functools import wraps
2 
3 from statsmodels.tools.data import _is_using_pandas
4 from statsmodels.tsa.tsatools import freq_to_period
5 
6 
7 def _get_pandas_wrapper(X, trim_head=None, trim_tail=None, names=None):
8     index = X.index
9     #TODO: allow use index labels
10     if trim_head is None and trim_tail is None:
11         index = index
12     elif trim_tail is None:
13         index = index[trim_head:]
14     elif trim_head is None:
15         index = index[:-trim_tail]
16     else:
17         index = index[trim_head:-trim_tail]
18     if hasattr(X, "columns"):
19         if names is None:
20             names = X.columns
21         return lambda x : X.__class__(x, index=index, columns=names)
22     else:
23         if names is None:
24             names = X.name
25         return lambda x : X.__class__(x, index=index, name=names)
26 
27 
28 def _maybe_get_pandas_wrapper(X, trim_head=None, trim_tail=None):
29     """
30     If using pandas returns a function to wrap the results, e.g., wrapper(X)
31     trim is an integer for the symmetric truncation of the series in some
32     filters.
33     otherwise returns None
34     """
35     if _is_using_pandas(X, None):
36         return _get_pandas_wrapper(X, trim_head, trim_tail)
37     else:
38         return
39 
40 
41 def _maybe_get_pandas_wrapper_freq(X, trim=None):
42     if _is_using_pandas(X, None):
43         index = X.index
44         func = _get_pandas_wrapper(X, trim)
45         freq = index.inferred_freq
46         return func, freq
47     else:
48         return lambda x : x, None
49 
50 
51 def pandas_wrapper(func, trim_head=None, trim_tail=None, names=None, *args,
52                    **kwargs):
53     @wraps(func)
54     def new_func(X, *args, **kwargs):
55         # quick pass-through for do nothing case
56         if not _is_using_pandas(X, None):
57             return func(X, *args, **kwargs)
58 
59         wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,
60                                            names)
61         ret = func(X, *args, **kwargs)
62         ret = wrapper_func(ret)
63         return ret
64 
65     return new_func
66 
67 
68 def pandas_wrapper_bunch(func, trim_head=None, trim_tail=None,
69                          names=None, *args, **kwargs):
70     @wraps(func)
71     def new_func(X, *args, **kwargs):
72         # quick pass-through for do nothing case
73         if not _is_using_pandas(X, None):
74             return func(X, *args, **kwargs)
75 
76         wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,
77                                            names)
78         ret = func(X, *args, **kwargs)
79         ret = wrapper_func(ret)
80         return ret
81 
82     return new_func
83 
84 
85 def pandas_wrapper_predict(func, trim_head=None, trim_tail=None,
86                            columns=None, *args, **kwargs):
87     pass
88 
89 
90 def pandas_wrapper_freq(func, trim_head=None, trim_tail=None,
91                         freq_kw='freq', columns=None, *args, **kwargs):
92     """
93     Return a new function that catches the incoming X, checks if it's pandas,
94     calls the functions as is. Then wraps the results in the incoming index.
95 
96     Deals with frequencies. Expects that the function returns a tuple,
97     a Bunch object, or a pandas-object.
98     """
99 
100     @wraps(func)
101     def new_func(X, *args, **kwargs):
102         # quick pass-through for do nothing case
103         if not _is_using_pandas(X, None):
104             return func(X, *args, **kwargs)
105 
106         wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,
107                                            columns)
108         index = X.index
109         freq = index.inferred_freq
110         kwargs.update({freq_kw : freq_to_period(freq)})
111         ret = func(X, *args, **kwargs)
112         ret = wrapper_func(ret)
113         return ret
114 
115     return new_func
116 
117 
118 def dummy_func(X):
119     return X
120 
121 def dummy_func_array(X):
122     return X.values
123 
124 def dummy_func_pandas_columns(X):
125     return X.values
126 
127 
128 def dummy_func_pandas_series(X):
129     return X['A']
130 
131 import pandas as pd
132 import numpy as np
133 
134 
135 def test_pandas_freq_decorator():
136     X = pd.util.testing.makeDataFrame()
137     # in X, get a function back that returns an X with the same columns
138     func = pandas_wrapper(dummy_func)
139 
140     np.testing.assert_equal(func(X.values), X)
141 
142     func = pandas_wrapper(dummy_func_array)
143     pd.util.testing.assert_frame_equal(func(X), X)
144 
145     expected = X.rename(columns=dict(zip('ABCD', 'EFGH')))
146     func = pandas_wrapper(dummy_func_array, names=list('EFGH'))
147     pd.util.testing.assert_frame_equal(func(X), expected)
148 
149
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/statsmodels/tsa/filters/_utils.py b/statsmodels/tsa/filters/_utils.py
--- a/statsmodels/tsa/filters/_utils.py
+++ b/statsmodels/tsa/filters/_utils.py
@@ -35,7 +35,7 @@
     if _is_using_pandas(X, None):
         return _get_pandas_wrapper(X, trim_head, trim_tail)
     else:
-        return
+        return lambda x : x
 
 
 def _maybe_get_pandas_wrapper_freq(X, trim=None):
diff --git a/statsmodels/tsa/seasonal.py b/statsmodels/tsa/seasonal.py
--- a/statsmodels/tsa/seasonal.py
+++ b/statsmodels/tsa/seasonal.py
@@ -4,7 +4,8 @@
 from statsmodels.compat.python import lmap, range, iteritems
 import numpy as np
 from pandas.core.nanops import nanmean as pd_nanmean
-from .filters._utils import _maybe_get_pandas_wrapper_freq
+from .filters._utils import (_maybe_get_pandas_wrapper_freq,
+                             _maybe_get_pandas_wrapper)
 from .filters.filtertools import convolution_filter
 from statsmodels.tsa.tsatools import freq_to_period
 
@@ -65,7 +66,11 @@
     statsmodels.tsa.filters.hp_filter.hpfilter
     statsmodels.tsa.filters.convolution_filter
     """
-    _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)
+    if freq is None:
+        _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)
+    else:
+        _pandas_wrapper = _maybe_get_pandas_wrapper(x)
+        pfreq = None
     x = np.asanyarray(x).squeeze()
     nobs = len(x)

{"golden_diff": "diff --git a/statsmodels/tsa/filters/_utils.py b/statsmodels/tsa/filters/_utils.py\n--- a/statsmodels/tsa/filters/_utils.py\n+++ b/statsmodels/tsa/filters/_utils.py\n@@ -35,7 +35,7 @@\n     if _is_using_pandas(X, None):\n         return _get_pandas_wrapper(X, trim_head, trim_tail)\n     else:\n-        return\n+        return lambda x : x\n \n \n def _maybe_get_pandas_wrapper_freq(X, trim=None):\ndiff --git a/statsmodels/tsa/seasonal.py b/statsmodels/tsa/seasonal.py\n--- a/statsmodels/tsa/seasonal.py\n+++ b/statsmodels/tsa/seasonal.py\n@@ -4,7 +4,8 @@\n from statsmodels.compat.python import lmap, range, iteritems\n import numpy as np\n from pandas.core.nanops import nanmean as pd_nanmean\n-from .filters._utils import _maybe_get_pandas_wrapper_freq\n+from .filters._utils import (_maybe_get_pandas_wrapper_freq,\n+                             _maybe_get_pandas_wrapper)\n from .filters.filtertools import convolution_filter\n from statsmodels.tsa.tsatools import freq_to_period\n \n@@ -65,7 +66,11 @@\n     statsmodels.tsa.filters.hp_filter.hpfilter\n     statsmodels.tsa.filters.convolution_filter\n     \"\"\"\n-    _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)\n+    if freq is None:\n+        _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)\n+    else:\n+        _pandas_wrapper = _maybe_get_pandas_wrapper(x)\n+        pfreq = None\n     x = np.asanyarray(x).squeeze()\n     nobs = len(x)\n", "issue": "seasonal_decompose() With Known Freq but Without DatetimeIndex()\nIn issue #3225, someone wrote \"In 0.8, you should be able to specify freq as keyword argument to override the index.\" But I seem to still be having the same problems as the questioner there, with my data.\r\n\r\nHere is a simple reproducible code example of my issue:\r\n```\r\nimport statsmodels.api as sm\r\ndta = pd.Series([x%3 for x in range(100)])\r\ndecomposed = sm.tsa.seasonal_decompose(dta, freq=3)\r\n```\r\n\r\n> AttributeError: 'RangeIndex' object has no attribute 'inferred_freq'\r\n\r\n```\r\nimport statsmodels\r\nprint(statsmodels.__version__)\r\n```\r\n\r\n> 0.8.0\r\n\r\nShould it be possible to use StatsModels seasonal_decompose() with a given frequency but without a DatetimeIndex? With my real data, I know it is of half-hour resolution, but I don't know the dates/times it corresponds to, so there's no neat/clean way of mapping it to a datetime index.\r\n\r\nI've also posted this question on [StackOverflow](http://stackoverflow.com/questions/42425774/using-statsmodels-seasonal-decompose-without-datetimeindex-but-with-known-freq) and will add an answer there if I get one here. Thanks everyone for your time.\n", "before_files": [{"content": "\"\"\"\nSeasonal Decomposition by Moving Averages\n\"\"\"\nfrom statsmodels.compat.python import lmap, range, iteritems\nimport numpy as np\nfrom pandas.core.nanops import nanmean as pd_nanmean\nfrom .filters._utils import _maybe_get_pandas_wrapper_freq\nfrom .filters.filtertools import convolution_filter\nfrom statsmodels.tsa.tsatools import freq_to_period\n\n\ndef seasonal_mean(x, freq):\n    \"\"\"\n    Return means for each period in x. freq is an int that gives the\n    number of periods per cycle. E.g., 12 for monthly. NaNs are ignored\n    in the mean.\n    \"\"\"\n    return np.array([pd_nanmean(x[i::freq]) for i in range(freq)])\n\n\ndef seasonal_decompose(x, model=\"additive\", filt=None, freq=None, two_sided=True):\n    \"\"\"\n    Seasonal decomposition using moving averages\n\n    Parameters\n    ----------\n    x : array-like\n        Time series\n    model : str {\"additive\", \"multiplicative\"}\n        Type of seasonal component. Abbreviations are accepted.\n    filt : array-like\n        The filter coefficients for filtering out the seasonal component.\n        The concrete moving average method used in filtering is determined by two_sided.\n    freq : int, optional\n        Frequency of the series. Must be used if x is not  a pandas object.\n        Overrides default periodicity of x if x is a pandas\n        object with a timeseries index.\n    two_sided : bool\n        The moving average method used in filtering.\n        If True (default), a centered moving average is computed using the filt.\n        If False, the filter coefficients are for past values only.\n\n    Returns\n    -------\n    results : obj\n        A object with seasonal, trend, and resid attributes.\n\n    Notes\n    -----\n    This is a naive decomposition. More sophisticated methods should\n    be preferred.\n\n    The additive model is Y[t] = T[t] + S[t] + e[t]\n\n    The multiplicative model is Y[t] = T[t] * S[t] * e[t]\n\n    The seasonal component is first removed by applying a convolution\n    filter to the data. The average of this smoothed series for each\n    period is the returned seasonal component.\n\n    See Also\n    --------\n    statsmodels.tsa.filters.bk_filter.bkfilter\n    statsmodels.tsa.filters.cf_filter.xffilter\n    statsmodels.tsa.filters.hp_filter.hpfilter\n    statsmodels.tsa.filters.convolution_filter\n    \"\"\"\n    _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)\n    x = np.asanyarray(x).squeeze()\n    nobs = len(x)\n\n    if not np.all(np.isfinite(x)):\n        raise ValueError(\"This function does not handle missing values\")\n    if model.startswith('m'):\n        if np.any(x <= 0):\n            raise ValueError(\"Multiplicative seasonality is not appropriate \"\n                             \"for zero and negative values\")\n\n    if freq is None:\n        if pfreq is not None:\n            pfreq = freq_to_period(pfreq)\n            freq = pfreq\n        else:\n            raise ValueError(\"You must specify a freq or x must be a \"\n                             \"pandas object with a timeseries index with\"\n                             \"a freq not set to None\")\n\n    if filt is None:\n        if freq % 2 == 0:  # split weights at ends\n            filt = np.array([.5] + [1] * (freq - 1) + [.5]) / freq\n        else:\n            filt = np.repeat(1./freq, freq)\n\n    nsides = int(two_sided) + 1\n    trend = convolution_filter(x, filt, nsides)\n\n    # nan pad for conformability - convolve doesn't do it\n    if model.startswith('m'):\n        detrended = x / trend\n    else:\n        detrended = x - trend\n\n    period_averages = seasonal_mean(detrended, freq)\n\n    if model.startswith('m'):\n        period_averages /= np.mean(period_averages)\n    else:\n        period_averages -= np.mean(period_averages)\n\n    seasonal = np.tile(period_averages, nobs // freq + 1)[:nobs]\n\n    if model.startswith('m'):\n        resid = x / seasonal / trend\n    else:\n        resid = detrended - seasonal\n\n    results = lmap(_pandas_wrapper, [seasonal, trend, resid, x])\n    return DecomposeResult(seasonal=results[0], trend=results[1],\n                           resid=results[2], observed=results[3])\n\n\nclass DecomposeResult(object):\n    def __init__(self, **kwargs):\n        for key, value in iteritems(kwargs):\n            setattr(self, key, value)\n        self.nobs = len(self.observed)\n\n    def plot(self):\n        from statsmodels.graphics.utils import _import_mpl\n        plt = _import_mpl()\n        fig, axes = plt.subplots(4, 1, sharex=True)\n        if hasattr(self.observed, 'plot'):  # got pandas use it\n            self.observed.plot(ax=axes[0], legend=False)\n            axes[0].set_ylabel('Observed')\n            self.trend.plot(ax=axes[1], legend=False)\n            axes[1].set_ylabel('Trend')\n            self.seasonal.plot(ax=axes[2], legend=False)\n            axes[2].set_ylabel('Seasonal')\n            self.resid.plot(ax=axes[3], legend=False)\n            axes[3].set_ylabel('Residual')\n        else:\n            axes[0].plot(self.observed)\n            axes[0].set_ylabel('Observed')\n            axes[1].plot(self.trend)\n            axes[1].set_ylabel('Trend')\n            axes[2].plot(self.seasonal)\n            axes[2].set_ylabel('Seasonal')\n            axes[3].plot(self.resid)\n            axes[3].set_ylabel('Residual')\n            axes[3].set_xlabel('Time')\n            axes[3].set_xlim(0, self.nobs)\n\n        fig.tight_layout()\n        return fig\n\n\nif __name__ == \"__main__\":\n    x = np.array([-50, 175, 149, 214, 247, 237, 225, 329, 729, 809,\n                  530, 489, 540, 457, 195, 176, 337, 239, 128, 102,\n                  232, 429, 3, 98, 43, -141, -77, -13, 125, 361, -45, 184])\n    results = seasonal_decompose(x, freq=4)\n\n    from pandas import DataFrame, DatetimeIndex\n    data = DataFrame(x, DatetimeIndex(start='1/1/1951',\n                                      periods=len(x),\n                                      freq='Q'))\n\n    res = seasonal_decompose(data)\n\n", "path": "statsmodels/tsa/seasonal.py"}, {"content": "from functools import wraps\n\nfrom statsmodels.tools.data import _is_using_pandas\nfrom statsmodels.tsa.tsatools import freq_to_period\n\n\ndef _get_pandas_wrapper(X, trim_head=None, trim_tail=None, names=None):\n    index = X.index\n    #TODO: allow use index labels\n    if trim_head is None and trim_tail is None:\n        index = index\n    elif trim_tail is None:\n        index = index[trim_head:]\n    elif trim_head is None:\n        index = index[:-trim_tail]\n    else:\n        index = index[trim_head:-trim_tail]\n    if hasattr(X, \"columns\"):\n        if names is None:\n            names = X.columns\n        return lambda x : X.__class__(x, index=index, columns=names)\n    else:\n        if names is None:\n            names = X.name\n        return lambda x : X.__class__(x, index=index, name=names)\n\n\ndef _maybe_get_pandas_wrapper(X, trim_head=None, trim_tail=None):\n    \"\"\"\n    If using pandas returns a function to wrap the results, e.g., wrapper(X)\n    trim is an integer for the symmetric truncation of the series in some\n    filters.\n    otherwise returns None\n    \"\"\"\n    if _is_using_pandas(X, None):\n        return _get_pandas_wrapper(X, trim_head, trim_tail)\n    else:\n        return\n\n\ndef _maybe_get_pandas_wrapper_freq(X, trim=None):\n    if _is_using_pandas(X, None):\n        index = X.index\n        func = _get_pandas_wrapper(X, trim)\n        freq = index.inferred_freq\n        return func, freq\n    else:\n        return lambda x : x, None\n\n\ndef pandas_wrapper(func, trim_head=None, trim_tail=None, names=None, *args,\n                   **kwargs):\n    @wraps(func)\n    def new_func(X, *args, **kwargs):\n        # quick pass-through for do nothing case\n        if not _is_using_pandas(X, None):\n            return func(X, *args, **kwargs)\n\n        wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,\n                                           names)\n        ret = func(X, *args, **kwargs)\n        ret = wrapper_func(ret)\n        return ret\n\n    return new_func\n\n\ndef pandas_wrapper_bunch(func, trim_head=None, trim_tail=None,\n                         names=None, *args, **kwargs):\n    @wraps(func)\n    def new_func(X, *args, **kwargs):\n        # quick pass-through for do nothing case\n        if not _is_using_pandas(X, None):\n            return func(X, *args, **kwargs)\n\n        wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,\n                                           names)\n        ret = func(X, *args, **kwargs)\n        ret = wrapper_func(ret)\n        return ret\n\n    return new_func\n\n\ndef pandas_wrapper_predict(func, trim_head=None, trim_tail=None,\n                           columns=None, *args, **kwargs):\n    pass\n\n\ndef pandas_wrapper_freq(func, trim_head=None, trim_tail=None,\n                        freq_kw='freq', columns=None, *args, **kwargs):\n    \"\"\"\n    Return a new function that catches the incoming X, checks if it's pandas,\n    calls the functions as is. Then wraps the results in the incoming index.\n\n    Deals with frequencies. Expects that the function returns a tuple,\n    a Bunch object, or a pandas-object.\n    \"\"\"\n\n    @wraps(func)\n    def new_func(X, *args, **kwargs):\n        # quick pass-through for do nothing case\n        if not _is_using_pandas(X, None):\n            return func(X, *args, **kwargs)\n\n        wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,\n                                           columns)\n        index = X.index\n        freq = index.inferred_freq\n        kwargs.update({freq_kw : freq_to_period(freq)})\n        ret = func(X, *args, **kwargs)\n        ret = wrapper_func(ret)\n        return ret\n\n    return new_func\n\n\ndef dummy_func(X):\n    return X\n\ndef dummy_func_array(X):\n    return X.values\n\ndef dummy_func_pandas_columns(X):\n    return X.values\n\n\ndef dummy_func_pandas_series(X):\n    return X['A']\n\nimport pandas as pd\nimport numpy as np\n\n\ndef test_pandas_freq_decorator():\n    X = pd.util.testing.makeDataFrame()\n    # in X, get a function back that returns an X with the same columns\n    func = pandas_wrapper(dummy_func)\n\n    np.testing.assert_equal(func(X.values), X)\n\n    func = pandas_wrapper(dummy_func_array)\n    pd.util.testing.assert_frame_equal(func(X), X)\n\n    expected = X.rename(columns=dict(zip('ABCD', 'EFGH')))\n    func = pandas_wrapper(dummy_func_array, names=list('EFGH'))\n    pd.util.testing.assert_frame_equal(func(X), expected)\n\n", "path": "statsmodels/tsa/filters/_utils.py"}], "after_files": [{"content": "\"\"\"\nSeasonal Decomposition by Moving Averages\n\"\"\"\nfrom statsmodels.compat.python import lmap, range, iteritems\nimport numpy as np\nfrom pandas.core.nanops import nanmean as pd_nanmean\nfrom .filters._utils import (_maybe_get_pandas_wrapper_freq,\n                             _maybe_get_pandas_wrapper)\nfrom .filters.filtertools import convolution_filter\nfrom statsmodels.tsa.tsatools import freq_to_period\n\n\ndef seasonal_mean(x, freq):\n    \"\"\"\n    Return means for each period in x. freq is an int that gives the\n    number of periods per cycle. E.g., 12 for monthly. NaNs are ignored\n    in the mean.\n    \"\"\"\n    return np.array([pd_nanmean(x[i::freq]) for i in range(freq)])\n\n\ndef seasonal_decompose(x, model=\"additive\", filt=None, freq=None, two_sided=True):\n    \"\"\"\n    Seasonal decomposition using moving averages\n\n    Parameters\n    ----------\n    x : array-like\n        Time series\n    model : str {\"additive\", \"multiplicative\"}\n        Type of seasonal component. Abbreviations are accepted.\n    filt : array-like\n        The filter coefficients for filtering out the seasonal component.\n        The concrete moving average method used in filtering is determined by two_sided.\n    freq : int, optional\n        Frequency of the series. Must be used if x is not  a pandas object.\n        Overrides default periodicity of x if x is a pandas\n        object with a timeseries index.\n    two_sided : bool\n        The moving average method used in filtering.\n        If True (default), a centered moving average is computed using the filt.\n        If False, the filter coefficients are for past values only.\n\n    Returns\n    -------\n    results : obj\n        A object with seasonal, trend, and resid attributes.\n\n    Notes\n    -----\n    This is a naive decomposition. More sophisticated methods should\n    be preferred.\n\n    The additive model is Y[t] = T[t] + S[t] + e[t]\n\n    The multiplicative model is Y[t] = T[t] * S[t] * e[t]\n\n    The seasonal component is first removed by applying a convolution\n    filter to the data. The average of this smoothed series for each\n    period is the returned seasonal component.\n\n    See Also\n    --------\n    statsmodels.tsa.filters.bk_filter.bkfilter\n    statsmodels.tsa.filters.cf_filter.xffilter\n    statsmodels.tsa.filters.hp_filter.hpfilter\n    statsmodels.tsa.filters.convolution_filter\n    \"\"\"\n    if freq is None:\n        _pandas_wrapper, pfreq = _maybe_get_pandas_wrapper_freq(x)\n    else:\n        _pandas_wrapper = _maybe_get_pandas_wrapper(x)\n        pfreq = None\n    x = np.asanyarray(x).squeeze()\n    nobs = len(x)\n\n    if not np.all(np.isfinite(x)):\n        raise ValueError(\"This function does not handle missing values\")\n    if model.startswith('m'):\n        if np.any(x <= 0):\n            raise ValueError(\"Multiplicative seasonality is not appropriate \"\n                             \"for zero and negative values\")\n\n    if freq is None:\n        if pfreq is not None:\n            pfreq = freq_to_period(pfreq)\n            freq = pfreq\n        else:\n            raise ValueError(\"You must specify a freq or x must be a \"\n                             \"pandas object with a timeseries index with\"\n                             \"a freq not set to None\")\n\n    if filt is None:\n        if freq % 2 == 0:  # split weights at ends\n            filt = np.array([.5] + [1] * (freq - 1) + [.5]) / freq\n        else:\n            filt = np.repeat(1./freq, freq)\n\n    nsides = int(two_sided) + 1\n    trend = convolution_filter(x, filt, nsides)\n\n    # nan pad for conformability - convolve doesn't do it\n    if model.startswith('m'):\n        detrended = x / trend\n    else:\n        detrended = x - trend\n\n    period_averages = seasonal_mean(detrended, freq)\n\n    if model.startswith('m'):\n        period_averages /= np.mean(period_averages)\n    else:\n        period_averages -= np.mean(period_averages)\n\n    seasonal = np.tile(period_averages, nobs // freq + 1)[:nobs]\n\n    if model.startswith('m'):\n        resid = x / seasonal / trend\n    else:\n        resid = detrended - seasonal\n\n    results = lmap(_pandas_wrapper, [seasonal, trend, resid, x])\n    return DecomposeResult(seasonal=results[0], trend=results[1],\n                           resid=results[2], observed=results[3])\n\n\nclass DecomposeResult(object):\n    def __init__(self, **kwargs):\n        for key, value in iteritems(kwargs):\n            setattr(self, key, value)\n        self.nobs = len(self.observed)\n\n    def plot(self):\n        from statsmodels.graphics.utils import _import_mpl\n        plt = _import_mpl()\n        fig, axes = plt.subplots(4, 1, sharex=True)\n        if hasattr(self.observed, 'plot'):  # got pandas use it\n            self.observed.plot(ax=axes[0], legend=False)\n            axes[0].set_ylabel('Observed')\n            self.trend.plot(ax=axes[1], legend=False)\n            axes[1].set_ylabel('Trend')\n            self.seasonal.plot(ax=axes[2], legend=False)\n            axes[2].set_ylabel('Seasonal')\n            self.resid.plot(ax=axes[3], legend=False)\n            axes[3].set_ylabel('Residual')\n        else:\n            axes[0].plot(self.observed)\n            axes[0].set_ylabel('Observed')\n            axes[1].plot(self.trend)\n            axes[1].set_ylabel('Trend')\n            axes[2].plot(self.seasonal)\n            axes[2].set_ylabel('Seasonal')\n            axes[3].plot(self.resid)\n            axes[3].set_ylabel('Residual')\n            axes[3].set_xlabel('Time')\n            axes[3].set_xlim(0, self.nobs)\n\n        fig.tight_layout()\n        return fig\n\n\nif __name__ == \"__main__\":\n    x = np.array([-50, 175, 149, 214, 247, 237, 225, 329, 729, 809,\n                  530, 489, 540, 457, 195, 176, 337, 239, 128, 102,\n                  232, 429, 3, 98, 43, -141, -77, -13, 125, 361, -45, 184])\n    results = seasonal_decompose(x, freq=4)\n\n    from pandas import DataFrame, DatetimeIndex\n    data = DataFrame(x, DatetimeIndex(start='1/1/1951',\n                                      periods=len(x),\n                                      freq='Q'))\n\n    res = seasonal_decompose(data)\n\n", "path": "statsmodels/tsa/seasonal.py"}, {"content": "from functools import wraps\n\nfrom statsmodels.tools.data import _is_using_pandas\nfrom statsmodels.tsa.tsatools import freq_to_period\n\n\ndef _get_pandas_wrapper(X, trim_head=None, trim_tail=None, names=None):\n    index = X.index\n    #TODO: allow use index labels\n    if trim_head is None and trim_tail is None:\n        index = index\n    elif trim_tail is None:\n        index = index[trim_head:]\n    elif trim_head is None:\n        index = index[:-trim_tail]\n    else:\n        index = index[trim_head:-trim_tail]\n    if hasattr(X, \"columns\"):\n        if names is None:\n            names = X.columns\n        return lambda x : X.__class__(x, index=index, columns=names)\n    else:\n        if names is None:\n            names = X.name\n        return lambda x : X.__class__(x, index=index, name=names)\n\n\ndef _maybe_get_pandas_wrapper(X, trim_head=None, trim_tail=None):\n    \"\"\"\n    If using pandas returns a function to wrap the results, e.g., wrapper(X)\n    trim is an integer for the symmetric truncation of the series in some\n    filters.\n    otherwise returns None\n    \"\"\"\n    if _is_using_pandas(X, None):\n        return _get_pandas_wrapper(X, trim_head, trim_tail)\n    else:\n        return lambda x : x\n\n\ndef _maybe_get_pandas_wrapper_freq(X, trim=None):\n    if _is_using_pandas(X, None):\n        index = X.index\n        func = _get_pandas_wrapper(X, trim)\n        freq = index.inferred_freq\n        return func, freq\n    else:\n        return lambda x : x, None\n\n\ndef pandas_wrapper(func, trim_head=None, trim_tail=None, names=None, *args,\n                   **kwargs):\n    @wraps(func)\n    def new_func(X, *args, **kwargs):\n        # quick pass-through for do nothing case\n        if not _is_using_pandas(X, None):\n            return func(X, *args, **kwargs)\n\n        wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,\n                                           names)\n        ret = func(X, *args, **kwargs)\n        ret = wrapper_func(ret)\n        return ret\n\n    return new_func\n\n\ndef pandas_wrapper_bunch(func, trim_head=None, trim_tail=None,\n                         names=None, *args, **kwargs):\n    @wraps(func)\n    def new_func(X, *args, **kwargs):\n        # quick pass-through for do nothing case\n        if not _is_using_pandas(X, None):\n            return func(X, *args, **kwargs)\n\n        wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,\n                                           names)\n        ret = func(X, *args, **kwargs)\n        ret = wrapper_func(ret)\n        return ret\n\n    return new_func\n\n\ndef pandas_wrapper_predict(func, trim_head=None, trim_tail=None,\n                           columns=None, *args, **kwargs):\n    pass\n\n\ndef pandas_wrapper_freq(func, trim_head=None, trim_tail=None,\n                        freq_kw='freq', columns=None, *args, **kwargs):\n    \"\"\"\n    Return a new function that catches the incoming X, checks if it's pandas,\n    calls the functions as is. Then wraps the results in the incoming index.\n\n    Deals with frequencies. Expects that the function returns a tuple,\n    a Bunch object, or a pandas-object.\n    \"\"\"\n\n    @wraps(func)\n    def new_func(X, *args, **kwargs):\n        # quick pass-through for do nothing case\n        if not _is_using_pandas(X, None):\n            return func(X, *args, **kwargs)\n\n        wrapper_func = _get_pandas_wrapper(X, trim_head, trim_tail,\n                                           columns)\n        index = X.index\n        freq = index.inferred_freq\n        kwargs.update({freq_kw : freq_to_period(freq)})\n        ret = func(X, *args, **kwargs)\n        ret = wrapper_func(ret)\n        return ret\n\n    return new_func\n\n\ndef dummy_func(X):\n    return X\n\ndef dummy_func_array(X):\n    return X.values\n\ndef dummy_func_pandas_columns(X):\n    return X.values\n\n\ndef dummy_func_pandas_series(X):\n    return X['A']\n\nimport pandas as pd\nimport numpy as np\n\n\ndef test_pandas_freq_decorator():\n    X = pd.util.testing.makeDataFrame()\n    # in X, get a function back that returns an X with the same columns\n    func = pandas_wrapper(dummy_func)\n\n    np.testing.assert_equal(func(X.values), X)\n\n    func = pandas_wrapper(dummy_func_array)\n    pd.util.testing.assert_frame_equal(func(X), X)\n\n    expected = X.rename(columns=dict(zip('ABCD', 'EFGH')))\n    func = pandas_wrapper(dummy_func_array, names=list('EFGH'))\n    pd.util.testing.assert_frame_equal(func(X), expected)\n\n", "path": "statsmodels/tsa/filters/_utils.py"}]}