Skip to content

Commit ed8dcb2

Browse files
authored
Merge pull request #184 from posit-dev/feat-has-threshold-exceedances
feat: add `above_threshold()`
2 parents aff980a + 54d72e7 commit ed8dcb2

File tree

5 files changed

+406
-10
lines changed

5 files changed

+406
-10
lines changed

docs/_quarto.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ quartodoc:
178178
- name: Validate.all_passed
179179
- name: Validate.assert_passing
180180
- name: Validate.assert_below_threshold
181+
- name: Validate.above_threshold
181182
- name: Validate.n
182183
- name: Validate.n_passed
183184
- name: Validate.n_failed

pointblank/_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ def _get_api_text() -> str:
515515
"Validate.all_passed",
516516
"Validate.assert_passing",
517517
"Validate.assert_below_threshold",
518+
"Validate.above_threshold",
518519
"Validate.n",
519520
"Validate.n_passed",
520521
"Validate.n_failed",

pointblank/data/api-docs.txt

Lines changed: 109 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7621,7 +7621,7 @@ assert_passing(self) -> 'None'
76217621
```
76227622

76237623

7624-
assert_below_threshold(self, level: 'str' = 'warning', i: 'int' = None, message: 'str' = None) -> 'None'
7624+
assert_below_threshold(self, level: 'str' = 'warning', i: 'int | None' = None, message: 'str | None' = None) -> 'None'
76257625

76267626
Raise an `AssertionError` if validation steps exceed a specified threshold level.
76277627

@@ -7716,15 +7716,119 @@ assert_below_threshold(self, level: 'str' = 'warning', i: 'int' = None, message:
77167716

77177717
See Also
77187718
--------
7719-
- [`warning()`](`pointblank.Validate.warning`): Get the 'warning' status for each validation
7719+
- [`warning()`](`pointblank.Validate.warning`): get the 'warning' status for each validation
77207720
step
7721-
- [`error()`](`pointblank.Validate.error`): Get the 'error' status for each validation step
7722-
- [`critical()`](`pointblank.Validate.critical`): Get the 'critical' status for each
7721+
- [`error()`](`pointblank.Validate.error`): get the 'error' status for each validation step
7722+
- [`critical()`](`pointblank.Validate.critical`): get the 'critical' status for each
77237723
validation step
7724-
- [`assert_passing()`](`pointblank.Validate.assert_passing`): Assert all validations pass
7724+
- [`assert_passing()`](`pointblank.Validate.assert_passing`): assert all validations pass
77257725
completely
77267726

77277727

7728+
above_threshold(self, level: 'str' = 'warning', i: 'int | None' = None) -> 'bool'
7729+
7730+
Check if any validation steps exceed a specified threshold level.
7731+
7732+
The `above_threshold()` method checks whether validation steps exceed a given threshold
7733+
level. This provides a non-exception-based alternative to
7734+
[`assert_below_threshold()`](`pointblank.Validate.assert_below_threshold`) for conditional
7735+
workflow control based on validation results.
7736+
7737+
This method is useful in scenarios where you want to check if any validation steps failed
7738+
beyond a certain threshold without raising an exception, allowing for more flexible
7739+
programmatic responses to validation issues.
7740+
7741+
Parameters
7742+
----------
7743+
level
7744+
The threshold level to check against. Valid options are: `"warning"` (the least severe
7745+
threshold level), `"error"` (the middle severity threshold level), and `"critical"` (the
7746+
most severe threshold level). The default is `"warning"`.
7747+
i
7748+
Specific validation step number(s) to check. If a single integer, checks only that step.
7749+
If a list of integers, checks all specified steps. If `None` (the default), checks all
7750+
validation steps. Step numbers are 1-based (first step is `1`, not `0`).
7751+
7752+
Returns
7753+
-------
7754+
bool
7755+
`True` if any of the specified validation steps exceed the given threshold level,
7756+
`False` otherwise.
7757+
7758+
Raises
7759+
------
7760+
ValueError
7761+
If an invalid threshold level is provided.
7762+
7763+
Examples
7764+
--------
7765+
Below are some examples of how to use the `above_threshold()` method. First, we'll create a
7766+
simple Polars DataFrame with a single column (`values`).
7767+
7768+
Then a validation plan will be created with thresholds (`warning=0.1`, `error=0.2`,
7769+
`critical=0.3`). After interrogating, we display the validation report table:
7770+
7771+
```python
7772+
import pointblank as pb
7773+
7774+
validation = (
7775+
pb.Validate(data=tbl, thresholds=(0.1, 0.2, 0.3))
7776+
.col_vals_gt(columns="values", value=0)
7777+
.col_vals_lt(columns="values", value=10)
7778+
.col_vals_between(columns="values", left=0, right=5)
7779+
.interrogate()
7780+
)
7781+
7782+
validation
7783+
```
7784+
7785+
Let's check if any steps exceed the 'warning' threshold with the `above_threshold()` method.
7786+
A message will be printed if that's the case:
7787+
7788+
```python
7789+
if validation.above_threshold(level="warning"):
7790+
print("Some steps have exceeded the warning threshold")
7791+
```
7792+
7793+
Check if only steps 2 and 3 exceed the 'error' threshold through use of the `i=` argument:
7794+
7795+
```python
7796+
if validation.above_threshold(level="error", i=[2, 3]):
7797+
print("Steps 2 and/or 3 have exceeded the error threshold")
7798+
```
7799+
7800+
You can use this in a workflow to conditionally trigger processes. Here's a snippet of how
7801+
you might use this in a function:
7802+
7803+
```python
7804+
def process_data(validation_obj):
7805+
# Only continue processing if validation passes critical thresholds
7806+
if not validation_obj.above_threshold(level="critical"):
7807+
# Continue with processing
7808+
print("Data meets critical quality thresholds, proceeding...")
7809+
return True
7810+
else:
7811+
# Log failure and stop processing
7812+
print("Data fails critical quality checks, aborting...")
7813+
return False
7814+
```
7815+
7816+
Note that this is just a suggestion for how to implement conditional workflow processes. You
7817+
should adapt this pattern to your specific requirements, which might include different
7818+
threshold levels, custom logging mechanisms, or integration with your organization's data
7819+
pipelines and notification systems.
7820+
7821+
See Also
7822+
--------
7823+
- [`assert_below_threshold()`](`pointblank.Validate.assert_below_threshold`): a similar
7824+
method that raises an exception if thresholds are exceeded
7825+
- [`warning()`](`pointblank.Validate.warning`): get the 'warning' status for each validation
7826+
step
7827+
- [`error()`](`pointblank.Validate.error`): get the 'error' status for each validation step
7828+
- [`critical()`](`pointblank.Validate.critical`): get the 'critical' status for each
7829+
validation step
7830+
7831+
77287832
n(self, i: 'int | list[int] | None' = None, scalar: 'bool' = False) -> 'dict[int, int] | int'
77297833

77307834
Provides a dictionary of the number of test units for each validation step.

pointblank/validate.py

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8831,7 +8831,7 @@ def assert_passing(self) -> None:
88318831
raise AssertionError(msg)
88328832

88338833
def assert_below_threshold(
8834-
self, level: str = "warning", i: int = None, message: str = None
8834+
self, level: str = "warning", i: int | None = None, message: str | None = None
88358835
) -> None:
88368836
"""
88378837
Raise an `AssertionError` if validation steps exceed a specified threshold level.
@@ -8940,12 +8940,12 @@ def assert_below_threshold(
89408940

89418941
See Also
89428942
--------
8943-
- [`warning()`](`pointblank.Validate.warning`): Get the 'warning' status for each validation
8943+
- [`warning()`](`pointblank.Validate.warning`): get the 'warning' status for each validation
89448944
step
8945-
- [`error()`](`pointblank.Validate.error`): Get the 'error' status for each validation step
8946-
- [`critical()`](`pointblank.Validate.critical`): Get the 'critical' status for each
8945+
- [`error()`](`pointblank.Validate.error`): get the 'error' status for each validation step
8946+
- [`critical()`](`pointblank.Validate.critical`): get the 'critical' status for each
89478947
validation step
8948-
- [`assert_passing()`](`pointblank.Validate.assert_passing`): Assert all validations pass
8948+
- [`assert_passing()`](`pointblank.Validate.assert_passing`): assert all validations pass
89498949
completely
89508950
"""
89518951
# Check if validation has been interrogated
@@ -8991,6 +8991,145 @@ def assert_below_threshold(
89918991
)
89928992
raise AssertionError(msg)
89938993

8994+
def above_threshold(self, level: str = "warning", i: int | None = None) -> bool:
8995+
"""
8996+
Check if any validation steps exceed a specified threshold level.
8997+
8998+
The `above_threshold()` method checks whether validation steps exceed a given threshold
8999+
level. This provides a non-exception-based alternative to
9000+
[`assert_below_threshold()`](`pointblank.Validate.assert_below_threshold`) for conditional
9001+
workflow control based on validation results.
9002+
9003+
This method is useful in scenarios where you want to check if any validation steps failed
9004+
beyond a certain threshold without raising an exception, allowing for more flexible
9005+
programmatic responses to validation issues.
9006+
9007+
Parameters
9008+
----------
9009+
level
9010+
The threshold level to check against. Valid options are: `"warning"` (the least severe
9011+
threshold level), `"error"` (the middle severity threshold level), and `"critical"` (the
9012+
most severe threshold level). The default is `"warning"`.
9013+
i
9014+
Specific validation step number(s) to check. If a single integer, checks only that step.
9015+
If a list of integers, checks all specified steps. If `None` (the default), checks all
9016+
validation steps. Step numbers are 1-based (first step is `1`, not `0`).
9017+
9018+
Returns
9019+
-------
9020+
bool
9021+
`True` if any of the specified validation steps exceed the given threshold level,
9022+
`False` otherwise.
9023+
9024+
Raises
9025+
------
9026+
ValueError
9027+
If an invalid threshold level is provided.
9028+
9029+
Examples
9030+
--------
9031+
```{python}
9032+
#| echo: false
9033+
#| output: false
9034+
import pointblank as pb
9035+
pb.config(report_incl_header=False, report_incl_footer=False, preview_incl_header=False)
9036+
```
9037+
Below are some examples of how to use the `above_threshold()` method. First, we'll create a
9038+
simple Polars DataFrame with a single column (`values`).
9039+
9040+
```{python}
9041+
import polars as pl
9042+
9043+
tbl = pl.DataFrame({
9044+
"values": [1, 2, 3, 4, 5, 0, -1]
9045+
})
9046+
```
9047+
9048+
Then a validation plan will be created with thresholds (`warning=0.1`, `error=0.2`,
9049+
`critical=0.3`). After interrogating, we display the validation report table:
9050+
9051+
```{python}
9052+
import pointblank as pb
9053+
9054+
validation = (
9055+
pb.Validate(data=tbl, thresholds=(0.1, 0.2, 0.3))
9056+
.col_vals_gt(columns="values", value=0)
9057+
.col_vals_lt(columns="values", value=10)
9058+
.col_vals_between(columns="values", left=0, right=5)
9059+
.interrogate()
9060+
)
9061+
9062+
validation
9063+
```
9064+
9065+
Let's check if any steps exceed the 'warning' threshold with the `above_threshold()` method.
9066+
A message will be printed if that's the case:
9067+
9068+
```{python}
9069+
if validation.above_threshold(level="warning"):
9070+
print("Some steps have exceeded the warning threshold")
9071+
```
9072+
9073+
Check if only steps 2 and 3 exceed the 'error' threshold through use of the `i=` argument:
9074+
9075+
```{python}
9076+
if validation.above_threshold(level="error", i=[2, 3]):
9077+
print("Steps 2 and/or 3 have exceeded the error threshold")
9078+
```
9079+
9080+
You can use this in a workflow to conditionally trigger processes. Here's a snippet of how
9081+
you might use this in a function:
9082+
9083+
```python
9084+
def process_data(validation_obj):
9085+
# Only continue processing if validation passes critical thresholds
9086+
if not validation_obj.above_threshold(level="critical"):
9087+
# Continue with processing
9088+
print("Data meets critical quality thresholds, proceeding...")
9089+
return True
9090+
else:
9091+
# Log failure and stop processing
9092+
print("Data fails critical quality checks, aborting...")
9093+
return False
9094+
```
9095+
9096+
Note that this is just a suggestion for how to implement conditional workflow processes. You
9097+
should adapt this pattern to your specific requirements, which might include different
9098+
threshold levels, custom logging mechanisms, or integration with your organization's data
9099+
pipelines and notification systems.
9100+
9101+
See Also
9102+
--------
9103+
- [`assert_below_threshold()`](`pointblank.Validate.assert_below_threshold`): a similar
9104+
method that raises an exception if thresholds are exceeded
9105+
- [`warning()`](`pointblank.Validate.warning`): get the 'warning' status for each validation
9106+
step
9107+
- [`error()`](`pointblank.Validate.error`): get the 'error' status for each validation step
9108+
- [`critical()`](`pointblank.Validate.critical`): get the 'critical' status for each
9109+
validation step
9110+
"""
9111+
# Ensure validation has been run
9112+
if not hasattr(self, "time_start") or self.time_start is None:
9113+
return False
9114+
9115+
# Validate the level parameter
9116+
level = level.lower()
9117+
if level not in ["warning", "error", "critical"]:
9118+
raise ValueError(
9119+
f"Invalid threshold level: {level}. Must be one of 'warning', 'error', or 'critical'."
9120+
)
9121+
9122+
# Get the threshold status using the appropriate method
9123+
if level == "warning":
9124+
status = self.warning(i=i)
9125+
elif level == "error":
9126+
status = self.error(i=i)
9127+
elif level == "critical":
9128+
status = self.critical(i=i)
9129+
9130+
# Return True if any steps exceeded the threshold
9131+
return any(status.values())
9132+
89949133
def n(self, i: int | list[int] | None = None, scalar: bool = False) -> dict[int, int] | int:
89959134
"""
89969135
Provides a dictionary of the number of test units for each validation step.

0 commit comments

Comments
 (0)