For the complete documentation index, see llms.txt. This page is also available as Markdown.

Data quality & feature hygiene

Expectations that name the failing row and column, a profile that keeps null, NaN and zero apart, duplicates under a key and an equality you declare

Five kernel families for the question "can I trust this table". They run as compiled kernels inside the runtime that ships with the SDK — there is nothing to start and nothing to install separately.

The point of this package is the address, not the count. "3 rows failed" tells you a table is bad; it does not tell you what to fix. Every expectation comes back with the row, the column, the bound and the observed value:

from algenta import MojoRuntime

runtime = MojoRuntime(mode="local")

# Two columns (id, price), three rows, laid out row by row.
table   = [1.0, 5.0,
           2.0, -12.0,
           3.0, 7.0]
present = [True, True, True, True, True, True]

# Expect column 1 (price) to be greater than 0.0. Op 0 is ">".
report = runtime.execute(
    "data_quality.expect", "expect_column_flat",
    [table, 3, 2, present, 1, 0, 0.0, 0.0, 16],
)["result"]

report["n_violations"]     # 1
report["first_fail_row"]   # 1
report["fail_rows"]        # [1]
report["fail_columns"]     # [1]
report["fail_expected"]    # [0.0]
report["fail_observed"]    # [-12.0]

Row 1, column 1, expected > 0.0, observed -12.0. That is a work item. n_violations = 1 on its own is a status light.

The modules

Module
What it does

data_quality.expect

expectations over a column, over a suite of columns, or over a typed predicate you build yourself — each returning the failing rows, the columns they came from, the bound and the observed value, plus the rows it did not get to

data_quality.profile

a per-column census that counts nulls, NaNs, positive and negative infinities, zeros, negatives, positives and distinct finite values separately, with every statistic reported present or absent

data_quality.duplicates

grouping under a key you state and an equality you state, reporting group sizes, the first row of each group and how many rows are removable

data_quality.leakage

the ladder from "no signal" through "suspicious" to "proven", kept strictly apart, plus a timestamp check that is the only thing that can prove future information

data_quality.contract

the package's one status vocabulary, its finiteness test, its rounding-tolerance and decimal-grid builders, its non-wrapping work budget and its guarded column reader

Null, NaN and zero are three different facts

A column of nothing and a column of zeros are not the same column, and a profile that reports 0.0 for both is wrong in the way that costs the most. Two calls, same values, different presence:

Look at the last two lines together. Both mean slots read 0.0, and they mean completely different things — which is exactly why has_mean is the answer and mean is only a number you may read once the flag says you can. A mean of no data does not exist; a mean of three zeros is zero. The same rule holds for the spread, the extremes and the median.

A NaN is not a null either. A value that is present but not a number is counted in n_nan, and the two infinities are counted separately in n_pos_inf and n_neg_inf. None of them is allowed to become an average or an extreme, and none of them is silently dropped — the counts are the report.

A whole table profiles column by column, and the columns worth looking at first come back named:

Column 2 is empty and column 0 is constant. Neither is an error and both are usually a bug in whatever produced the table.

Ordering is a statistic too, so it is present-or-absent

A profile also reports whether a column's values are strictly increasing (1), strictly decreasing (-1) or neither (0) — useful when a column is supposed to be a sorted key or a timestamp. Nulls are skipped rather than treated as breaks, because a timestamp column with a gap in it is still ordered.

Because an order is a relation between at least two values, this field is flag-paired like every other statistic:

Read the flag first. A column with one live value, or none, has no ordering to report, and saying "strictly increasing" about a single point would be an answer about nothing. When the flag is True, a 0 covers both "constant" and "genuinely out of order" — and n_distinct_finite in the same report tells you which: 1 is constant, more is out of order.

A duplicate needs a key and a notion of equality

Nothing is a duplicate in the abstract. It is a duplicate under a key, and under a rule for when two numbers count as the same number. You state both, and changing either changes the answer — visibly:

1.001 and 1.002 are two customers or one, depending on a decision only you can make. The package will not make it for you, and it records which one you made in decimals.

Rows whose key is missing are not duplicates of each other. They are counted in n_null_key_rows and excluded from every group, because "both unknown" is not "both the same".

Suspicious is not proven

This is the distinction the whole leakage module exists to hold. A feature that correlates perfectly with the target is suspicious. A feature that was recorded after the event you are predicting is proven leakage. From the values alone you can prove the first kind of thing but never the second, and the report says which one it found.

a and c both have a correlation of essentially 1. Only c is proven, because only c is a deterministic function of the target that reproduces it on every row it was allowed to compare. a is a lead to investigate — it could equally be a feature that is genuinely, and legitimately, very predictive.

What "proven" costs, so that it means something

A proof claim is only worth having if it is hard to get, so two things are required beyond a small residual.

The feature has to actually vary. A column that never changes fits 0 * target + c perfectly on every row — and a constant column can be produced without ever looking at the target, which is the opposite of leakage. An all-default or all-zero column is common in real tables, so this matters in practice: it comes back as no signal, with has_affine false and feature_constant_on_compared telling you why.

Each row is judged against its own magnitude. The residual at a row is compared to a few units in the last place of that row's observed value, and to nothing else — not to a total, a maximum, or anything about the other rows. One consequence is worth knowing in advance: when a target has a very large magnitude and a very narrow range (epoch timestamps are the everyday example), the straight line through its two extreme rows is numerically fragile, and a relationship that is exactly affine on paper can come back not proven with a large affine_max_residual. That is deliberate. It means "this data cannot support the claim", not "there is no relationship" — and such a feature still correlates at 1.0, so it is still reported, as suspicious.

proven_affine false is not has_affine false. has_affine true with proven_affine false means a candidate line was fitted and affine_max_residual tells you how badly it missed. has_affine false means no line was fitted at all, and the report says which precondition was missing.

Row 2's feature was recorded fifteen units after the thing it is supposed to predict. That is not a correlation to argue about; it is a defect in how the dataset was built.

When nothing is late, the lead does not become zero:

A lead of 0.0 would mean a feature observed at the exact instant of the event, which is a real and different finding. Absent means there was no such row at all. Rows that share a timestamp exactly are counted in n_simultaneous_rows and are never treated as proof.

There is a second way the largest lead can be absent, and it is distinguishable from the first. If two timestamps are so far apart that their difference is larger than a 64-bit float can hold, that row's lead has no numeric value — the proof still stands, the row is still counted and still addressable, but the largest lead is no longer a number that exists:

So n_future_rows == 0 means "nothing was late" and n_future_rows > 0 with n_unrepresentable_lead_rows > 0 means "something was late by an amount too large to state". The largest representable lead is deliberately not reported in that case: telling you "the worst skew is 10 seconds" while another row leads by an astronomical amount is the exact mistake this field exists to prevent.

A large table tells you what it did not check

An expectation suite over a big table has a work ceiling, and when a request runs into it the report checks a prefix and says so. It does not quietly sample:

n_rows_checked + n_rows_not_checked always equals n_rows. This matters more than it looks: a sampling pass that reports a clean table has given you a wrong answer, while a prefix pass that reports 4,092 of 4,093 rows has given you a partial one you can act on. Narrow the table or split the suite and run again.

work_truncated is True for exactly one reason — the request needed more work than the ceiling allows — so that advice always applies when you see it. In particular a request that contains no expectations at all is refused (status 422) rather than answered: a suite of nothing would otherwise report zero violations over zero rows, which reads as a clean table, and would report a truncation it never hit.

The listed addresses are capped separately by the max_reports argument you pass, so a table with a million bad rows returns a bounded report rather than a million-element list. n_violations still counts all of them, and reports_truncated tells you the list was cut.

Statuses

Every function returns a status of 0 on success, or one of these:

Code
Meaning

400

a negative row or column count

401

the declared shape does not match the number of values given

402

too many rows

403

too many columns

404

the request exceeds the work budget

405

the presence mask does not describe exactly the declared number of rows

406

a column index outside the table

407

lists that should be parallel have different lengths

408

a decimal grid outside the supported range

409

a value that cannot be placed on the declared grid without changing it

410

a table with no columns to profile

420 – 429

an expectation or predicate the engine will not run: an unknown operator, a non-finite bound, a request containing nothing to check, a malformed predicate, or a suite past its ceiling

430 – 436

a leakage verdict, from no signal through suspicious to proven, or a threshold outside [0, 1]

440 – 449

a duplicate key that is empty, too wide, or names the same column twice

The leakage codes in the 430 range are verdicts rather than errors: 430 no signal, 431 suspicious, 432 the feature is the target, 433 the feature is an affine image of the target, 434 too few comparable rows to decide, 435 the feature was observed after the event.

434 is not 430. "Too few comparable rows to decide" and "no leakage found" are different answers, and a pipeline that treats them the same will pass a dataset it never actually examined. The same applies to a constant target: there is no learning problem to leak into, so the verdict is 434, not a clean bill of health.

See also

Last updated

Was this helpful?