pippen.data.validate¶
Cross-table validation, run after ingest and before anything computes.
Where schemas sees one table, this sees a season. A file can be perfectly
well formed and still disagree with the file next to it, and only a check that
looks at both will notice.
pippen.data.validate ¶
Dataset-level validation, run after ingest and before anything computes.
schemas.py validates one table at a time: its columns, its dtypes, its
per-row constraints. That catches a malformed file. It cannot catch a
well-formed file that disagrees with the file next to it.
The checks here look across tables and across a season. A play-by-play file missing forty games is perfectly valid on its own terms. Every column is present, every dtype is right, every row passes. The only thing wrong with it is that the schedule says there should be more games, and nothing except a cross-table check will ever notice.
Failure, skip and pass are three different outcomes
A check that could not run because an input was absent reports skipped,
never passed. Reporting a check that never ran as a pass is the worst
outcome available here, because it produces a green validation report for
data nobody checked.
Season boundaries A season labelled by its end year runs from roughly October of the previous calendar year to June of the labelled year. The window used below is deliberately wider than that, from 1 August of the previous year to 30 September of the labelled year. It exists to catch a year and season mix-up or an off-by-one in a filename, not to police the exact first and last day of a schedule that varies with lockouts, pandemics and tournaments.
CheckResult
dataclass
¶
The outcome of one validation check.
Attributes:
| Name | Type | Description |
|---|---|---|
check |
str
|
Short stable name for the check, suitable for a report row. |
status |
CheckStatus
|
|
detail |
str
|
One human-readable sentence. For a failure it says what is wrong and, where useful, which identifiers to look at. For a skip it says which input was missing. |
Source code in src/pippen/data/validate.py
ok
property
¶
Whether this check found nothing wrong.
A skip counts as not ok. It did not find a problem, but it also did not look, and treating those the same is how unchecked data reaches a calculation.
check_schedule_and_play_by_play_agree ¶
Check that the schedule and the play-by-play cover the same games.
A gap in either direction matters. Games in the schedule with no play-by-play are missing possessions, which biases anything computed from them. Games in the play-by-play with no schedule entry mean the two files came from different seasons or different sources.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schedule
|
DataFrame | None
|
The season's schedule table, or None if it was not ingested. |
required |
play_by_play
|
DataFrame | None
|
The season's play-by-play table, or None. |
required |
Returns:
| Type | Description |
|---|---|
CheckResult
|
The check outcome. |
Source code in src/pippen/data/validate.py
check_no_duplicate_rows ¶
Check a table has no repeated rows on its natural key.
This is what a re-download concatenated on top of itself looks like, and it inflates every per-player total downstream without changing the shape of the table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frame
|
DataFrame | None
|
The table to check, or None if it was not ingested. |
required |
dataset
|
str
|
Dataset name, used to pick the natural key and to name the check. |
required |
Returns:
| Type | Description |
|---|---|
CheckResult
|
The check outcome. A dataset with no defined natural key is skipped |
CheckResult
|
rather than passed. |
Source code in src/pippen/data/validate.py
check_teams_match_the_schedule ¶
Check each game's teams agree between the schedule and the play-by-play.
Both tables independently record which two teams played. When they disagree, one of them is wrong about the fixture, and every possession attributed from the play-by-play is attributed to the wrong side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schedule
|
DataFrame | None
|
The season's schedule table, or None. |
required |
play_by_play
|
DataFrame | None
|
The season's play-by-play table, or None. |
required |
Returns:
| Type | Description |
|---|---|
CheckResult
|
The check outcome. |
Source code in src/pippen/data/validate.py
check_events_run_in_order ¶
Check each game's periods do not run backwards.
Rows arrive in event order. A period that decreases partway through a game means two games were concatenated under one identifier, or the file was sorted by something other than time and the ordering was then trusted.
Clock values are deliberately not checked. hoopR's exact encoding of
clock_minutes and clock_seconds has not been verified, and a wrong
assumption about it would fail valid rows rather than catch corrupt ones.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
play_by_play
|
DataFrame | None
|
The season's play-by-play table, or None. |
required |
Returns:
| Type | Description |
|---|---|
CheckResult
|
The check outcome. |
Source code in src/pippen/data/validate.py
check_dates_fall_inside_the_season ¶
Check every scheduled date falls inside the season it is labelled with.
Catches a year and season mix-up, or a filename off by one, both of which produce a table that is internally consistent and attached to the wrong year. See the module docstring for why the window is wider than a real schedule.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schedule
|
DataFrame | None
|
The season's schedule table, or None. |
required |
season
|
int
|
Season end year, so 2024 means the 2023-24 season. |
required |
Returns:
| Type | Description |
|---|---|
CheckResult
|
The check outcome. |
Source code in src/pippen/data/validate.py
validate_season ¶
Run every applicable check for one season and return all results.
Every check runs, including after one fails. A person should be able to fix a season's files once rather than re-running validation after each individual fix.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
season
|
int
|
Season end year, so 2024 means the 2023-24 season. |
required |
tables
|
Mapping[str, DataFrame | None]
|
Ingested tables by dataset name. A dataset that is absent, or
present with the value None, causes the checks that need it to
report |
required |
Returns:
| Type | Description |
|---|---|
list[CheckResult]
|
One result per check, in a stable order. |
Source code in src/pippen/data/validate.py
summarise ¶
Render a list of check results as a short report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
list[CheckResult]
|
The results to render. |
required |
Returns:
| Type | Description |
|---|---|
str
|
A multi-line report: one counts line, then one line per check. |