Skills › Scientific Agent Skills › exploratory-data-analysis › missingness_leakage_audit.py
missingness_leakage_audit.py
130 lines · skills/exploratory-data-analysis/scripts/missingness_leakage_audit.py at 5f2bc03
1#!/usr/bin/env python3
2"""Bounded missingness, group structure, and split leakage audit for CSV/TSV."""
4from __future__ import annotations
6import argparse
8from _capabilities import capability_for_path
9from _common import (
10 DEFAULT_MAX_FILE_BYTES,
11 DEFAULT_MAX_ROWS,
12 bounded_file_limit,
13 checked_input_file,
14 emit_json,
15 run_cli,
16)
17from _tabular import audit_missingness_and_leakage
20def build_parser() -> argparse.ArgumentParser:
21 parser = argparse.ArgumentParser(
22 description=(
23 "Audit bounded CSV/TSV missingness and common group/entity/time split "
24 "leakage. Identifiers and cell values are tokenized or omitted."
25 )
26 )
27 parser.add_argument("input", help="Local .csv or .tsv path inside --root")
28 parser.add_argument(
29 "--root",
30 default=".",
31 help="Existing local directory that bounds all input/output paths",
32 )
33 parser.add_argument(
34 "--max-bytes",
35 type=int,
36 default=DEFAULT_MAX_FILE_BYTES,
37 help=f"Maximum input bytes (hard ceiling: {512 * 1024 * 1024})",
38 )
39 parser.add_argument(
40 "--max-rows",
41 type=int,
42 default=DEFAULT_MAX_ROWS,
43 help="Maximum rows to scan (hard ceiling: 1000000)",
44 )
45 parser.add_argument(
46 "--missing-token",
47 action="append",
48 help=(
49 "Explicit additional missing code; repeat as needed. Empty/whitespace "
50 "is always missing."
51 ),
52 )
53 parser.add_argument(
54 "--group-column",
55 help="Exact column identifier for biological/experimental grouping",
56 )
57 parser.add_argument(
58 "--entity-column",
59 help="Exact subject/sample/entity identifier used to detect split overlap",
60 )
61 parser.add_argument(
62 "--split-column",
63 help="Exact train/validation/test or analysis split column identifier",
64 )
65 parser.add_argument(
66 "--time-column",
67 help="Exact ISO-8601 time column used for split interval overlap checks",
68 )
69 parser.add_argument(
70 "--reveal-identifiers",
71 action="store_true",
72 help="Emit sanitized column identifiers; values and group/entity IDs stay tokenized",
73 )
74 parser.add_argument("--output", help="Optional local .json output path")
75 parser.add_argument(
76 "--force",
77 action="store_true",
78 help="Allow replacement of an existing regular output file",
79 )
80 return parser
83def _main() -> None:
84 args = build_parser().parse_args()
85 max_bytes = bounded_file_limit(args.max_bytes)
86 path = checked_input_file(
87 args.input,
88 root=args.root,
89 suffixes={".csv", ".tsv"},
90 max_bytes=max_bytes,
91 )
92 report = {
93 "schema_version": "1.1",
94 "capability": capability_for_path(path),
95 "analysis": audit_missingness_and_leakage(
96 path,
97 max_rows=args.max_rows,
98 missing_tokens=args.missing_token,
99 group_column=args.group_column,
100 entity_column=args.entity_column,
101 split_column=args.split_column,
102 time_column=args.time_column,
103 reveal_identifiers=args.reveal_identifiers,
104 ),
105 "security": {
106 "local_only": True,
107 "file_text_is_untrusted_data": True,
108 "embedded_instructions_followed": False,
109 "raw_values_paths_and_entity_ids_emitted": False,
110 },
111 "interpretation": {
112 "potential_overlap_is_not_proof_of_model_leakage": True,
113 "no_overlap_found_is_not_proof_of_independence": True,
114 "domain_design_review_required": True,
115 },
116 }
117 emit_json(
118 report,
119 output=args.output,
120 root=args.root,
121 force=args.force,
122 )
125def main() -> int:
126 return run_cli(_main)
129if __name__ == "__main__":
130 raise SystemExit(main())