Validate.col_vals_between

Validate.col_vals_between(
    columns,
    left,
    right,
    inclusive=(True, True),
    na_pass=False,
    pre=None,
    thresholds=None,
    actions=None,
    brief=None,
    active=True,
)

Do column data lie between two specified values or data in other columns?

The col_vals_between() validation method checks whether column values in a table fall within a range. The range is specified with three arguments: left=, right=, and inclusive=. The left= and right= values specify the lower and upper bounds. These bounds can be specified as literal values or as column names provided within col(). The validation will operate over the number of test units that is equal to the number of rows in the table (determined after any pre= mutation has been applied).

Parameters

columns : str | list[str] | Column | ColumnSelector | ColumnSelectorNarwhals

A single column or a list of columns to validate. Can also use col() with column selectors to specify one or more columns. If multiple columns are supplied or resolved, there will be a separate validation step generated for each column.

left : float | int | Column

The lower bound of the range. This can be a single value or a single column name given in col(). The latter option allows for a column-to-column comparison for this bound. See the What Can Be Used in left= and right=? section for details on this.

right : float | int | Column

The upper bound of the range. This can be a single value or a single column name given in col(). The latter option allows for a column-to-column comparison for this bound. See the What Can Be Used in left= and right=? section for details on this.

inclusive : tuple[bool, bool] = (True, True)

A tuple of two boolean values indicating whether the comparison should be inclusive. The position of the boolean values correspond to the left= and right= values, respectively. By default, both values are True.

na_pass : bool = False

Should any encountered None, NA, or Null values be considered as passing test units? By default, this is False. Set to True to pass test units with missing values.

pre : Callable | None = None

A optional preprocessing function or lambda to apply to the data table during interrogation. This function should take a table as input and return a modified table. Have a look at the Preprocessing section for more information on how to use this argument.

thresholds : int | float | bool | tuple | dict | Thresholds = None

Set threshold failure levels for reporting and reacting to exceedences of the levels. The thresholds are set at the step level and will override any global thresholds set in Validate(thresholds=...). The default is None, which means that no thresholds will be set locally and global thresholds (if any) will take effect. Look at the Thresholds section for information on how to set threshold levels.

actions : Actions | None = None

Optional actions to take when the validation step(s) meets or exceeds any set threshold levels. If provided, the Actions class should be used to define the actions.

brief : str | bool | None = None

An optional brief description of the validation step that will be displayed in the reporting table. You can use the templating elements like "{step}" to insert the step number, or "{auto}" to include an automatically generated brief. If True the entire brief will be automatically generated. If None (the default) then there won’t be a brief.

active : bool = True

A boolean value indicating whether the validation step should be active. Using False will make the validation step inactive (still reporting its presence and keeping indexes for the steps unchanged).

Returns

: Validate

The Validate object with the added validation step.

What Can Be Used in left= and right=?

The left= and right= arguments both allow for a variety of input types. The most common are:

  • a single numeric value
  • a single date or datetime value
  • A col() object that represents a column in the target table

When supplying a number as the basis of comparison, keep in mind that all resolved columns must also be numeric. Should you have columns that are of the date or datetime types, you can supply a date or datetime value within left= and right=. There is flexibility in how you provide the date or datetime values for the bounds; they can be:

  • string-based dates or datetimes (e.g., "2023-10-01", "2023-10-01 13:45:30", etc.)
  • date or datetime objects using the datetime module (e.g., datetime.date(2023, 10, 1), datetime.datetime(2023, 10, 1, 13, 45, 30), etc.)

Finally, when supplying a column name in either left= or right= (or both), it must be specified within col(). This facilitates column-to-column comparisons and, crucially, the columns being compared to either/both of the bounds must be of the same type as the column data (e.g., all numeric, all dates, etc.).

Preprocessing

The pre= argument allows for a preprocessing function or lambda to be applied to the data table during interrogation. This function should take a table as input and return a modified table. This is useful for performing any necessary transformations or filtering on the data before the validation step is applied.

The preprocessing function can be any callable that takes a table as input and returns a modified table. For example, you could use a lambda function to filter the table based on certain criteria or to apply a transformation to the data. Note that you can refer to columns via columns= and left=col(...)/right=col(...) that are expected to be present in the transformed table, but may not exist in the table before preprocessing. Regarding the lifetime of the transformed table, it only exists during the validation step and is not stored in the Validate object or used in subsequent validation steps.

Thresholds

The thresholds= parameter is used to set the failure-condition levels for the validation step. If they are set here at the step level, these thresholds will override any thresholds set at the global level in Validate(thresholds=...).

There are three threshold levels: ‘warning’, ‘error’, and ‘critical’. The threshold values can either be set as a proportion failing of all test units (a value between 0 to 1), or, the absolute number of failing test units (as integer that’s 1 or greater).

Thresholds can be defined using one of these input schemes:

  1. use the Thresholds class (the most direct way to create thresholds)
  2. provide a tuple of 1-3 values, where position 0 is the ‘warning’ level, position 1 is the ‘error’ level, and position 2 is the ‘critical’ level
  3. create a dictionary of 1-3 value entries; the valid keys: are ‘warning’, ‘error’, and ‘critical’
  4. a single integer/float value denoting absolute number or fraction of failing test units for the ‘warning’ level only

If the number of failing test units exceeds set thresholds, the validation step will be marked as ‘warning’, ‘error’, or ‘critical’. All of the threshold levels don’t need to be set, you’re free to set any combination of them.

Aside from reporting failure conditions, thresholds can be used to determine the actions to take for each level of failure (using the actions= parameter).

Examples

For the examples here, we’ll use a simple Polars DataFrame with three numeric columns (a, b, and c). The table is shown below:

import pointblank as pb
import polars as pl

tbl = pl.DataFrame(
    {
        "a": [2, 3, 2, 4, 3, 4],
        "b": [5, 6, 1, 6, 8, 5],
        "c": [9, 8, 8, 7, 7, 8],
    }
)

pb.preview(tbl)
a
Int64
b
Int64
c
Int64
1 2 5 9
2 3 6 8
3 2 1 8
4 4 6 7
5 3 8 7
6 4 5 8

Let’s validate that values in column a are all between the fixed boundary values of 1 and 5. We’ll determine if this validation had any failing test units (there are six test units, one for each row).

validation = (
    pb.Validate(data=tbl)
    .col_vals_between(columns="a", left=1, right=5)
    .interrogate()
)

validation
STEP COLUMNS VALUES TBL EVAL UNITS PASS FAIL W E C EXT
#4CA64C 1
col_vals_between
col_vals_between()
a [1, 5] 6 6
1.00
0
0.00

Printing the validation object shows the validation table in an HTML viewing environment. The validation table shows the single entry that corresponds to the validation step created by using col_vals_between(). All test units passed, and there are no failing test units.

Aside from checking a column against two literal values representing the lower and upper bounds, we can also provide column names to the left= and/or right= arguments (by using the helper function col(). In this way, we can perform three additional comparison types:

  1. left=column, right=column
  2. left=literal, right=column
  3. left=column, right=literal

For the next example, we’ll use col_vals_between() to check whether the values in column b are between than corresponding values in columns a (lower bound) and c (upper bound).

validation = (
    pb.Validate(data=tbl)
    .col_vals_between(columns="b", left=pb.col("a"), right=pb.col("c"))
    .interrogate()
)

validation
STEP COLUMNS VALUES TBL EVAL UNITS PASS FAIL W E C EXT
#4CA64C66 1
col_vals_between
col_vals_between()
b [a, c] 6 4
0.67
2
0.33

The validation table reports two failing test units. The specific failing cases are:

  • Row 2: b is 1 but the bounds are 2 (a) and 8 (c).
  • Row 4: b is 8 but the bounds are 3 (a) and 7 (c).