Skip to content

Commit 3ec52ca

Browse files
HaoZekebzaczynski
andauthored
feat: add pointblank tutorial materials (#764)
* feat: add pointblank tutorial materials * docs: refine pointblank materials * fix: sync pointblank materials with tutorial scripts Align the quickstart and atom scripts and the atom YAML with the published tutorial examples, and refresh the generated HTML report. * Update README --------- Co-authored-by: Bartosz Zaczyński <bartosz.zaczynski@gmail.com>
1 parent cbf5c35 commit 3ec52ca

9 files changed

Lines changed: 734 additions & 0 deletions

python-pointblank/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Validating Data With Pointblank in Python
2+
3+
Supporting code and sample data for the Real Python tutorial [Validating Data With Pointblank in Python](https://realpython.com/python-pointblank/).
4+
5+
## Requirements
6+
7+
The Python scripts use PEP 723 dependency metadata and run with [uv](https://docs.astral.sh/uv/):
8+
9+
```console
10+
$ uv run pointblank_quickstart.py
11+
$ uv run pointblank_thresholds.py
12+
$ uv run pointblank_atoms.py
13+
```
14+
15+
The command-line examples can run without a project environment:
16+
17+
```console
18+
$ uv run --no-project --with 'pointblank[pl]' -- pb scan pointblank_atoms.csv
19+
$ uv run --no-project --with 'pointblank[pl]' -- pb missing pointblank_atoms.csv
20+
$ uvx --from 'pointblank[pl]' pb run pointblank_atoms.yaml --output-html pointblank_report.html
21+
```
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
atom_id,symbol,x,y,z,fx,fy,fz
2+
0,Cu,1.0,0.5,0.1,0.1,0.0,0.0
3+
1,Pt,2.1,1.5,0.2,-0.2,0.1,-0.1
4+
2,Cu,3.2,2.5,0.3,0.3,-0.1,0.1
5+
3,Pt,4.3,3.5,0.4,-0.1,0.0,0.0
6+
4,Cu,5.4,4.5,0.5,0.2,0.1,-0.1
7+
5,Pt,6.5,5.5,0.6,-0.3,-0.1,0.1
8+
6,Cu,7.6,6.5,0.7,0.1,0.0,0.0
9+
7,Pt,8.7,7.5,0.8,-0.2,0.1,-0.1
10+
8,Cu,9.8,8.5,0.9,0.3,-0.1,0.1
11+
9,Pt,10.9,9.5,1.0,-0.1,0.0,0.0
12+
10,Zz,0.5,0.5,0.1,0.0,0.0,0.0
13+
11,Cu,,1.5,0.2,0.0,0.0,0.0
14+
12,Pt,12.1,2.5,0.3,1500.0,0.0,0.0
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# /// script
2+
# requires-python = ">=3.10"
3+
# dependencies = [
4+
# "pointblank[pl]",
5+
# ]
6+
# ///
7+
8+
import polars as pl
9+
import pointblank as pb
10+
11+
VALID_ELEMENTS = ["Cu", "Pt"]
12+
13+
14+
def main() -> None:
15+
atoms = pl.read_csv("pointblank_atoms.csv")
16+
17+
validation = (
18+
pb.Validate(
19+
data=atoms,
20+
tbl_name="atoms",
21+
label="Atom data validation",
22+
thresholds=pb.Thresholds(warning=0.02, error=0.05, critical=0.07),
23+
)
24+
.col_vals_in_set(columns="symbol", set=VALID_ELEMENTS)
25+
.col_vals_not_null(columns=["x", "y", "z"])
26+
.col_vals_between(columns=["x", "y", "z"], left=0, right=20)
27+
.col_vals_between(columns=["fx", "fy", "fz"], left=-1000, right=1000)
28+
.interrogate()
29+
)
30+
31+
clean = validation.get_sundered_data(type="pass")
32+
dirty = validation.get_sundered_data(type="fail")
33+
34+
print(f"Clean rows: {len(clean)}")
35+
print(clean.select(["atom_id", "symbol", "x", "fx"]))
36+
print(f"\nDirty rows: {len(dirty)}")
37+
print(dirty.select(["atom_id", "symbol", "x", "fx"]))
38+
39+
40+
if __name__ == "__main__":
41+
main()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
tbl: pointblank_atoms.csv
2+
df_library: polars
3+
tbl_name: "atoms"
4+
label: "Atom data validation"
5+
thresholds:
6+
warning: 0.02
7+
error: 0.05
8+
critical: 0.07
9+
steps:
10+
- col_vals_in_set:
11+
columns: symbol
12+
set: [Cu, Pt]
13+
- col_vals_not_null:
14+
columns: [x, y, z]
15+
- col_vals_between:
16+
columns: [x, y, z]
17+
left: 0
18+
right: 20
19+
- col_vals_between:
20+
columns: [fx, fy, fz]
21+
left: -1000
22+
right: 1000
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# /// script
2+
# requires-python = ">=3.10"
3+
# dependencies = [
4+
# "pointblank[pl]",
5+
# ]
6+
# ///
7+
8+
import json
9+
10+
import pointblank as pb
11+
12+
13+
def main() -> None:
14+
validation = (
15+
pb.Validate(
16+
data=pb.load_dataset("small_table", tbl_type="polars"),
17+
tbl_name="small_table",
18+
label="Quickstart validation",
19+
)
20+
.col_vals_between(columns="d", left=0, right=5000)
21+
.col_vals_in_set(columns="f", set=["low", "mid", "high"])
22+
.col_vals_not_null(columns="c")
23+
.interrogate()
24+
)
25+
26+
report = json.loads(validation.get_json_report())
27+
28+
print("Validation summary:\n")
29+
for step in report:
30+
print(
31+
f"{step['assertion_type']:20}"
32+
f"passed={step['n_passed']:<4}"
33+
f"failed={step['n_failed']}"
34+
)
35+
36+
37+
if __name__ == "__main__":
38+
main()

python-pointblank/pointblank_report.html

Lines changed: 526 additions & 0 deletions
Large diffs are not rendered by default.
71 KB
Loading
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Starter Pointblank template for adapting to your own pipeline.
2+
#
3+
# You can run this template against a real file with:
4+
# uv run --no-project --with 'pointblank[pl]' pb run pointblank_starter_validation.yaml --data your_data.csv --fail-on critical
5+
6+
tbl: small_table
7+
df_library: polars
8+
tbl_name: "Starter Validation"
9+
label: "Adapt this template to your data"
10+
thresholds:
11+
warning: 0.02
12+
error: 0.05
13+
critical: 0.10
14+
steps:
15+
- col_exists:
16+
columns: [record_id, status, amount]
17+
- col_vals_not_null:
18+
columns: record_id
19+
- col_vals_in_set:
20+
columns: status
21+
set: [pending, shipped, delivered]
22+
- col_vals_gt:
23+
columns: amount
24+
value: 0
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# /// script
2+
# requires-python = ">=3.10"
3+
# dependencies = [
4+
# "pointblank[pl]",
5+
# ]
6+
# ///
7+
8+
import pointblank as pb
9+
10+
11+
def main() -> None:
12+
validation = (
13+
pb.Validate(
14+
data=pb.load_dataset("small_table", tbl_type="polars"),
15+
tbl_name="small_table",
16+
label="Threshold-driven validation",
17+
thresholds=pb.Thresholds(warning=0.05, error=0.10, critical=0.15),
18+
actions=pb.Actions(
19+
warning=(
20+
"Warning: step {step} reached {level} severity during "
21+
"{type}."
22+
),
23+
critical=(
24+
"Critical: step {step} reached {level} severity during "
25+
"{type}."
26+
),
27+
),
28+
)
29+
.col_vals_between(columns="d", left=0, right=5000)
30+
.col_vals_not_null(columns="c")
31+
.rows_distinct()
32+
.interrogate()
33+
)
34+
35+
print("All checks passed perfectly:", validation.all_passed())
36+
print(
37+
"Anything above the error threshold:",
38+
validation.above_threshold(level="error"),
39+
)
40+
41+
try:
42+
validation.assert_below_threshold(level="critical")
43+
except AssertionError as exc:
44+
print("CI gate tripped:", exc)
45+
46+
47+
if __name__ == "__main__":
48+
main()

0 commit comments

Comments
 (0)