Data Profiling & Schema Inference

data_quality.validate_rows consumes a hand-written schema and stats.describe summarises one numeric list — nothing surveyed a whole row-set to report per-column null fraction, cardinality, inferred type, value ranges, and top values, nor proposed a starting schema. This adds the profiler step that feeds the existing validator.

Pure standard library (collections + reuse of stats); imports no PySide6. Every function is pure (rows in, dict out), so it is fully deterministic in CI.

Headless API

from je_auto_control import profile_rows, infer_schema, validate_rows, load_rows

rows = load_rows("export.csv")
profile = profile_rows(rows)
# profile["columns"]["age"] -> {count, null_count, null_fraction, distinct,
#   unique, inferred_type, top_values, min, max, mean}

schema = infer_schema(rows)        # validate_rows-compatible
report = validate_rows(rows, schema)

profile_rows returns {row_count, columns} where each column carries its count, null count and fraction, distinct count, a uniqueness flag, the inferred type (int / number / bool / str), the top values with counts, and min / max / mean for numeric columns. infer_schema turns that profile into a schema the existing validate_rows understands: a column is required when it has no nulls, unique when every non-null value is distinct, and carries numeric bounds. Pass an explicit columns list to restrict either function to a subset.

Executor commands

AC_profile_rows profiles a rows list (optional columns subset) and returns {profile}. AC_infer_schema returns {schema}. Both are exposed as MCP tools (ac_profile_rows / ac_infer_schema) and as Script Builder commands under Data.