From aadb661feac3d498259bae8bd798be609edcc780 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:21:52 -0500 Subject: [PATCH 01/13] resolve columns method --- pointblank/_utils.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pointblank/_utils.py b/pointblank/_utils.py index 1af2c4969..1247585df 100644 --- a/pointblank/_utils.py +++ b/pointblank/_utils.py @@ -11,6 +11,7 @@ from narwhals.typing import FrameT from pointblank._constants import ASSERTION_TYPE_METHOD_MAP, GENERAL_COLUMN_TYPES, IBIS_BACKENDS +from pointblank.column import Column, ColumnLiteral, ColumnSelector, ColumnSelectorNarwhals, col if TYPE_CHECKING: from collections.abc import Mapping @@ -550,6 +551,24 @@ def _column_subset_test_prep( return dfn +_PBUnresolvedColumn = str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals +_PBResolvedColumn = Column | ColumnLiteral | ColumnSelectorNarwhals | list[Column] | list[str] + + +def _resolve_columns(columns: _PBUnresolvedColumn) -> _PBResolvedColumn: + # If `columns` is a ColumnSelector or Narwhals selector, call `col()` on it to later + # resolve the columns + # TODO: I feel like there are several functions that do basically this same thing; consolidate? + if isinstance(columns, (ColumnSelector, nw.selectors.Selector)): + return [col(columns)] + + # If `columns` is Column value or a string, place it in a list for iteration + if isinstance(columns, (Column, str)): + return [columns] + + raise NotImplementedError # pragma: no cover + + def _get_fn_name() -> str: # Get the current function name fn_name = inspect.currentframe().f_back.f_code.co_name @@ -660,10 +679,10 @@ def _format_to_float_value( def _pivot_to_dict(col_dict: Mapping[str, Any]): # TODO : Type hint and unit test result_dict = {} - for col, sub_dict in col_dict.items(): + for _col, sub_dict in col_dict.items(): for key, value in sub_dict.items(): # add columns fields not present if key not in result_dict: result_dict[key] = [None] * len(col_dict) - result_dict[key][list(col_dict.keys()).index(col)] = value + result_dict[key][list(col_dict.keys()).index(_col)] = value return result_dict From a148d88797417ebd68f4f122ca865248aacbcad1 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:22:24 -0500 Subject: [PATCH 02/13] add col sum assertion on validate --- pointblank/_constants.py | 11 +++++ pointblank/validate.py | 97 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 8 deletions(-) diff --git a/pointblank/_constants.py b/pointblank/_constants.py index 39d658460..5879b4503 100644 --- a/pointblank/_constants.py +++ b/pointblank/_constants.py @@ -51,6 +51,7 @@ "tbl_match": "tbl_match", "conjointly": "conjointly", "specially": "specially", + "col_sum_eq": "sum_eq", } COMPARISON_OPERATORS = { @@ -220,6 +221,16 @@ CROSS_MARK_SPAN = "" SVG_ICONS_FOR_ASSERTION_TYPES = { + "col_sum_eq": """ + + col_vals_gt + + + + + + +""", "col_vals_gt": """ col_vals_gt diff --git a/pointblank/validate.py b/pointblank/validate.py index 3ad0fe388..596b70ab6 100644 --- a/pointblank/validate.py +++ b/pointblank/validate.py @@ -25,6 +25,7 @@ from importlib_resources import files from narwhals.typing import FrameT +from pointblank._agg import is_valid_agg, resolve_agg_registries from pointblank._constants import ( ASSERTION_TYPE_METHOD_MAP, CHECK_MARK_SPAN, @@ -90,6 +91,8 @@ _is_lib_present, _is_narwhals_table, _is_value_a_df, + _PBUnresolvedColumn, + _resolve_columns, _select_df_lib, ) from pointblank._utils_check_args import ( @@ -3721,6 +3724,30 @@ class _ValidationInfo: insertion order, ensuring notes appear in a consistent sequence in reports and logs. """ + @classmethod + def from_agg_validator( + cls, + assertion_type: str, + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> _ValidationInfo: + _check_thresholds(thresholds=thresholds) + + return cls( + assertion_type=assertion_type, + column=_resolve_columns(columns), + values={"value": value, "tol": tol}, + thresholds=_normalize_thresholds_creation(thresholds), + brief=_transform_auto_brief(brief=brief), + actions=actions, + active=active, + ) + # Validation plan i: int | None = None i_o: int | None = None @@ -4971,6 +4998,46 @@ def set_tbl( def _repr_html_(self) -> str: return self.get_tabular_report()._repr_html_() # pragma: no cover + def col_sum_eq( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the sum of the values in a column is equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + def col_vals_gt( self, columns: str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals, @@ -5212,6 +5279,7 @@ def col_vals_gt( - Row 1: `c` is `1` and `b` is `2`. - Row 3: `c` is `2` and `b` is `2`. """ + columns = _resolve_columns(columns) assertion_type = _get_fn_name() @@ -5232,14 +5300,7 @@ def col_vals_gt( self.thresholds if thresholds is None else _normalize_thresholds_creation(thresholds) ) - # If `columns` is a ColumnSelector or Narwhals selector, call `col()` on it to later - # resolve the columns - if isinstance(columns, (ColumnSelector, nw.selectors.Selector)): - columns = col(columns) - - # If `columns` is Column value or a string, place it in a list for iteration - if isinstance(columns, (Column, str)): - columns = [columns] + columns = _resolve_columns(columns) # Determine brief to use (global or local) and transform any shorthands of `brief=` brief = self.brief if brief is None else _transform_auto_brief(brief=brief) @@ -12760,6 +12821,26 @@ def interrogate( tbl_type=tbl_type, ) + elif is_valid_agg(assertion_type): + agg, comp = resolve_agg_registries(assertion_type) + + # Produce a 1-column Narwhals DataFrame + # TODO: Should be able to take lazy too + vec: nw.DataFrame = nw.from_native(data_tbl_step).select(column) + real = agg(vec) + + target = value["value"] + tol = value["tol"] + lower_bound, upper_bound = _derive_bounds(target, tol) + + result_bool = comp(real, target - lower_bound, target + upper_bound) + + validation.all_passed = result_bool + validation.n = 1 + validation.n_passed = int(result_bool) + validation.n_failed = 1 - result_bool + + results_tbl = None else: raise ValueError( f"Unknown assertion type: {assertion_type}" From 8466a54cf4135d9d93f1669d806ef0a46a524a69 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:22:36 -0500 Subject: [PATCH 03/13] add aggregator factory --- pointblank/_agg.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 pointblank/_agg.py diff --git a/pointblank/_agg.py b/pointblank/_agg.py new file mode 100644 index 000000000..cbeac04e3 --- /dev/null +++ b/pointblank/_agg.py @@ -0,0 +1,88 @@ +from collections.abc import Callable +from typing import Any + +import narwhals as nw + +# TODO: Should take any frame type +Aggregator = Callable[[nw.DataFrame], Any] +Comparator = Callable[[Any, Any], bool] + +AGGREGATOR_REGISTRY: dict[str, Aggregator] = {} + +COMPARATOR_REGISTRY: dict[str, Comparator] = {} + + +def register(fn): + name: str = fn.__name__ + if name.startswith("comp_"): + COMPARATOR_REGISTRY[name.removeprefix("comp_")] = fn + elif name.startswith("agg_"): + AGGREGATOR_REGISTRY[name.removeprefix("agg_")] = fn + else: + raise NotImplementedError # pragma: no cover + return fn + + +## Aggregator Functions +@register +def agg_sum(column: nw.DataFrame) -> float: + return column.select(nw.all().sum()).item() + + +## Comparator functions +@register +def comp_eq(real: float, lower: float, upper: float) -> bool: + if lower == upper: + return bool(real == lower) + return _generic_between(real, lower, upper) + + +@register +def comp_gt(real: float, lower: float, upper: float) -> bool: + if lower == upper: + return bool(real > lower) + return bool(real > lower) + + +@register +def comp_ge(real: Any, lower: float, upper: float) -> bool: + if lower == upper: + return bool(real >= lower) + return bool(real >= lower) + + +def _generic_between(real: Any, lower: Any, upper: Any) -> bool: + """Call if comparator needs to check between two values.""" + return bool(lower <= real <= upper) + + +def resolve_agg_registries(name: str) -> tuple[Aggregator, Comparator]: + """Resolve the assertion name to a valid aggregator + + Args: + name (str): The name of the assertion. + + Returns: + tuple[Aggregator, Comparator]: _description_ + """ + name = name.removeprefix("col_") + agg_name, comp_name = name.split("_")[-2:] + + aggregator = AGGREGATOR_REGISTRY.get(agg_name) + comparator = COMPARATOR_REGISTRY.get(comp_name) + + if aggregator is None: + raise ValueError(f"Aggregator '{agg_name}' not found in registry.") + + if comparator is None: + raise ValueError(f"Comparator '{comp_name}' not found in registry.") + + return aggregator, comparator + + +def is_valid_agg(name: str) -> bool: + try: + resolve_agg_registries(name) + return True + except ValueError: + return False From b4b54716340ac227e3482a583479737db5546eb7 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:22:52 -0500 Subject: [PATCH 04/13] add tests to sum eq --- tests/test_agg.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/test_agg.py diff --git a/tests/test_agg.py b/tests/test_agg.py new file mode 100644 index 000000000..1c02eca4b --- /dev/null +++ b/tests/test_agg.py @@ -0,0 +1,26 @@ +import pytest + +from pointblank import Validate +import polars as pl + + +@pytest.mark.parametrize( + "tol", + [ + (0, 0), + (1, 1), + (100, 100), + 0, + ], +) +def test_sums(tol) -> None: + data = pl.DataFrame({"a": [1, 1, 1, None]}) + v = Validate(data).col_sum_eq("a", 3).interrogate() + + v.assert_below_threshold() + + v.get_tabular_report() + + +if __name__ == "__main__": + pytest.main([__file__]) From 367a6f44a880230f33122ad10fd705e7f84ca192 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:31:37 -0500 Subject: [PATCH 05/13] quick format pyproject --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1da906b57..4d48a489d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ docs = [ [dependency-groups] dev = [ "chatlas>=0.6.1", - "duckdb>=1.2.0,<1.3.3", # Pin to stable versions avoiding 1.4.0+ RecordBatchReader issues + "duckdb>=1.2.0,<1.3.3", # Pin to stable versions avoiding 1.4.0+ RecordBatchReader issues "griffe==0.38.1", "hypothesis>=6.129.2", "ibis-framework[duckdb,mysql,postgres,sqlite]>=9.5.0", From 3944b026a8713eacdb3dc72163026ab797e9173b Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:34:56 -0500 Subject: [PATCH 06/13] paramaterize tests --- tests/test_agg.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/tests/test_agg.py b/tests/test_agg.py index 1c02eca4b..c6001794e 100644 --- a/tests/test_agg.py +++ b/tests/test_agg.py @@ -4,6 +4,17 @@ import polars as pl +@pytest.fixture +def simple_pl() -> pl.DataFrame: + return pl.DataFrame( + { + "a": [1, 1, 1, None], + "b": [2, 2, 2, None], + "c": [3, 3, 3, None], + } + ) + + @pytest.mark.parametrize( "tol", [ @@ -13,14 +24,33 @@ 0, ], ) -def test_sums(tol) -> None: - data = pl.DataFrame({"a": [1, 1, 1, None]}) - v = Validate(data).col_sum_eq("a", 3).interrogate() +def test_sums_old(tol, simple_pl) -> None: + v = Validate(simple_pl).col_sum_eq("a", 3, tol=tol).interrogate() v.assert_below_threshold() v.get_tabular_report() +@pytest.mark.parametrize( + ("method", "vals"), + [ + ("col_sum_eq", (3, 6, 9)), + ("col_sum_gt", (2, 5, 8)), + ("col_sum_ge", (3, 6, 9)), + ("col_sum_lt", (4, 7, 10)), + ("col_sum_le", (3, 6, 9)), + ], +) +def test_sums(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): + v = Validate(simple_pl) + for col, val in zip(["a", "b", "c"], vals): + getattr(v, method)(col, val) + v = v.interrogate() + + v.assert_below_threshold() + v.get_tabular_report() + + if __name__ == "__main__": pytest.main([__file__]) From cb0fbb24347f32238004968c22ac98c01955f139 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 13:53:10 -0500 Subject: [PATCH 07/13] add sum comp methods --- pointblank/_agg.py | 14 ++++ pointblank/_constants.py | 50 ++++++++++++ pointblank/validate.py | 162 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 225 insertions(+), 1 deletion(-) diff --git a/pointblank/_agg.py b/pointblank/_agg.py index cbeac04e3..fc9b98b54 100644 --- a/pointblank/_agg.py +++ b/pointblank/_agg.py @@ -51,6 +51,20 @@ def comp_ge(real: Any, lower: float, upper: float) -> bool: return bool(real >= lower) +@register +def comp_lt(real: float, lower: float, upper: float) -> bool: + if lower == upper: + return bool(real < lower) + return bool(real < upper) + + +@register +def comp_le(real: float, lower: float, upper: float) -> bool: + if lower == upper: + return bool(real <= lower) + return bool(real <= upper) + + def _generic_between(real: Any, lower: Any, upper: Any) -> bool: """Call if comparator needs to check between two values.""" return bool(lower <= real <= upper) diff --git a/pointblank/_constants.py b/pointblank/_constants.py index 5879b4503..b7f89c81a 100644 --- a/pointblank/_constants.py +++ b/pointblank/_constants.py @@ -240,6 +240,16 @@ +""", + "col_sum_gt": """ + + col_vals_gt + + + + + + """, "col_vals_lt": """ @@ -250,6 +260,16 @@ +""", + "col_sum_lt": """ + + col_vals_lt + + + + + + """, "col_vals_eq": """ @@ -270,6 +290,16 @@ +""", + "col_sum_ne": """ + + col_vals_ne + + + + + + """, "col_vals_ge": """ @@ -280,6 +310,16 @@ +""", + "col_sum_ge": """ + + col_vals_ge + + + + + + """, "col_vals_le": """ @@ -290,6 +330,16 @@ +""", + "col_sum_le": """ + + col_vals_le + + + + + + """, "col_vals_between": """ diff --git a/pointblank/validate.py b/pointblank/validate.py index 596b70ab6..a6c6f7106 100644 --- a/pointblank/validate.py +++ b/pointblank/validate.py @@ -5038,6 +5038,166 @@ def col_sum_eq( return self + def col_sum_gt( + self, + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the values in a column sum to a value greater than some `value`. + + Args: + columns (_PBUnresolvedColumn): _description_ + value (float | Column): _description_ + tol (Tolerance, optional): _description_. Defaults to 0. + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + active (bool, optional): _description_. Defaults to True. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sum_ge( + self, + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the values in a column sum to a value greater or equal than some `value`. + + Args: + columns (_PBUnresolvedColumn): _description_ + value (float | Column): _description_ + tol (Tolerance, optional): _description_. Defaults to 0. + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + active (bool, optional): _description_. Defaults to True. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sum_lt( + self, + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the values in a column sum to a value less than some `value`. + + Args: + columns (_PBUnresolvedColumn): _description_ + value (float | Column): _description_ + tol (Tolerance, optional): _description_. Defaults to 0. + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + active (bool, optional): _description_. Defaults to True. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sum_le( + self, + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the values in a column sum to a value less than or equal to some `value`. + + Args: + columns (_PBUnresolvedColumn): _description_ + value (float | Column): _description_ + tol (Tolerance, optional): _description_. Defaults to 0. + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + active (bool, optional): _description_. Defaults to True. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + def col_vals_gt( self, columns: str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals, @@ -12340,7 +12500,7 @@ def interrogate( segment = validation.segments # Get compatible data types for this assertion type - assertion_method = ASSERTION_TYPE_METHOD_MAP[assertion_type] + assertion_method = ASSERTION_TYPE_METHOD_MAP.get(assertion_type, assertion_type) compatible_dtypes = COMPATIBLE_DTYPES.get(assertion_method, []) # Process the `brief` text for the validation step by including template variables to From 3c155ce07c1ae02b1de256806965ec25b56ea185 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 14:03:38 -0500 Subject: [PATCH 08/13] add average summary stat --- pointblank/_agg.py | 5 + pointblank/_constants.py | 65 +++++++++++++ pointblank/validate.py | 200 +++++++++++++++++++++++++++++++++++++++ tests/test_agg.py | 20 ++++ 4 files changed, 290 insertions(+) diff --git a/pointblank/_agg.py b/pointblank/_agg.py index fc9b98b54..64b1b6e6a 100644 --- a/pointblank/_agg.py +++ b/pointblank/_agg.py @@ -29,6 +29,11 @@ def agg_sum(column: nw.DataFrame) -> float: return column.select(nw.all().sum()).item() +@register +def agg_avg(column: nw.DataFrame) -> float: + return column.select(nw.all().mean()).item() + + ## Comparator functions @register def comp_eq(real: float, lower: float, upper: float) -> bool: diff --git a/pointblank/_constants.py b/pointblank/_constants.py index b7f89c81a..8fce6ae32 100644 --- a/pointblank/_constants.py +++ b/pointblank/_constants.py @@ -230,6 +230,16 @@ +""", + "col_avg_eq": """ + + col_vals_gt + + + + + + """, "col_vals_gt": """ @@ -251,6 +261,17 @@ """, + "col_avg_gt": """ + + col_vals_gt + + + + + + +""", + ## LT Icons "col_vals_lt": """ col_vals_lt @@ -271,6 +292,17 @@ """, + "col_avg_lt": """ + + col_vals_lt + + + + + + +""", + ## EQ Icons "col_vals_eq": """ col_vals_eq @@ -281,6 +313,7 @@ """, + ## NE Icons "col_vals_ne": """ col_vals_ne @@ -301,6 +334,17 @@ """, + "col_avg_ne": """ + + col_vals_ne + + + + + + +""", + ## GE Icons "col_vals_ge": """ col_vals_ge @@ -321,6 +365,17 @@ """, + "col_avg_ge": """ + + col_vals_ge + + + + + + +""", + ## LE Icons "col_vals_le": """ col_vals_le @@ -340,6 +395,16 @@ +""", + "col_avg_le": """ + + col_vals_le + + + + + + """, "col_vals_between": """ diff --git a/pointblank/validate.py b/pointblank/validate.py index a6c6f7106..29be8ed6c 100644 --- a/pointblank/validate.py +++ b/pointblank/validate.py @@ -4998,6 +4998,206 @@ def set_tbl( def _repr_html_(self) -> str: return self.get_tabular_report()._repr_html_() # pragma: no cover + def col_avg_eq( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the avg of the values in a column is equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_avg_ge( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the avg of the values in a column is greater or equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_avg_gt( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the avg of the values in a column greater than some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_avg_le( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the avg of the values in a column is less than or equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_avg_lt( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the avg of the values in a column is less than `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + def col_sum_eq( self, # TODO: Public type alias diff --git a/tests/test_agg.py b/tests/test_agg.py index c6001794e..a034ba8a5 100644 --- a/tests/test_agg.py +++ b/tests/test_agg.py @@ -52,5 +52,25 @@ def test_sums(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): v.get_tabular_report() +@pytest.mark.parametrize( + ("method", "vals"), + [ + ("col_avg_eq", (1, 2, 3)), + ("col_avg_gt", (0, 1, 2)), + ("col_avg_ge", (1, 2, 3)), + ("col_avg_lt", (2, 3, 4)), + ("col_avg_le", (1, 2, 3)), + ], +) +def test_avgs(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): + v = Validate(simple_pl) + for col, val in zip(["a", "b", "c"], vals): + getattr(v, method)(col, val) + v = v.interrogate() + + v.assert_below_threshold() + v.get_tabular_report() + + if __name__ == "__main__": pytest.main([__file__]) From aadfe5d3884d1df87a0054ca6c2937ac96eee3f7 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 14:10:13 -0500 Subject: [PATCH 09/13] consolidate tests --- tests/test_agg.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/tests/test_agg.py b/tests/test_agg.py index a034ba8a5..b3367a4b2 100644 --- a/tests/test_agg.py +++ b/tests/test_agg.py @@ -40,21 +40,6 @@ def test_sums_old(tol, simple_pl) -> None: ("col_sum_ge", (3, 6, 9)), ("col_sum_lt", (4, 7, 10)), ("col_sum_le", (3, 6, 9)), - ], -) -def test_sums(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): - v = Validate(simple_pl) - for col, val in zip(["a", "b", "c"], vals): - getattr(v, method)(col, val) - v = v.interrogate() - - v.assert_below_threshold() - v.get_tabular_report() - - -@pytest.mark.parametrize( - ("method", "vals"), - [ ("col_avg_eq", (1, 2, 3)), ("col_avg_gt", (0, 1, 2)), ("col_avg_ge", (1, 2, 3)), @@ -62,7 +47,7 @@ def test_sums(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): ("col_avg_le", (1, 2, 3)), ], ) -def test_avgs(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): +def test_aggs(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): v = Validate(simple_pl) for col, val in zip(["a", "b", "c"], vals): getattr(v, method)(col, val) From 029431df5b1fd978585d67f8fffc3b4443533655 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 14:45:49 -0500 Subject: [PATCH 10/13] fix columns --- pointblank/_utils.py | 7 +++---- pointblank/validate.py | 2 -- tests/test_validate.py | 3 +++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pointblank/_utils.py b/pointblank/_utils.py index 1247585df..fa3a20a8d 100644 --- a/pointblank/_utils.py +++ b/pointblank/_utils.py @@ -558,15 +558,14 @@ def _column_subset_test_prep( def _resolve_columns(columns: _PBUnresolvedColumn) -> _PBResolvedColumn: # If `columns` is a ColumnSelector or Narwhals selector, call `col()` on it to later # resolve the columns - # TODO: I feel like there are several functions that do basically this same thing; consolidate? if isinstance(columns, (ColumnSelector, nw.selectors.Selector)): - return [col(columns)] + columns = col(columns) # If `columns` is Column value or a string, place it in a list for iteration if isinstance(columns, (Column, str)): - return [columns] + columns = [columns] - raise NotImplementedError # pragma: no cover + return columns def _get_fn_name() -> str: diff --git a/pointblank/validate.py b/pointblank/validate.py index 29be8ed6c..76bf5fb54 100644 --- a/pointblank/validate.py +++ b/pointblank/validate.py @@ -5639,8 +5639,6 @@ def col_vals_gt( - Row 1: `c` is `1` and `b` is `2`. - Row 3: `c` is `2` and `b` is `2`. """ - columns = _resolve_columns(columns) - assertion_type = _get_fn_name() _check_column(column=columns) diff --git a/tests/test_validate.py b/tests/test_validate.py index 1ba6c748d..38ea6345c 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -20112,3 +20112,6 @@ def test_footer_controls_override_global_config(): report_incl_footer_notes=original_config.report_incl_footer_notes, preview_incl_header=original_config.preview_incl_header, ) + + +pytest.main([__file__, "-k", "test_col_selector_in_value_parameter_write_read_file"]) From aebc45cfc368a1e7b30ecfb8bb96cb28dff02262 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 14:47:50 -0500 Subject: [PATCH 11/13] chore remove pytest artifact --- tests/test_validate.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_validate.py b/tests/test_validate.py index 38ea6345c..1ba6c748d 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -20112,6 +20112,3 @@ def test_footer_controls_override_global_config(): report_incl_footer_notes=original_config.report_incl_footer_notes, preview_incl_header=original_config.preview_incl_header, ) - - -pytest.main([__file__, "-k", "test_col_selector_in_value_parameter_write_read_file"]) From 2f4f14714a0d9c753f6bb05ef73321d1f37f1dd8 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 15:24:04 -0500 Subject: [PATCH 12/13] add sd methods --- pointblank/_agg.py | 5 + pointblank/_constants.py | 73 ++++++++++++-- pointblank/validate.py | 200 +++++++++++++++++++++++++++++++++++++++ tests/test_agg.py | 10 ++ 4 files changed, 282 insertions(+), 6 deletions(-) diff --git a/pointblank/_agg.py b/pointblank/_agg.py index 64b1b6e6a..5d4cf1655 100644 --- a/pointblank/_agg.py +++ b/pointblank/_agg.py @@ -34,6 +34,11 @@ def agg_avg(column: nw.DataFrame) -> float: return column.select(nw.all().mean()).item() +@register +def agg_sd(column: nw.DataFrame) -> float: + return column.select(nw.all().std()).item() + + ## Comparator functions @register def comp_eq(real: float, lower: float, upper: float) -> bool: diff --git a/pointblank/_constants.py b/pointblank/_constants.py index 8fce6ae32..4678f7f50 100644 --- a/pointblank/_constants.py +++ b/pointblank/_constants.py @@ -221,6 +221,17 @@ CROSS_MARK_SPAN = "" SVG_ICONS_FOR_ASSERTION_TYPES = { + ## EQ Icons + "col_vals_eq": """ + + col_vals_eq + + + + + + +""", "col_sum_eq": """ col_vals_gt @@ -230,6 +241,16 @@ +""", + "col_sd_eq": """ + + col_vals_gt + + + + + + """, "col_avg_eq": """ @@ -241,6 +262,7 @@ """, + ## GT Icons "col_vals_gt": """ col_vals_gt @@ -260,6 +282,16 @@ +""", + "col_sd_gt": """ + + col_vals_gt + + + + + + """, "col_avg_gt": """ @@ -292,7 +324,7 @@ """, - "col_avg_lt": """ + "col_sd_lt": """ col_vals_lt @@ -302,14 +334,13 @@ """, - ## EQ Icons - "col_vals_eq": """ + "col_avg_lt": """ - col_vals_eq + col_vals_lt - + - + """, @@ -333,6 +364,16 @@ +""", + "col_sd_ne": """ + + col_vals_ne + + + + + + """, "col_avg_ne": """ @@ -364,6 +405,16 @@ +""", + "col_sd_ge": """ + + col_vals_ge + + + + + + """, "col_avg_ge": """ @@ -395,6 +446,16 @@ +""", + "col_sd_le": """ + + col_vals_le + + + + + + """, "col_avg_le": """ diff --git a/pointblank/validate.py b/pointblank/validate.py index 76bf5fb54..0af8caf1f 100644 --- a/pointblank/validate.py +++ b/pointblank/validate.py @@ -5038,6 +5038,206 @@ def col_avg_eq( return self + def col_sd_eq( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the standard deviation of the values in a column is equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sd_gt( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the standard deviation of the values in a column is greater than some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sd_ge( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the standard deviation of the values in a column is greater than or equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sd_lt( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the standard deviation of the values in a column is less than some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + def col_sd_le( + self, + # TODO: Public type alias + columns: _PBUnresolvedColumn, + value: float | Column, + tol: Tolerance = 0, + # TODO: type alias this, especially the tuple/dict parts + thresholds: float | bool | tuple | dict | Thresholds | None = None, + brief: str | bool = False, + actions: Actions | None = None, + active: bool = True, + ) -> Validate: + """Assert the standard deviation of the values in a column is less than or equal to some `value`. + + Args: + columns (str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals): _description_ + value (float | Column): _description_ + thresholds (float | bool | tuple | dict | Thresholds | None, optional): _description_. Defaults to None. + brief (str | bool, optional): _description_. Defaults to False. + actions (Actions | None, optional): _description_. Defaults to None. + + Returns: + Validate: _description_ + """ + for column in columns: # TODO: Not typed correctly + val_info = _ValidationInfo.from_agg_validator( + assertion_type=_get_fn_name(), + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + def col_avg_ge( self, # TODO: Public type alias diff --git a/tests/test_agg.py b/tests/test_agg.py index b3367a4b2..514c0453b 100644 --- a/tests/test_agg.py +++ b/tests/test_agg.py @@ -32,19 +32,29 @@ def test_sums_old(tol, simple_pl) -> None: v.get_tabular_report() +# TODO: Expand expression types +# TODO: Expand table types @pytest.mark.parametrize( ("method", "vals"), [ + # Sum -> 3, 6, 9 ("col_sum_eq", (3, 6, 9)), ("col_sum_gt", (2, 5, 8)), ("col_sum_ge", (3, 6, 9)), ("col_sum_lt", (4, 7, 10)), ("col_sum_le", (3, 6, 9)), + # Average -> 1, 2, 3 ("col_avg_eq", (1, 2, 3)), ("col_avg_gt", (0, 1, 2)), ("col_avg_ge", (1, 2, 3)), ("col_avg_lt", (2, 3, 4)), ("col_avg_le", (1, 2, 3)), + # Standard Deviation -> 0, 0, 0 + ("col_sd_eq", (0, 0, 0)), + ("col_sd_gt", (-1, -1, -1)), + ("col_sd_ge", (0, 0, 0)), + ("col_sd_lt", (1, 1, 1)), + ("col_sd_le", (0, 0, 0)), ], ) def test_aggs(simple_pl: pl.DataFrame, method: str, vals: tuple[int, int, int]): From 9f6474f4870af5cf47fd29edefe7ddd21045e326 Mon Sep 17 00:00:00 2001 From: Tyler Riccio Date: Mon, 1 Dec 2025 15:44:38 -0500 Subject: [PATCH 13/13] register validation steps instead of manual --- pointblank/validate.py | 299 ++++++++++------------------------------- 1 file changed, 74 insertions(+), 225 deletions(-) diff --git a/pointblank/validate.py b/pointblank/validate.py index 0af8caf1f..874ee02ad 100644 --- a/pointblank/validate.py +++ b/pointblank/validate.py @@ -136,6 +136,50 @@ "get_validation_summary", ] +from functools import wraps +from typing import Callable, ParamSpec, TypeVar + +P = ParamSpec("P") +R = TypeVar("R") + + +def _register_agg_validations(func: Callable[P, R]) -> Callable[P, R]: + """ + Decorator that handles the standard validation pattern for aggregate validators. + + The decorated function should just be a stub that defines the signature. + """ + + @wraps(func) + def wrapper( + self: Validate, + columns, + value, + tol=0, + thresholds=None, + brief=False, + actions=None, + active=True, + ): + for column in columns: + val_info = _ValidationInfo.from_agg_validator( + assertion_type=func.__name__, # Use the function name + columns=column, + value=value, + tol=tol, + thresholds=self.thresholds if thresholds is None else thresholds, + actions=self.actions if actions is None else actions, + brief=self.brief if brief is None else brief, + active=active, + ) + + self._add_validation(validation_info=val_info) + + return self + + return wrapper + + # Create a thread-local storage for the metadata _action_context = threading.local() @@ -4998,6 +5042,7 @@ def set_tbl( def _repr_html_(self) -> str: return self.get_tabular_report()._repr_html_() # pragma: no cover + @_register_agg_validations def col_avg_eq( self, # TODO: Public type alias @@ -5022,22 +5067,9 @@ def col_avg_eq( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sd_eq( self, # TODO: Public type alias @@ -5062,22 +5094,9 @@ def col_sd_eq( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sd_gt( self, # TODO: Public type alias @@ -5102,22 +5121,9 @@ def col_sd_gt( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sd_ge( self, # TODO: Public type alias @@ -5142,22 +5148,9 @@ def col_sd_ge( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sd_lt( self, # TODO: Public type alias @@ -5182,22 +5175,9 @@ def col_sd_lt( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sd_le( self, # TODO: Public type alias @@ -5222,22 +5202,9 @@ def col_sd_le( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_avg_ge( self, # TODO: Public type alias @@ -5262,22 +5229,9 @@ def col_avg_ge( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_avg_gt( self, # TODO: Public type alias @@ -5302,22 +5256,9 @@ def col_avg_gt( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_avg_le( self, # TODO: Public type alias @@ -5342,22 +5283,9 @@ def col_avg_le( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_avg_lt( self, # TODO: Public type alias @@ -5382,22 +5310,9 @@ def col_avg_lt( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sum_eq( self, # TODO: Public type alias @@ -5422,22 +5337,9 @@ def col_sum_eq( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sum_gt( self, columns: _PBUnresolvedColumn, @@ -5462,22 +5364,9 @@ def col_sum_gt( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sum_ge( self, columns: _PBUnresolvedColumn, @@ -5502,22 +5391,9 @@ def col_sum_ge( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sum_lt( self, columns: _PBUnresolvedColumn, @@ -5542,22 +5418,9 @@ def col_sum_lt( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass + @_register_agg_validations def col_sum_le( self, columns: _PBUnresolvedColumn, @@ -5582,21 +5445,7 @@ def col_sum_le( Returns: Validate: _description_ """ - for column in columns: # TODO: Not typed correctly - val_info = _ValidationInfo.from_agg_validator( - assertion_type=_get_fn_name(), - columns=column, - value=value, - tol=tol, - thresholds=self.thresholds if thresholds is None else thresholds, - actions=self.actions if actions is None else actions, - brief=self.brief if brief is None else brief, - active=active, - ) - - self._add_validation(validation_info=val_info) - - return self + pass def col_vals_gt( self,