Skip to content

EDA & leakage

parleml_mli.eda is a data-level exploratory analysis package: correlations, distributions, missingness, outliers, duplicates, consolidated alerts, and a leakage scanner. It works on any frame — no model required. The canonical entry point is compute_eda.

In short

  • compute_eda(X, y=y) works on any frame; no model needed.
  • It profiles a 20,000-row sample by default. Duplicates, rarity and unlabelled-row checks read the full frame.
  • The leakage scan flags six kinds of suspicious feature, such as near-perfect target separation, ID-like columns and dates recorded after the outcome.
  • Associations share one bias-corrected |r| scale: Pearson, Cramér's V and η.
  • Leakage findings feed the trust summary's leakage tile.

Agent access

The EDA artifact is exposed to AI agents as the MCP tool get_eda (sections + per-column drill) plus the eda.* chart family — see the ParleML MCP docs site (parleml_mcp/docs/).

Two ways in

As part of profiling — set ProfilingConfig(eda=EDAConfig(...)) and read result.eda_:

from parleml_mli import ProfilingConfig, EDAConfig, fit_profiler

config = ProfilingConfig(eda=EDAConfig())
result = fit_profiler(
    model=clf, X=X_train, y=y_train,
    target_mask=(y_train == 1), config=config,
)
eda = result.eda_

Standalone — call compute_eda on any frame:

from parleml_mli import compute_eda

eda = compute_eda(X_train, y=y_train)
eda.table              # TableStats: rows, columns, missing %, duplicates
eda.variables          # per-column VariableStats
eda.correlation_matrix # numeric + categorical correlation matrix
eda.alerts             # consolidated data-quality alerts
eda.leakage_report     # suspicious-feature findings (see below)

What runs on the sample, and what on the full frame

compute_eda profiles a working sample (sample_size, default 20,000 rows) with fg-data-profiling (import data_profiling) and mli's own kernels. The profiler runs with its usage telemetry switched off (YDATA_PROFILING_NO_ANALYTICS), so profiling a dataset never sends anything over the network. A few checks need every row and read the full frame instead:

  • Duplicate rows — one pass finds them: table.n_duplicate_rows counts the rows that repeat an earlier row (1,000 identical rows count 999), the DUPLICATES alert reports that count, and duplicates_df lists the largest groups. duplicates_kwargs={"subset": [...]} defines a duplicate by those columns for all three.
  • Rarity — a CONSTANT alert from the sample is dropped when the column varies in the full frame, and an ID_LIKENESS finding confirms its cardinality there.
  • Unlabelled rows — a row without a target value is not an extra target class. Target correlations, the target tie-break between over-correlated features and the leakage scan's target checks read the labelled rows only (and are skipped when no row is labelled); one MISSING alert on the target counts the unlabelled rows of the full frame.

Target and matrix associations share one |r| scale: Pearson for numeric pairs, Cramér's V for categorical pairs and the correlation ratio η for mixed pairs. Both categorical measures are bias-corrected — Cramér's V by Bergsma–Wicher, η as the square root of Kelley's ε² (the adjusted η²) — so a high-cardinality categorical does not read as associated with pure noise; a categorical with more levels than half the rows gets no η (NaN).

Configuration

compute_eda takes either a prebuilt EDAConfig or field overrides as **kwargs:

from parleml_mli.eda import EDAConfig

cfg = EDAConfig(sample_size=50_000, correlations_enabled=True, leakage_enabled=True)
eda = compute_eda(X_train, y=y_train, config=cfg)

# …or override individual fields inline:
eda = compute_eda(X_train, y=y_train, sample_size=50_000, outliers_enabled=False)

Runtime depends on row count, column types, cardinality and enabled checks. Every artifact has an on/off toggle (univariate_enabled, missingness_enabled, outliers_enabled, …) and a *_kwargs sub-config. See the full table on the Configuration parameters page.

Leakage scanning

When leakage_enabled=True (the default), compute_eda runs detect_suspicious_features as its final step and attaches the result to eda.leakage_report. It flags six finding kinds:

Finding Trigger
SINGLE_VALUE Fewer than two distinct values — a constant column.
HIGH_CARDINALITY A categorical / text column whose distinct values come close to the row count (never a number, boolean or timestamp).
ID_LIKENESS Near-unique cardinality (leakage_id_cardinality_threshold) plus an ID-shaped value or name — looks like a row id, not a feature.
TARGET_CORRELATION_EXTREME |correlation| with the target ≥ leakage_target_correlation_threshold (default 0.99), or, for a binary target, a numeric feature that alone separates the classes with ROC AUC ≥ leakage_binary_auc_threshold (default 0.98, either direction; scored when each class has at least 30 labelled rows).
NEAR_DUPLICATE_PROXY A feature is a near-perfect proxy for the target (leakage_near_duplicate_threshold).
POST_OUTCOME_DATE A datetime feature is later than time_column on a high share of rows — recorded after the outcome.

The target checks read the labelled rows only; the other detectors read every row.

eda = compute_eda(X_train, y=y_train, time_column="event_ts")
for finding in eda.leakage_report.findings:
    print(finding.kind, finding.columns, finding.suggested_action, finding.description)

You can also run the scanner directly without a full EDA pass — on a frame that carries the target column:

from parleml_mli.eda import detect_suspicious_features

report = detect_suspicious_features(train_df, target="is_fraud")

Plots

EDAResult carries plot helpers that return plotly figures on demand:

eda.plot_correlation_matrix()
eda.plot_missingness()
eda.plot_outliers()

The row-level AutoViz figures — correlated pairs, category boxplots, the PCA biplot and parallel coordinates — draw sampled rows of the dataset, so the result stores none of their data: build them from the frame the EDA ran on with parleml_mli.eda.render_autoviz_figure(kind, df, result=eda) (seeded from eda.config, so a figure is identical across calls). The shape radar is drawn from the result itself (eda.plot_shape_radar()).

Persisted results

An EDAResult carries schema_version (parleml_mli.eda.EDA_SCHEMA_VERSION). One written under another schema is refused on load — unpickling it (e.g. inside a saved profile) and EDAResult.from_dict both raise parleml_mli.StaleArtifactError: re-run compute_eda on the dataset. The platform treats such a cached EDA as absent and recomputes it on the next Run EDA.

In the platform

The product's Dataset EDA / Leakage panels are powered by compute_eda + detect_suspicious_features, and the leakage findings feed the Trust Summary leakage tile.

Next