Repository: statsmodels/statsmodels

Issue:
TST: consolidate version checks, remove outdated
Closes #1063.

We recently removed some version checks for unsupported numpy/scipy/pandas versions, but missed a few.  This gets most or all of the remaining ones.

Consolidates version checks in the compat files so that in the future they are all in one place when it comes time to update the minimum required versions.


Discussion:

Resolving patch:
diff --git a/statsmodels/compat/numpy.py b/statsmodels/compat/numpy.py
--- a/statsmodels/compat/numpy.py
+++ b/statsmodels/compat/numpy.py
@@ -41,11 +41,13 @@
 
 from __future__ import absolute_import
 from distutils.version import LooseVersion
+
 import numpy as np
 
 from .python import PY3
 
 NP_LT_114 = LooseVersion(np.__version__) < LooseVersion('1.14')
+NP_LT_113 = LooseVersion(np.__version__) < '1.13.0'
 
 np_matrix_rank = np.linalg.matrix_rank
 np_new_unique = np.unique
diff --git a/statsmodels/compat/pandas.py b/statsmodels/compat/pandas.py
--- a/statsmodels/compat/pandas.py
+++ b/statsmodels/compat/pandas.py
@@ -6,6 +6,8 @@
 
 
 version = LooseVersion(pandas.__version__)
+pandas_lte_0_19_2 = version <= LooseVersion('0.19.2')
+pandas_gt_0_19_2 = version > LooseVersion('0.19.2')
 
 
 try:
diff --git a/statsmodels/stats/mediation.py b/statsmodels/stats/mediation.py
--- a/statsmodels/stats/mediation.py
+++ b/statsmodels/stats/mediation.py
@@ -20,7 +20,6 @@
 import numpy as np
 import pandas as pd
 from statsmodels.graphics.utils import maybe_name_or_idx
-import statsmodels.compat.pandas as pdc  # pragma: no cover
 
 
 class Mediation(object):
@@ -369,7 +368,6 @@ def __init__(self, indirect_effects, direct_effects):
         self.ACME_avg = (self.ACME_ctrl + self.ACME_tx) / 2
         self.ADE_avg = (self.ADE_ctrl + self.ADE_tx) / 2
 
-
     def summary(self, alpha=0.05):
         """
         Provide a summary of a mediation analysis.
@@ -395,9 +393,6 @@ def summary(self, alpha=0.05):
             smry.iloc[i, 2] = np.percentile(vec, 100 * (1 - alpha / 2))
             smry.iloc[i, 3] = _pvalue(vec)
 
-        if pdc.version < '0.17.0':  # pragma: no cover
-            smry = smry.convert_objects(convert_numeric=True)
-        else:  # pragma: no cover
-            smry = smry.apply(pd.to_numeric, errors='coerce')
+        smry = smry.apply(pd.to_numeric, errors='coerce')
 
         return smry


Test patch:
diff --git a/statsmodels/genmod/tests/results/results_glm.py b/statsmodels/genmod/tests/results/results_glm.py
--- a/statsmodels/genmod/tests/results/results_glm.py
+++ b/statsmodels/genmod/tests/results/results_glm.py
@@ -8,6 +8,7 @@
 import numpy as np
 import pandas as pd
 from statsmodels.compat.python import asbytes
+from statsmodels.compat.numpy import NP_LT_113
 from . import glm_test_resids
 import os
 from statsmodels.api import add_constant, categorical
@@ -15,8 +16,6 @@
 # for genfromtxt changes
 import sys
 PY2 = (sys.version_info[0] < 3)
-from distutils.version import LooseVersion
-NUMPY_LT_113 = LooseVersion(np.__version__) < '1.13.0'
 
 # Test Precisions
 DECIMAL_4 = 4
@@ -693,7 +692,7 @@ def __init__(self):
             "stata_lbw_glm.csv")
 
         # https://github.com/statsmodels/statsmodels/pull/4432#issuecomment-379279617
-        if NUMPY_LT_113 or PY2:
+        if NP_LT_113 or PY2:
             with open(filename, 'rb') as datafile:
                 data=np.recfromcsv(datafile)
             vfunc = np.vectorize(lambda x: x.strip(asbytes("\"")))
diff --git a/statsmodels/graphics/tests/test_mosaicplot.py b/statsmodels/graphics/tests/test_mosaicplot.py
--- a/statsmodels/graphics/tests/test_mosaicplot.py
+++ b/statsmodels/graphics/tests/test_mosaicplot.py
@@ -12,8 +12,9 @@
 
 import numpy as np
 from itertools import product
+
 try:
-    import matplotlib.pyplot as plt
+    import matplotlib.pyplot as plt  # noqa:F401
 except ImportError:
     pass
 
@@ -112,25 +113,25 @@ def test_mosaic(close_figures):
     datas = datas.sort_values(['rate_marriage', 'religious'])
 
     num_to_desc = {1: 'awful', 2: 'bad', 3: 'intermediate',
-                      4: 'good', 5: 'wonderful'}
+                   4: 'good', 5: 'wonderful'}
     datas['rate_marriage'] = datas['rate_marriage'].map(num_to_desc)
     num_to_faith = {1: 'non religious', 2: 'poorly religious', 3: 'religious',
-                      4: 'very religious'}
+                    4: 'very religious'}
     datas['religious'] = datas['religious'].map(num_to_faith)
     num_to_cheat = {False: 'faithful', True: 'cheated'}
     datas['cheated'] = datas['cheated'].map(num_to_cheat)
     # finished cleaning
     _, ax = plt.subplots(2, 2)
     mosaic(datas, ['rate_marriage', 'cheated'], ax=ax[0, 0],
-                title='by marriage happiness')
+           title='by marriage happiness')
     mosaic(datas, ['religious', 'cheated'], ax=ax[0, 1],
-                title='by religiosity')
+           title='by religiosity')
     mosaic(datas, ['rate_marriage', 'religious', 'cheated'], ax=ax[1, 0],
-                title='by both', labelizer=lambda k:'')
+           title='by both', labelizer=lambda k:'')
     ax[1, 0].set_xlabel('marriage rating')
     ax[1, 0].set_ylabel('religion status')
     mosaic(datas, ['religious', 'rate_marriage'], ax=ax[1, 1],
-                title='inter-dependence', axes_label=False)
+           title='inter-dependence', axes_label=False)
     plt.suptitle("extramarital affairs (plot 3 of 4)")
 
 
@@ -141,7 +142,7 @@ def test_mosaic_very_complex(close_figures):
     # new function that does this automatically based on the type of data
     key_name = ['gender', 'age', 'health', 'work']
     key_base = (['male', 'female'], ['old', 'young'],
-                    ['healty', 'ill'], ['work', 'unemployed'])
+                ['healty', 'ill'], ['work', 'unemployed'])
     keys = list(product(*key_base))
     data = OrderedDict(zip(keys, range(1, 1 + len(keys))))
     props = {}
@@ -188,7 +189,7 @@ def test_axes_labeling(close_figures):
     fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8))
     mosaic(data, ax=ax1, labelizer=lab, horizontal=True, label_rotation=45)
     mosaic(data, ax=ax2, labelizer=lab, horizontal=False,
-        label_rotation=[0, 45, 90, 0])
+           label_rotation=[0, 45, 90, 0])
     #fig.tight_layout()
     fig.suptitle("correct alignment of the axes labels")
 
@@ -211,8 +212,8 @@ def test_mosaic_empty_cells(close_figures):
                                    62: 'default', 63: 'default'}})
 
     ct = pd.crosstab(mydata.id1, mydata.id2)
-    fig, vals = mosaic(ct.T.unstack())
-    fig, vals = mosaic(mydata, ['id1','id2'])
+    _, vals = mosaic(ct.T.unstack())
+    _, vals = mosaic(mydata, ['id1','id2'])
 
 
 eq = lambda x, y: assert_(np.allclose(x, y))
diff --git a/statsmodels/graphics/tests/test_tsaplots.py b/statsmodels/graphics/tests/test_tsaplots.py
--- a/statsmodels/graphics/tests/test_tsaplots.py
+++ b/statsmodels/graphics/tests/test_tsaplots.py
@@ -1,13 +1,12 @@
 from statsmodels.compat.python import lmap, BytesIO
 
-from distutils.version import LooseVersion
-
 import numpy as np
 import pandas as pd
 from numpy.testing import assert_equal, assert_
 import pytest
 
 import statsmodels.api as sm
+from statsmodels.compat.pandas import pandas_lte_0_19_2
 from statsmodels.graphics.tsaplots import (plot_acf, plot_pacf, month_plot,
                                            quarter_plot, seasonal_plot)
 import statsmodels.tsa.arima_process as tsp
@@ -18,8 +17,6 @@
 except ImportError:
     pass
 
-pandas_lt_0_19_2 = LooseVersion(pd.__version__) < '0.19.1'
-
 
 @pytest.mark.matplotlib
 def test_plot_acf(close_figures):
@@ -156,7 +153,7 @@ def test_plot_pacf_irregular(close_figures):
     plot_pacf(pacf, ax=ax, alpha=None, zero=False)
 
 
-@pytest.mark.skipif(pandas_lt_0_19_2, reason='pandas too old')
+@pytest.mark.skipif(pandas_lte_0_19_2, reason='pandas too old')
 @pytest.mark.matplotlib
 def test_plot_month(close_figures):
     dta = sm.datasets.elnino.load_pandas().data
@@ -180,7 +177,7 @@ def test_plot_month(close_figures):
     fig = month_plot(dta)
 
 
-@pytest.mark.skipif(pandas_lt_0_19_2, reason='pandas too old')
+@pytest.mark.skipif(pandas_lte_0_19_2, reason='pandas too old')
 @pytest.mark.matplotlib
 def test_plot_quarter(close_figures):
     dta = sm.datasets.macrodata.load_pandas().data
diff --git a/statsmodels/imputation/tests/test_ros.py b/statsmodels/imputation/tests/test_ros.py
--- a/statsmodels/imputation/tests/test_ros.py
+++ b/statsmodels/imputation/tests/test_ros.py
@@ -13,9 +13,6 @@
 from statsmodels.imputation import ros
 from statsmodels.compat.python import StringIO
 
-if pandas.__version__.split('.')[1] <  '14':
-    __test__ = False
-
 
 def load_basic_data():
     raw_csv = StringIO(
diff --git a/statsmodels/multivariate/tests/test_factor.py b/statsmodels/multivariate/tests/test_factor.py
--- a/statsmodels/multivariate/tests/test_factor.py
+++ b/statsmodels/multivariate/tests/test_factor.py
@@ -208,8 +208,7 @@ def test_getframe_smoke():
 
     df = res.get_loadings_frame(style='raw')
     assert_(isinstance(df, pd.DataFrame))
-    if pd.__version__ < '0.17':
-        return
+
     lds = res.get_loadings_frame(style='strings', decimals=3, threshold=0.3)
     lds.to_latex()
 
diff --git a/statsmodels/stats/tests/test_power.py b/statsmodels/stats/tests/test_power.py
--- a/statsmodels/stats/tests/test_power.py
+++ b/statsmodels/stats/tests/test_power.py
@@ -11,26 +11,21 @@
 """
 import copy
 import warnings
-from distutils.version import LooseVersion
-
 
 import numpy as np
 from numpy.testing import (assert_almost_equal, assert_allclose, assert_raises,
                            assert_equal, assert_warns)
 import pytest
-import scipy
 
 import statsmodels.stats.power as smp
 from statsmodels.stats.tests.test_weightstats import Holder
 
 try:
-    import matplotlib.pyplot as plt
+    import matplotlib.pyplot as plt  # noqa:F401
 except ImportError:
     pass
 
 
-SM_GT_10 = LooseVersion(scipy.__version__) >= '0.10'
-
 class CheckPowerMixin(object):
 
     def test_power(self):
@@ -738,7 +733,9 @@ def test_power_solver():
     assert_raises(ValueError, nip.solve_power, nobs1=None, effect_size=0, alpha=0.01,
                   power=0.005, ratio=1, alternative='larger')
 
-@pytest.mark.skipif(SM_GT_10, reason='Known failure on modern SciPy')
+
+# TODO: can something useful be made from this?
+@pytest.mark.skip(reason='Known failure on modern SciPy >= 0.10')
 def test_power_solver_warn():
     # messing up the solver to trigger warning
     # I wrote this with scipy 0.9,
diff --git a/statsmodels/tsa/vector_ar/tests/test_dynamic.py b/statsmodels/tsa/vector_ar/tests/test_dynamic.py
--- a/statsmodels/tsa/vector_ar/tests/test_dynamic.py
+++ b/statsmodels/tsa/vector_ar/tests/test_dynamic.py
@@ -1,4 +1,3 @@
-from distutils.version import LooseVersion
 
 import numpy as np
 import pandas as pd
@@ -6,9 +5,9 @@
 from numpy.testing import assert_allclose
 from pandas.util.testing import assert_frame_equal, assert_series_equal
 
-PD_GT_19 = LooseVersion(pd.__version__) > LooseVersion('0.19.2')
+from statsmodels.compat.pandas import pandas_gt_0_19_2
 
-pytestmark = pytest.mark.skipif(PD_GT_19,
+pytestmark = pytest.mark.skipif(pandas_gt_0_19_2,
                                 reason='Requires pandas <= 0.19.2')
 
 from statsmodels.tsa.vector_ar.dynamic import _window_ols