From fd563f0be04d08e355372c05a0917f1d894d8fff Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 12 Dec 2023 15:06:55 +0100 Subject: [PATCH 1/4] feat(selectors): Add All and None_ selectors --- src/pandas_indexing/selectors.py | 46 ++++++++++++++++++++++++++++++++ tests/test_selectors.py | 31 ++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/pandas_indexing/selectors.py b/src/pandas_indexing/selectors.py index aad0800..a5b127f 100644 --- a/src/pandas_indexing/selectors.py +++ b/src/pandas_indexing/selectors.py @@ -26,9 +26,13 @@ def __invert__(self): return Not(self) def __and__(self, other): + if isinstance(other, Special): + return other & self return And(self, maybe_const(other)) def __or__(self, other): + if isinstance(other, Special): + return other | self return Or(self, maybe_const(other)) __rand__ = __and__ @@ -69,6 +73,48 @@ def __call__(self, df): return ~self.a.__call__(df) +class Special(Selector): + pass + + +@define +class AllSelector(Special): + def __invert__(self): + return NoneSelector() + + def __and__(self, other): + return other + + def __or__(self, other): + return self + + def __call__(self, df): + return Series(True, df.index) + + +@define +class NoneSelector(Special): + def __invert__(self): + return AllSelector() + + def __and__(self, other): + return self + + def __or__(self, other): + return other + + __rand__ = __and__ + __ror__ = __or__ + + def __call__(self, df): + return Series(False, df.index) + + +# Singletons for easy access +All = AllSelector() +None_ = NoneSelector() + + @define class Isin(Selector): filters: Mapping[str, Any] diff --git a/tests/test_selectors.py b/tests/test_selectors.py index e0e06fb..ac474d9 100644 --- a/tests/test_selectors.py +++ b/tests/test_selectors.py @@ -5,7 +5,16 @@ from pandas.testing import assert_frame_equal, assert_series_equal from pandas_indexing import isin, ismatch -from pandas_indexing.selectors import And, Const, Isin, Ismatch, Not, Or +from pandas_indexing.selectors import ( + AllSelector, + And, + Const, + Isin, + Ismatch, + NoneSelector, + Not, + Or, +) def test_isin_mseries(mseries: Series): @@ -57,3 +66,23 @@ def test_ismatch_explicitly_given(sdf): assert_series_equal( ismatch(sdf.columns, ["o*"]), Series([True, False], sdf.columns) ) + + +def test_all_none(sdf): + assert isinstance(~AllSelector(), NoneSelector) + assert isinstance(~NoneSelector(), AllSelector) + + sel = isin(str="bar") + + assert isinstance(sel | AllSelector(), AllSelector) + assert isinstance(AllSelector() | sel, AllSelector) + assert sel & AllSelector() == sel + assert AllSelector() & sel == sel + + assert sel | NoneSelector() == sel + assert NoneSelector() | sel == sel + assert isinstance(sel & NoneSelector(), NoneSelector) + assert isinstance(NoneSelector() & sel, NoneSelector) + + assert_frame_equal(sdf.loc[AllSelector()], sdf) + assert_frame_equal(sdf.loc[NoneSelector()], sdf.iloc[[]]) From 9ef8fbf658a6128dbc0eb2a49b9a1e5eb14a8db1 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 12 Dec 2023 15:08:00 +0100 Subject: [PATCH 2/4] Import All and None_ from __init__ --- src/pandas_indexing/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pandas_indexing/__init__.py b/src/pandas_indexing/__init__.py index c9cc6d6..1405415 100644 --- a/src/pandas_indexing/__init__.py +++ b/src/pandas_indexing/__init__.py @@ -21,7 +21,7 @@ to_tidy, uniquelevel, ) -from .selectors import isin, ismatch +from .selectors import All, None_, isin, ismatch from .units import convert_unit, dequantify, quantify, set_openscm_registry_as_default From 608cfd742e51d457c35d8ae734f28c2fc42c39f3 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 12 Dec 2023 15:10:55 +0100 Subject: [PATCH 3/4] Update CHANGELOG [skip ci] --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6109c10..e059cfe 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,7 @@ Changelog ========= +* Add :attr:`~selectors.All` and :attr:`~selectors.None_` completing selector group :pull:`46` * Fix type hints of function :func:`~core.aggregatelevel` * Switch from black to ruff for formatting and update pre-commit versions From caffa3f79ef9e8ac94bc3bd76f5ef42786a16097 Mon Sep 17 00:00:00 2001 From: Jonas Hoersch Date: Tue, 12 Dec 2023 15:15:19 +0100 Subject: [PATCH 4/4] Re-flow CHANGELOG --- CHANGELOG.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e0dd719..ea8402d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,8 @@ v0.4.0 (2023-12-12) ------------------------------------------------------------ * **BREAKING** :mod:`accessors` is imported implicitly. User code does not need to import it any longer :pull:`45` -* Add :attr:`~selectors.All` and :attr:`~selectors.None_` completing selector group :pull:`46` +* Add :attr:`~selectors.All` and :attr:`~selectors.None_` completing selector group + :pull:`46` * Fix type hints of function :func:`~core.aggregatelevel` :pull:`44` * Switch from black to ruff for formatting and update pre-commit versions :pull:`43`