From a6114b4a65dcd4b130715d3c3d94ce3993d1b2df Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Tue, 5 Jun 2018 06:24:02 -0500 Subject: [PATCH 1/9] Compat with NumPy 1.15 logical func Accepts axis=None as reduce all dims --- pandas/core/frame.py | 22 +++++- pandas/core/generic.py | 43 +++++++---- pandas/core/series.py | 2 + pandas/tests/frame/test_analytics.py | 107 +++++++++++++++++++++++++-- 4 files changed, 151 insertions(+), 23 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 02c86d2f4dcc8..e065aa7db47ed 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -6844,13 +6844,18 @@ def _count_level(self, level, axis=0, numeric_only=False): def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None, filter_type=None, **kwds): - axis = self._get_axis_number(axis) + if axis is None and filter_type == 'bool': + labels = None + constructor = None + else: + # TODO: Make other agg func handle axis=None properly + axis = self._get_axis_number(axis) + labels = self._get_agg_axis(axis) + constructor = self._constructor def f(x): return op(x, axis=axis, skipna=skipna, **kwds) - labels = self._get_agg_axis(axis) - # exclude timedelta/datetime unless we are uniform types if axis == 1 and self._is_mixed_type and self._is_datelike_mixed_type: numeric_only = True @@ -6859,6 +6864,13 @@ def f(x): try: values = self.values result = f(values) + + if (filter_type == 'bool' and values.dtype.kind == 'O' and + axis is None): + # work around https://github.com/numpy/numpy/issues/10489 + # TODO: combine with hasattr(result, 'dtype') further down + # hard since we don't have `values` down there. + result = np.bool_(result) except Exception as e: # try by-column first @@ -6925,7 +6937,9 @@ def f(x): if axis == 0: result = coerce_to_dtypes(result, self.dtypes) - return Series(result, index=labels) + if constructor is not None: + result = Series(result, index=labels) + return result def nunique(self, axis=0, dropna=True): """ diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 32f64b1d3e05c..6d8e445e1bf82 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -8728,6 +8728,8 @@ def pct_change(self, periods=1, fill_method='pad', limit=None, freq=None, return rs def _agg_by_level(self, name, axis=0, level=0, skipna=True, **kwargs): + if axis is None: + raise ValueError("Must specify 'axis' when aggregating by level.") grouped = self.groupby(level=level, axis=axis, sort=False) if hasattr(grouped, name) and skipna: return getattr(grouped, name)(**kwargs) @@ -9055,8 +9057,16 @@ def _doc_parms(cls): Parameters ---------- -axis : int, default 0 - Select the axis which can be 0 for indices and 1 for columns. +axis : {None, 0 or 'index', 1 or 'columns'}, default None + Indicate which axis should be reduced. By default all axes are reduced + and a scalar is returned. + + * None : reduce all axes, return a scalar. + * 0 / 'index' : reduce the index, return a Series whose index is the + original column labels. + * 1 / 'columns' : reduce the columns, return a Series whose index is the + original index. + skipna : boolean, default True Exclude NA/null values. If an entire row/column is NA, the result will be NA. @@ -9078,9 +9088,9 @@ def _doc_parms(cls): %(examples)s""" _all_doc = """\ -Return whether all elements are True over series or dataframe axis. +Return whether all elements are True, potentially over an axis. -Returns True if all elements within a series or along a dataframe +Returns True if all elements within a series or along a Dataframe axis are non-zero, not-empty or not-False.""" _all_examples = """\ @@ -9093,7 +9103,7 @@ def _doc_parms(cls): >>> pd.Series([True, False]).all() False -Dataframes +DataFrames Create a dataframe from a dictionary. @@ -9110,12 +9120,17 @@ def _doc_parms(cls): col2 False dtype: bool -Adding axis=1 argument will check if row-wise values all return True. +Specify ``axis=1`` to check if row-wise values all return True. >>> df.all(axis=1) 0 True 1 False dtype: bool + +Or ``axis=None`` for whether every value is True. + +>>> df.all(axis=None) +False """ _all_see_also = """\ @@ -9481,6 +9496,11 @@ def _doc_parms(cls): 1 False dtype: bool +Aggregating over the entire DataFrame with ``axis=None``. + +>>> df.any(axis=None) +True + `any` for an empty DataFrame is an empty Series. >>> pd.DataFrame([]).any() @@ -9651,22 +9671,17 @@ def _make_logical_function(cls, name, name1, name2, axis_descr, desc, f, @Substitution(outname=name, desc=desc, name1=name1, name2=name2, axis_descr=axis_descr, examples=examples, see_also=see_also) @Appender(_bool_doc) - def logical_func(self, axis=None, bool_only=None, skipna=None, level=None, + def logical_func(self, axis=0, bool_only=None, skipna=True, level=None, **kwargs): nv.validate_logical_func(tuple(), kwargs, fname=name) - if skipna is None: - skipna = True - if axis is None: - axis = self._stat_axis_number if level is not None: if bool_only is not None: raise NotImplementedError("Option bool_only is not " "implemented with option level.") return self._agg_by_level(name, axis=axis, level=level, skipna=skipna) - return self._reduce(f, axis=axis, skipna=skipna, - numeric_only=bool_only, filter_type='bool', - name=name) + return self._reduce(f, name, axis=axis, skipna=skipna, + numeric_only=bool_only, filter_type='bool') return set_function_name(logical_func, name, cls) diff --git a/pandas/core/series.py b/pandas/core/series.py index 0450f28087f66..5fbc9d7088a1d 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3238,6 +3238,8 @@ def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None, """ delegate = self._values + if axis is None: + axis = self._stat_axis_number if isinstance(delegate, np.ndarray): # Validate that 'axis' is consistent with Series's single axis. self._get_axis_number(axis) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index b8f1acc2aa679..a0762fa1e3b59 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -1159,11 +1159,34 @@ def test_any_all(self): self._check_bool_op('any', np.any, has_skipna=True, has_bool_only=True) self._check_bool_op('all', np.all, has_skipna=True, has_bool_only=True) - df = DataFrame(randn(10, 4)) > 0 - df.any(1) - df.all(1) - df.any(1, bool_only=True) - df.all(1, bool_only=True) + df = DataFrame({ + 'A': [True, False, False], + 'B': [True, True, False], + 'C': [True, True, True], + }, index=['a', 'b', 'c']) + result = df[['A', 'B']].any(1) + expected = Series([True, True, False], index=['a', 'b', 'c']) + tm.assert_series_equal(result, expected) + + result = df[['A', 'B']].any(1, bool_only=True) + tm.assert_series_equal(result, expected) + + result = df.all(1) + expected = Series([True, False, False], index=['a', 'b', 'c']) + tm.assert_series_equal(result, expected) + + result = df.all(1, bool_only=True) + tm.assert_series_equal(result, expected) + + # Axis is None + result = df.all(axis=None).item() + assert result is False + + result = df.any(axis=None).item() + assert result is True + + result = df[['C']].all(axis=None).item() + assert result is True # skip pathological failure cases # class CantNonzero(object): @@ -1185,6 +1208,80 @@ def test_any_all(self): # df.any(1, bool_only=True) # df.all(1, bool_only=True) + @pytest.mark.parametrize('func, data, expected', [ + (np.any, {}, False), + (np.all, {}, True), + (np.any, {'A': []}, False), + (np.all, {'A': []}, True), + (np.any, {'A': [False, False]}, False), + (np.all, {'A': [False, False]}, False), + (np.any, {'A': [True, False]}, True), + (np.all, {'A': [True, False]}, False), + (np.any, {'A': [True, True]}, True), + (np.all, {'A': [True, True]}, True), + + (np.any, {'A': [False], 'B': [False]}, False), + (np.all, {'A': [False], 'B': [False]}, False), + + (np.any, {'A': [False, False], 'B': [False, True]}, True), + (np.all, {'A': [False, False], 'B': [False, True]}, False), + + # other types + (np.all, {'A': pd.Series([0.0, 1.0], dtype='float')}, False), + (np.any, {'A': pd.Series([0.0, 1.0], dtype='float')}, True), + (np.all, {'A': pd.Series([0, 1], dtype=int)}, False), + (np.any, {'A': pd.Series([0, 1], dtype=int)}, True), + (np.all, {'A': pd.Series([0, 1], dtype='M8[ns]')}, False), + (np.any, {'A': pd.Series([0, 1], dtype='M8[ns]')}, True), + (np.all, {'A': pd.Series([1, 2], dtype='M8[ns]')}, True), + (np.any, {'A': pd.Series([1, 2], dtype='M8[ns]')}, True), + (np.all, {'A': pd.Series([0, 1], dtype='m8[ns]')}, False), + (np.any, {'A': pd.Series([0, 1], dtype='m8[ns]')}, True), + (np.all, {'A': pd.Series([1, 2], dtype='m8[ns]')}, True), + (np.any, {'A': pd.Series([1, 2], dtype='m8[ns]')}, True), + (np.all, {'A': pd.Series([0, 1], dtype='category')}, False), + (np.any, {'A': pd.Series([0, 1], dtype='category')}, True), + (np.all, {'A': pd.Series([1, 2], dtype='category')}, True), + (np.any, {'A': pd.Series([1, 2], dtype='category')}, True), + + # (np.all, {'A': pd.Series([0, 1], dtype=int)}, False), + + # # Mix + # GH-21484 + # (np.all, {'A': pd.Series([10, 20], dtype='M8[ns]'), + # 'B': pd.Series([10, 20], dtype='m8[ns]')}, True), + ]) + def test_any_all_np_func(self, func, data, expected): + # https://github.com/pandas-dev/pandas/issues/19976 + data = DataFrame(data) + result = func(data) + assert isinstance(result, np.bool_) + assert result.item() is expected + + # method version + result = getattr(DataFrame(data), func.__name__)(axis=None) + assert isinstance(result, np.bool_) + assert result.item() is expected + + def test_any_all_object(self): + # https://github.com/pandas-dev/pandas/issues/19976 + result = np.all(DataFrame(columns=['a', 'b'])).item() + assert result is True + + result = np.any(DataFrame(columns=['a', 'b'])).item() + assert result is False + + @pytest.mark.parametrize('method', ['any', 'all']) + def test_any_all_level_axis_none_raises(self, method): + df = DataFrame( + {"A": 1}, + index=MultiIndex.from_product([['A', 'B'], ['a', 'b']], + names=['out', 'in']) + ) + xpr = "Must specify 'axis' when aggregating by level." + with tm.assert_raises_regex(ValueError, xpr): + getattr(df, method)(axis=None, level='out') + def _check_bool_op(self, name, alternative, frame=None, has_skipna=True, has_bool_only=False): if frame is None: From 4bb6e53fc32277b6c738646a420a3e1ba83e7835 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 14 Jun 2018 14:53:48 -0500 Subject: [PATCH 2/9] whatsnew --- doc/source/whatsnew/v0.23.2.txt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/doc/source/whatsnew/v0.23.2.txt b/doc/source/whatsnew/v0.23.2.txt index 79a4c3da2ffa4..10a47e0e5d4d7 100644 --- a/doc/source/whatsnew/v0.23.2.txt +++ b/doc/source/whatsnew/v0.23.2.txt @@ -10,6 +10,36 @@ and bug fixes. We recommend that all users upgrade to this version. :local: :backlinks: none +.. _whatsnew_0232.enhancements: + +Logical Reductions over Entire DataFrame +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:meth:`DataFrame.all` and :meth:`DataFrame.any` now accept ``axis=None`` to reduce over all axes to a scalar (:issue:`19976`) + +.. ipython:: python + + df = pd.DataFrame({"A": [1, 2], "B": [True, False]}) + df.all(axis=None) + + +This also provides compatibility with NumPy 1.15, which now dispatches to ``DataFrame.all``. +With NumPy 1.15 and pandas 0.23.1 or earlier, :func:`numpy.all` will not reduce over every axis: + +.. code-block:: python + + >>> # NumPy 1.15, pandas 0.23.1 + >>> np.any(pd.DataFrame({"A": [False], "B": [False]})) + A False + B False + dtype: bool + +With pandas 0.23.2, that will correctly return False. + +.. ipython:: python + + np.any(pd.DataFrame({"A": [False], "B": [False]})) + .. _whatsnew_0232.fixed_regressions: From 9636d545fee2302da2795d6b5f19d19c65758520 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 14 Jun 2018 14:58:02 -0500 Subject: [PATCH 3/9] remove old test --- pandas/tests/frame/test_analytics.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index a0762fa1e3b59..08a24a845a81f 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -1244,8 +1244,6 @@ def test_any_all(self): (np.all, {'A': pd.Series([1, 2], dtype='category')}, True), (np.any, {'A': pd.Series([1, 2], dtype='category')}, True), - # (np.all, {'A': pd.Series([0, 1], dtype=int)}, False), - # # Mix # GH-21484 # (np.all, {'A': pd.Series([10, 20], dtype='M8[ns]'), From 18c1b11a51ffe71d37dd83c3272fa4cf1eadf5d2 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 20 Jun 2018 08:01:39 -0500 Subject: [PATCH 4/9] updated docs --- doc/source/whatsnew/v0.23.2.txt | 4 ++-- pandas/core/frame.py | 2 +- pandas/core/generic.py | 11 +++++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/source/whatsnew/v0.23.2.txt b/doc/source/whatsnew/v0.23.2.txt index 10a47e0e5d4d7..c0db1ca03cf34 100644 --- a/doc/source/whatsnew/v0.23.2.txt +++ b/doc/source/whatsnew/v0.23.2.txt @@ -24,7 +24,7 @@ Logical Reductions over Entire DataFrame This also provides compatibility with NumPy 1.15, which now dispatches to ``DataFrame.all``. -With NumPy 1.15 and pandas 0.23.1 or earlier, :func:`numpy.all` will not reduce over every axis: +With NumPy 1.15 and pandas 0.23.1 or earlier, :func:`numpy.all` will no longer reduce over every axis: .. code-block:: python @@ -34,7 +34,7 @@ With NumPy 1.15 and pandas 0.23.1 or earlier, :func:`numpy.all` will not reduce B False dtype: bool -With pandas 0.23.2, that will correctly return False. +With pandas 0.23.2, that will correctly return False, as it did with NumPy < 1.15. .. ipython:: python diff --git a/pandas/core/frame.py b/pandas/core/frame.py index e065aa7db47ed..2d9395ce7697f 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -6865,7 +6865,7 @@ def f(x): values = self.values result = f(values) - if (filter_type == 'bool' and values.dtype.kind == 'O' and + if (filter_type == 'bool' and is_object_dtype(values) and axis is None): # work around https://github.com/numpy/numpy/issues/10489 # TODO: combine with hasattr(result, 'dtype') further down diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 6d8e445e1bf82..e1273d3119108 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -9057,15 +9057,14 @@ def _doc_parms(cls): Parameters ---------- -axis : {None, 0 or 'index', 1 or 'columns'}, default None - Indicate which axis should be reduced. By default all axes are reduced - and a scalar is returned. +axis : {0 or 'index', 1 or 'columns', None}, default 0 + Indicate which axis or axes should be reduced. - * None : reduce all axes, return a scalar. * 0 / 'index' : reduce the index, return a Series whose index is the original column labels. * 1 / 'columns' : reduce the columns, return a Series whose index is the original index. + * None : reduce all axes, return a scalar. skipna : boolean, default True Exclude NA/null values. If an entire row/column is NA, the result @@ -9120,9 +9119,9 @@ def _doc_parms(cls): col2 False dtype: bool -Specify ``axis=1`` to check if row-wise values all return True. +Specify ``axis='columns'`` to check if row-wise values all return True. ->>> df.all(axis=1) +>>> df.all(axis='columns') 0 True 1 False dtype: bool From 1c32b2bc117092b4a2a7cd70f956340dfbcc5256 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Wed, 20 Jun 2018 13:46:25 -0500 Subject: [PATCH 5/9] Handle panel --- pandas/core/panel.py | 17 +++++++++++++++-- pandas/tests/test_panel.py | 7 +++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pandas/core/panel.py b/pandas/core/panel.py index c4aa471b8b944..4f7400ad8388b 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -1143,13 +1143,26 @@ def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None, raise NotImplementedError('Panel.{0} does not implement ' 'numeric_only.'.format(name)) - axis_name = self._get_axis_name(axis) - axis_number = self._get_axis_number(axis_name) + if axis is None and filter_type == 'bool': + # labels = None + # constructor = None + axis_number = None + axis_name = None + else: + # TODO: Make other agg func handle axis=None properly + axis = self._get_axis_number(axis) + # labels = self._get_agg_axis(axis) + # constructor = self._constructor + axis_name = self._get_axis_name(axis) + axis_number = self._get_axis_number(axis_name) + f = lambda x: op(x, axis=axis_number, skipna=skipna, **kwds) with np.errstate(all='ignore'): result = f(self.values) + if axis is None and filter_type == 'bool': + return np.bool_(result) axes = self._get_plane_axes(axis_name) if result.ndim == 2 and axis_name != self._info_axis_name: result = result.T diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index d95a2ad2d7f76..2f8bc228cf86e 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -2707,3 +2707,10 @@ def test_panel_index(): np.repeat([1, 2, 3], 4)], names=['time', 'panel']) tm.assert_index_equal(index, expected) + + +def test_panel_np_all(): + with catch_warnings(record=True): + wp = Panel({"A": DataFrame({'b': [1, 2]})}) + result = np.all(wp) + assert result == np.bool_(True) From 1f944699c2d7d68c7abe156e7234b24c4480e098 Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Thu, 21 Jun 2018 10:29:24 -0500 Subject: [PATCH 6/9] Skip for np 114 --- pandas/core/series.py | 5 ++--- pandas/tests/frame/test_analytics.py | 25 +++++++++++++++++-------- pandas/util/_test_decorators.py | 4 ++++ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/pandas/core/series.py b/pandas/core/series.py index 044f4948171a8..d374ddbf59ad2 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -3239,11 +3239,10 @@ def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None, """ delegate = self._values - if axis is None: - axis = self._stat_axis_number if isinstance(delegate, np.ndarray): # Validate that 'axis' is consistent with Series's single axis. - self._get_axis_number(axis) + if axis is not None: + self._get_axis_number(axis) if numeric_only: raise NotImplementedError('Series.{0} does not implement ' 'numeric_only.'.format(name)) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 4d7db412b50af..a809c8fa43310 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -1159,6 +1159,7 @@ def test_any_all(self): self._check_bool_op('any', np.any, has_skipna=True, has_bool_only=True) self._check_bool_op('all', np.all, has_skipna=True, has_bool_only=True) + def test_any_all_extra(self): df = DataFrame({ 'A': [True, False, False], 'B': [True, True, False], @@ -1231,14 +1232,22 @@ def test_any_all(self): (np.any, {'A': pd.Series([0.0, 1.0], dtype='float')}, True), (np.all, {'A': pd.Series([0, 1], dtype=int)}, False), (np.any, {'A': pd.Series([0, 1], dtype=int)}, True), - (np.all, {'A': pd.Series([0, 1], dtype='M8[ns]')}, False), - (np.any, {'A': pd.Series([0, 1], dtype='M8[ns]')}, True), - (np.all, {'A': pd.Series([1, 2], dtype='M8[ns]')}, True), - (np.any, {'A': pd.Series([1, 2], dtype='M8[ns]')}, True), - (np.all, {'A': pd.Series([0, 1], dtype='m8[ns]')}, False), - (np.any, {'A': pd.Series([0, 1], dtype='m8[ns]')}, True), - (np.all, {'A': pd.Series([1, 2], dtype='m8[ns]')}, True), - (np.any, {'A': pd.Series([1, 2], dtype='m8[ns]')}, True), + pytest.param(np.all, {'A': pd.Series([0, 1], dtype='M8[ns]')}, False, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.any, {'A': pd.Series([0, 1], dtype='M8[ns]')}, True, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.all, {'A': pd.Series([1, 2], dtype='M8[ns]')}, True, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.any, {'A': pd.Series([1, 2], dtype='M8[ns]')}, True, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.all, {'A': pd.Series([0, 1], dtype='m8[ns]')}, False, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.any, {'A': pd.Series([0, 1], dtype='m8[ns]')}, True, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.all, {'A': pd.Series([1, 2], dtype='m8[ns]')}, True, + marks=[td.skip_if_np_lt_115]), + pytest.param(np.any, {'A': pd.Series([1, 2], dtype='m8[ns]')}, True, + marks=[td.skip_if_np_lt_115]), (np.all, {'A': pd.Series([0, 1], dtype='category')}, False), (np.any, {'A': pd.Series([0, 1], dtype='category')}, True), (np.all, {'A': pd.Series([1, 2], dtype='category')}, True), diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 89d90258f58e0..49827bdcba755 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -27,6 +27,7 @@ def test_foo(): import pytest import locale from distutils.version import LooseVersion +import numpy as np from pandas.compat import (is_platform_windows, is_platform_32bit, PY3, import_lzma) @@ -160,6 +161,9 @@ def decorated_func(func): skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(), reason="Missing matplotlib dependency") + +skip_if_np_lt_115 = pytest.mark.skipif(np.__version__ < LooseVersion("1.15.0"), + reason="NumPy 1.15 or greater required") skip_if_mpl = pytest.mark.skipif(not _skip_if_no_mpl(), reason="matplotlib is present") skip_if_mpl_1_5 = pytest.mark.skipif(_skip_if_mpl_1_5(), From 50db7194b56a91b6ede118f7dc46ba0926a9247d Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Fri, 22 Jun 2018 22:01:32 -0500 Subject: [PATCH 7/9] reuse numpy compat --- pandas/util/_test_decorators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 49827bdcba755..27c24e3a68079 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -27,10 +27,10 @@ def test_foo(): import pytest import locale from distutils.version import LooseVersion -import numpy as np from pandas.compat import (is_platform_windows, is_platform_32bit, PY3, import_lzma) +from pandas.compat.numpy import _np_version_under1p15 from pandas.core.computation.expressions import (_USE_NUMEXPR, _NUMEXPR_INSTALLED) @@ -162,7 +162,7 @@ def decorated_func(func): skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(), reason="Missing matplotlib dependency") -skip_if_np_lt_115 = pytest.mark.skipif(np.__version__ < LooseVersion("1.15.0"), +skip_if_np_lt_115 = pytest.mark.skipif(_np_version_under1p15, reason="NumPy 1.15 or greater required") skip_if_mpl = pytest.mark.skipif(not _skip_if_no_mpl(), reason="matplotlib is present") From 9fd9740986a8d6110009a5898ee73afb06b4c31e Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Fri, 22 Jun 2018 22:04:54 -0500 Subject: [PATCH 8/9] remove xfail --- pandas/tests/frame/test_analytics.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index a809c8fa43310..329f4447e4d25 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -2178,9 +2178,6 @@ def test_clip_against_list_like(self, inplace, lower, axis, res): result = original tm.assert_frame_equal(result, expected, check_exact=True) - @pytest.mark.xfail( - not _np_version_under1p15, - reason="failing under numpy-dev gh-19976") @pytest.mark.parametrize("axis", [0, 1, None]) def test_clip_against_frame(self, axis): df = DataFrame(np.random.randn(1000, 2)) From ae759bdb3fb1f04ac8ebed5eca9e8baa7ca2245b Mon Sep 17 00:00:00 2001 From: Tom Augspurger Date: Sun, 24 Jun 2018 13:56:51 -0500 Subject: [PATCH 9/9] Linting --- pandas/tests/frame/test_analytics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/frame/test_analytics.py b/pandas/tests/frame/test_analytics.py index 329f4447e4d25..5f6aec9d882b6 100644 --- a/pandas/tests/frame/test_analytics.py +++ b/pandas/tests/frame/test_analytics.py @@ -15,7 +15,7 @@ from pandas.compat import lrange, PY35 from pandas import (compat, isna, notna, DataFrame, Series, MultiIndex, date_range, Timestamp, Categorical, - _np_version_under1p12, _np_version_under1p15, + _np_version_under1p12, to_datetime, to_timedelta) import pandas as pd import pandas.core.nanops as nanops