Skip to content

Added tolerance to equality and non-equality check functions#1011

Merged
mwojtyczka merged 14 commits into
databrickslabs:mainfrom
dwanneruchi:aggr-rounding-issue-1004
Feb 5, 2026
Merged

Added tolerance to equality and non-equality check functions#1011
mwojtyczka merged 14 commits into
databrickslabs:mainfrom
dwanneruchi:aggr-rounding-issue-1004

Conversation

@dwanneruchi

@dwanneruchi dwanneruchi commented Jan 23, 2026

Copy link
Copy Markdown
Contributor

Changes

Linked issues

Resolves #1004

Tests

  • manually tested
  • added unit tests
  • added integration tests
  • added end-to-end tests
  • added performance tests

Manul Test Examples:

Note: Below is the proper testing version:

%pip install git+https://github.com/dwanneruchi/dqx.git@aggr-rounding-issue-1004

dbutils.library.restartPython()

import pyspark.sql.functions as F

from databricks.labs.dqx.engine import DQEngine
from databricks.labs.dqx.check_funcs import get_normalized_column_and_expr, get_limit_expr, _match_values_with_tolerance, make_condition, is_equal_to, is_not_equal_to
from databricks.labs.dqx.rule import DQRowRule
from databricks.sdk import WorkspaceClient

dq_engine = DQEngine(WorkspaceClient())

is_equal_to:

Presently we will see a violation from C & D since they don't equal 200

data = [
    ("A", 200),
    ("B",200),
    ("C", 199),  # VIOLATION!
    ("D", 198),  # VIOLATION!
]

df = spark.createDataFrame(
    data,
    schema=[
        "id",
        "sales",
    ],
)

# List of checks
dqx_check = [
  DQRowRule(
      name="equal_200",
      criticality="warn",
      check_func=is_equal_to,
      column="sales",
      check_func_kwargs = {"value": 200}
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)

quarantine_df.display()
Screenshot 2026-01-23 at 3 31 09 PM

But we can use rel_tolerance and abs_tolerance to resolve:

# List of checks
dqx_check = [
  DQRowRule(
      name="equal_within_1_to_200",
      criticality="error",
      check_func=is_equal_to,
      column="sales",
      check_func_kwargs = {"value": 200, "abs_tolerance": 1.0, "rel_tolerance": 0.01 }
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)

quarantine_df.display()

This yields 0 violations, as expected:

  • C -> is within 1 (abs_tolerance) and within 1% (rel_tolerance)
  • D -> is NOT within 1 (abs_tolerance) but is exactly within 1% (rel_tolerance) since max(a,b) == 200

is_not_equal_to:

# List of checks - C will raise warning without tolerance
data = [
    ("A", 200.0),
    ("B",200.0),
    ("C", 199.0), 
]

df = spark.createDataFrame(
    data,
    schema=[
        "id",
        "sales",
    ],
)

# List of checks
dqx_check = [
  DQRowRule(
      name="nequal_200",
      criticality="warn",
      check_func=is_not_equal_to,
      column="sales",
      check_func_kwargs = {"value": 199}
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)

And adding abs_tolerance will cause all to raise warnings:

# List of checks
dqx_check = [
  DQRowRule(
      name="nequal_200",
      criticality="warn",
      check_func=is_not_equal_to,
      column="sales",
      check_func_kwargs = {"value": 199, "abs_tolerance": 1.0}
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)

is_aggr_equal

We see id2 == "B" will violate due to avg == 125.5

from databricks.labs.dqx.check_funcs import is_aggr_equal
from databricks.labs.dqx.rule import DQDatasetRule

data = [
    ("A", "A", 100),
    ("B", "A", 150),
    ("E", "A", None),
    ("C", "B", 100), # VIOLATION
    ("D", "B", 151), # VIOLATION
]

df = spark.createDataFrame(
    data,
    schema=[
        "id",
        "id2",
        "sales",
    ],
)

# List of checks
dqx_check = [
  DQDatasetRule(  
    name="agg_avg",
    criticality="warn",
    check_func=is_aggr_equal,
    column = "sales",
    check_func_kwargs={
      "group_by": ['id2'],
      "aggr_type": "avg",
      "limit": 125}
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)
quarantine_df.display()

Adding an abs_tolerance removes the violation:

  • Note that null is handled correctly
# List of checks
dqx_check = [
  DQDatasetRule(  
    name="agg_avg",
    criticality="warn",
    check_func=is_aggr_equal,
    column = "sales",
    check_func_kwargs={
      "group_by": ['id2'],
      "aggr_type": "avg",
      "limit": 125,
      "abs_tolerance": 0.5
    }
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)
quarantine_df.display()

is_aggr_not_equal

Same example, except the negative - now id2 == A` will fail:

# List of checks
dqx_check = [
  DQDatasetRule(  
    name="agg_avg_neq",
    criticality="warn",
    check_func=is_aggr_not_equal,
    column = "sales",
    check_func_kwargs={
      "group_by": ['id2'],
      "aggr_type": "avg",
      "limit": 125}
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)

Adding abs_tolerance ==1.0 will cause all to be violations as expected:

# List of checks
dqx_check = [
  DQDatasetRule(  
    name="agg_avg_neq",
    criticality="warn",
    check_func=is_aggr_not_equal,
    column = "sales",
    check_func_kwargs={
      "group_by": ['id2'],
      "aggr_type": "avg",
      "limit": 125,
      "abs_tolerance": 1.0}
  ),
]

valid_df, quarantine_df = dq_engine.apply_checks_and_split(df, dqx_check)

quarantine_df.display()

@dwanneruchi dwanneruchi requested a review from a team as a code owner January 23, 2026 20:44
@dwanneruchi dwanneruchi requested review from gergo-databricks and removed request for a team January 23, 2026 20:44
@dwanneruchi dwanneruchi marked this pull request as draft January 23, 2026 20:44
@mwojtyczka mwojtyczka requested a review from Copilot January 24, 2026 09:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds tolerance parameters (abs_tolerance and rel_tolerance) to equality comparison functions, enabling flexible numeric comparisons that account for floating-point precision issues and scale variations. This addresses issue #1004 related to aggregation rounding.

Changes:

  • Added optional abs_tolerance and rel_tolerance parameters to is_equal_to, is_not_equal_to, is_aggr_equal, and is_aggr_not_equal functions
  • Implemented _match_values_with_tolerance helper function for tolerance-based numeric comparisons
  • Created _build_aggregate_check_metadata helper to extract metadata building logic from _is_aggr_compare

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py
@github-actions

Copy link
Copy Markdown
Contributor

All commits in PR should be signed ('git commit -S ...'). See https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits

@dwanneruchi

Copy link
Copy Markdown
Contributor Author

Still going to add tests in tests/integration/test_dataset_checks.py for is_equal_to and is_not_equal_to with new tolerance args, then this should be ready for a review.

@dwanneruchi

dwanneruchi commented Jan 25, 2026

Copy link
Copy Markdown
Contributor Author

A few notes for reviewers:

hatch run pytest tests/integration/test_row_checks.py -v -s
hatch run pytest tests/integration/test_dataset_checks.py -v -s

Otherwise ready for review, open to any feedback as this is my first contribution to dqx!

@dwanneruchi dwanneruchi force-pushed the aggr-rounding-issue-1004 branch from a58e647 to 405d360 Compare January 25, 2026 22:31
@dwanneruchi dwanneruchi marked this pull request as ready for review January 25, 2026 22:32
@davidwanner-8451

Copy link
Copy Markdown

This should be ready for review, anything pausing that @mwojtyczka and / or @ghanse ?

@mwojtyczka mwojtyczka changed the title feat: add tolerance to eq and neq func Added tolerance to equality and non-equality check functions Feb 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/integration/test_row_checks.py Outdated
Comment thread tests/integration/test_row_checks.py Outdated
Comment thread tests/integration/test_row_checks.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated
Comment thread src/databricks/labs/dqx/check_funcs.py Outdated

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implementation LGTM. I still need to check tests.

Comment thread tests/integration/test_row_checks.py Outdated

@mwojtyczka mwojtyczka left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, added missing docs

@mwojtyczka mwojtyczka merged commit 090fedc into databrickslabs:main Feb 5, 2026
14 checks passed
mwojtyczka added a commit that referenced this pull request Feb 9, 2026
* New DQX Data Quality Dashboard ([#1019](#1019)). The data quality dashboard has been significantly enhanced to provide a centralized view of data quality metrics across all tables, allowing users to monitor and track data quality issues with greater ease. The dashboard now consists of three tabs - Data Quality Summary, Data Quality by Table (Time Series), and Data Quality by Table (Full Snapshot) - each catering to different monitoring scenarios, and offers customizable parameters for reporting column names and filtering tables with data quality issues. Additionally, the installation process for the dashboard has been simplified, with options to import it directly to a Workspace or deploy it automatically using the Databricks CLI.
* DQX App Skeleton ([#982](#982)). The DQX application (frontend and backend) has been built with a core set of features, including configuration management and AI-assisted rule generation based on natural-language input from users. A comprehensive README documents the application architecture as well as development and deployment workflows. Future versions of DQX will introduce additional functionality (loading/saving rules, rules authoring in graphical form) and provide a streamlined, user-friendly way to deploy the application directly into a Databricks workspace.
* Added Decimal support to check functions and to min_max generator ([#1013](#1013)) ([#1017](#1017)). The data quality checks have been enhanced to support Python's Decimal type, in addition to int and float, for min/max validation checks, enabling proper data quality checks for decimal-precise financial and scientific data where floating-point precision issues would cause false positives.
* Added DQX produciton best practices and fix datetime limit handling ([#997](#997)). Practical guidance and best practices for using DQX in production have been added, covering aspects such as storing checks in Delta tables, enforcing access controls, and optimizing rules for performance and scalability. Fixes have also been implemented to address issues related to handling date and datetime limits, particularly when provided as strings.
* Added new row-level check functions: is_null, is_empty, and is_null_or_empty ([#1015](#1015)). DQX now includes three new check functions, `is_null`, `is_empty`, and `is_null_or_empty`, which enable verification of column values as null, empty strings, or both, complementing existing checks like `is_not_null`, `is_not_empty`, and `is_not_null_and_not_empty`. The functions also support optional arguments, like `trim_strings` to trim spaces from strings.
* Added tolerance to equality and non-equality check functions ([#1011](#1011)). The library's quality check functionality has been enhanced to support absolute and relative tolerance parameters for numeric value comparisons in `is_equal_to`,  `is_not_equal_to`, `is_aggr_equal` and `is_aggr_not_equal` checks, allowing for more flexible and precise control over data validation. The introduction of tolerance logic, which checks for absolute and relative differences within specified thresholds via `abs_tolerance` and `rel_tolerance` parameters, provides more nuanced comparisons for numeric data.
* Allow new lines in sql expression checks ([#1009](#1009)). SQL expression check function (`sql_expression`) has been updated to support new lines in its expression argument, allowing for more complex and formatted SQL expressions.
* Allow summary metrics with SparkConnect sessions ([#1000](#1000)). The library now supports writing summary metrics directly to a table with SparkConnect sessions, eliminating the need for a classic compute cluster in Dedicated access mode. This change lifts the previous restriction and enables generatic summary metrics  using Serverless and all standard clusters with Databricks Runtime 17.3LTS or higeher.
* Fixed loading checks from a delta table with special characters ([#992](#992)). The loading checks functionality from a delta table has been fixed to handle special characters in the fully qualified table.
* Fixed resolution of pii detection check function ([#1003](#1003)). The PII detection check function resolution has been enhanced to support the application of checks defined as metadata (YAML).
* Fixed serialization/deserialization of row filter parameter for dataset-level rules ([#1021](#1021)). The `filter` field in checks definition now correctly pushes down the `filter` condition defined at the check-level as `row_filter` to the check function, allowing checks to operate on the relevant subset of rows before aggregation. The documentation has been updated to advice users to use  op-level `filter` condition for consistency instead of `row_filter` parameter. Overall, these changes aim to enhance the overall user experience.
* Improved Lakeflow Declarative Pipeline tests ([#1010](#1010)). The Lakeflow Declarative Pipeline (LDP) tests have been enhanced to utilize full Unity Catalog mode, enabling support for writing to arbitrary catalogs and schemas, and performing additional checks to prevent certain operations.
* Updated Lakebase authentication method ([#975](#975)). The Lakebase authentication method has been updated to utilize a client ID instead of a username, simplifying its use in the context of a Databricks App. The `lakebase_user` parameter has been replaced with `lakebase_client_id`, an optional service principal client ID used to connect to Lakebase, defaulting to the caller's identity if not provided. This change enhances the security and reliability of the authentication process, making it easier to work with Lakebase as a checks storage.
* Updated handling of metadata columns during schema validation ([#1002](#1002)). The `has_valid_schema` check has been enhanced to provide more flexibility in schema validation by introducing an optional `exclude_columns` parameter, allowing users to specify columns to ignore during validation. This parameter can be used to exclude metadata columns or other columns not relevant to schema validation, and it takes precedence over the `columns` list.
* Updated product info when missing in config while verifying workspace client ([#987](#987)). The workspace client configuration has been enhanced to default product information to `dqx` with the current version when it is missing, ensuring that product information is always set for telemetry purposes.
* Updated profiler and generator documentation ([#1026](#1026)). The data profiling and quality checks generation feature has been enhanced with updated documentation, providing reference information for data quality profile types and associated rules.
* Added filter attribute in rules generated from ODCS ([#978](#978)). The rules generation process has been enhanced with the introduction of a filter attribute in rules generated from Open Data Contract Standard (ODCS), allowing for more flexible and targeted rules creation.
@mwojtyczka mwojtyczka mentioned this pull request Feb 9, 2026
mwojtyczka added a commit that referenced this pull request Feb 9, 2026
Change Log for New Release:
* New DQX Data Quality Dashboard
([#1019](#1019)). The data
quality dashboard has been significantly enhanced to provide a
centralized view of data quality metrics across all tables, allowing
users to monitor and track data quality issues with greater ease. The
dashboard now consists of three tabs - Data Quality Summary, Data
Quality by Table (Time Series), and Data Quality by Table (Full
Snapshot) - each catering to different monitoring scenarios, and offers
customizable parameters for reporting column names and filtering tables
with data quality issues. Additionally, the installation process for the
dashboard has been simplified, with options to import it directly to a
Workspace or deploy it automatically using the Databricks CLI.
* DQX App Skeleton
([#982](#982)). The DQX
application (frontend and backend) has been built with a core set of
features, including configuration management and AI-assisted rule
generation based on natural-language input from users. A comprehensive
README documents the application architecture as well as development and
deployment workflows. Future versions of DQX will introduce additional
functionality (loading/saving rules, rules authoring in graphical form)
and provide a streamlined, user-friendly way to deploy the application
directly into a Databricks workspace.
* Added Decimal support to check functions and to min_max generator
([#1013](#1013))
([#1017](#1017)). The data
quality checks have been enhanced to support Python's Decimal type, in
addition to int and float, for min/max validation checks, enabling
proper data quality checks for decimal-precise financial and scientific
data where floating-point precision issues would cause false positives.
* Added DQX produciton best practices and fix datetime limit handling
([#997](#997)). Practical
guidance and best practices for using DQX in production have been added,
covering aspects such as storing checks in Delta tables, enforcing
access controls, and optimizing rules for performance and scalability.
Fixes have also been implemented to address issues related to handling
date and datetime limits, particularly when provided as strings.
* Added new row-level check functions: is_null, is_empty, and
is_null_or_empty
([#1015](#1015)). DQX now
includes three new check functions, `is_null`, `is_empty`, and
`is_null_or_empty`, which enable verification of column values as null,
empty strings, or both, complementing existing checks like
`is_not_null`, `is_not_empty`, and `is_not_null_and_not_empty`. The
functions also support optional arguments, like `trim_strings` to trim
spaces from strings.
* Added tolerance to equality and non-equality check functions
([#1011](#1011)). The
library's quality check functionality has been enhanced to support
absolute and relative tolerance parameters for numeric value comparisons
in `is_equal_to`, `is_not_equal_to`, `is_aggr_equal` and
`is_aggr_not_equal` checks, allowing for more flexible and precise
control over data validation. The introduction of tolerance logic, which
checks for absolute and relative differences within specified thresholds
via `abs_tolerance` and `rel_tolerance` parameters, provides more
nuanced comparisons for numeric data.
* Allow new lines in sql expression checks
([#1009](#1009)). SQL
expression check function (`sql_expression`) has been updated to support
new lines in its expression argument, allowing for more complex and
formatted SQL expressions.
* Allow summary metrics with SparkConnect sessions
([#1000](#1000)). The
library now supports writing summary metrics directly to a table with
SparkConnect sessions, eliminating the need for a classic compute
cluster in Dedicated access mode. This change lifts the previous
restriction and enables generatic summary metrics using Serverless and
all standard clusters with Databricks Runtime 17.3LTS or higeher.
* Fixed loading checks from a delta table with special characters
([#992](#992)). The loading
checks functionality from a delta table has been fixed to handle special
characters in the fully qualified table.
* Fixed resolution of pii detection check function
([#1003](#1003)). The PII
detection check function resolution has been enhanced to support the
application of checks defined as metadata (YAML).
* Fixed serialization/deserialization of row filter parameter for
dataset-level rules
([#1021](#1021)). The
`filter` field in checks definition now correctly pushes down the
`filter` condition defined at the check-level as `row_filter` to the
check function, allowing checks to operate on the relevant subset of
rows before aggregation. The documentation has been updated to advice
users to use op-level `filter` condition for consistency instead of
`row_filter` parameter. Overall, these changes aim to enhance the
overall user experience.
* Improved Lakeflow Declarative Pipeline tests
([#1010](#1010)). The
Lakeflow Declarative Pipeline (LDP) tests have been enhanced to utilize
full Unity Catalog mode, enabling support for writing to arbitrary
catalogs and schemas, and performing additional checks to prevent
certain operations.
* Updated Lakebase authentication method
([#975](#975)). The Lakebase
authentication method has been updated to utilize a client ID instead of
a username, simplifying its use in the context of a Databricks App. The
`lakebase_user` parameter has been replaced with `lakebase_client_id`,
an optional service principal client ID used to connect to Lakebase,
defaulting to the caller's identity if not provided. This change
enhances the security and reliability of the authentication process,
making it easier to work with Lakebase as a checks storage.
* Updated handling of metadata columns during schema validation
([#1002](#1002)). The
`has_valid_schema` check has been enhanced to provide more flexibility
in schema validation by introducing an optional `exclude_columns`
parameter, allowing users to specify columns to ignore during
validation. This parameter can be used to exclude metadata columns or
other columns not relevant to schema validation, and it takes precedence
over the `columns` list.
* Updated product info when missing in config while verifying workspace
client ([#987](#987)). The
workspace client configuration has been enhanced to default product
information to `dqx` with the current version when it is missing,
ensuring that product information is always set for telemetry purposes.
* Updated profiler and generator documentation
([#1026](#1026)). The data
profiling and quality checks generation feature has been enhanced with
updated documentation, providing reference information for data quality
profile types and associated rules.
* Added filter attribute in rules generated from ODCS
([#978](#978)). The rules
generation process has been enhanced with the introduction of a filter
attribute in rules generated from Open Data Contract Standard (ODCS),
allowing for more flexible and targeted rules creation.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Add optional rounding for _aggr check_funcs

4 participants