{#- Brand mark: a beam enters an equilateral prism and the spectrum emerges staggered along the right face, each ray fanning out at its own angle. -#} {# The mark is base64-embedded rather than linked, so a report stays a single offline file. If the packaged asset is missing the masthead falls back to the wordmark alone rather than showing a broken image. #} {% if favicon_uri %} {% endif %}
{% set verdict = result.metadata.get('verdict') %} {% set consensus = result.evidence | selectattr('kind', 'equalto', 'anomaly_consensus_review') | list %} {% set review_count = consensus[0].value.review_row_count if consensus else 0 %}{{ result.summary }}
{{ ai.narrative }}
{% endif %} {% if ai.column_reads %}{{ read.column }}{{ read.meaning }}{{ read.child }} → {{ read.parent }}{{ read.reading }}Interpretation by the AI investigator ({{ result.metadata.get('model', 'model') }}) over the deterministic evidence{% if ai.shared_labels %}, with column value samples shared per your privacy setting{% endif %} — judgment layered on the measurements above, not new measurements. Verify against the evidence.
What this run could not cover, and why.
{{ warning.code }}: {{ warning.message }}
{% endfor %} {% if result.sampling %} {% for record in result.sampling %}{{ record.operation }}: {{ record.sampled_rows | format_value }} of {{ record.source_rows | format_value }} rows, sampled with seed {{ record.seed }} ({{ record.strategy }}).{% for limitation in record.limitations %} {{ limitation }}{% endfor %}
{% endfor %} {% endif %}Minimal combinations only; business confirmation is required.
| Table | Columns | Unique | Complete | Confidence |
|---|---|---|---|---|
| {{ item.scope.table }} | {{ item.scope.columns | join(' + ') }} | {{ "%.1f%%" | format(item.value.uniqueness_rate * 100) }}{% if item.value.sampled %} sampled{% endif %} | {{ "%.1f%%" | format(item.value.completeness_rate * 100) }} | {{ "%.1f%%" | format(item.confidence * 100) }} |
Direction runs from the unique parent key to the referencing child columns. Sorted by confidence — filter by table to focus a pair.
| Parent | Child | Cardinality | Included | Orphan rows | Confidence |
|---|---|---|---|---|---|
{{ item.value.parent_table }}.{{ item.value.parent_columns | join(' + ') }} |
{{ item.value.child_table }}.{{ item.value.child_columns | join(' + ') }} |
{{ item.value.cardinality | replace('_', '–') }} | {{ "%.1f%%" | format(item.value.inclusion_rate * 100) }} | {{ item.value.orphan_row_count }} | {{ "%.0f%%" | format(item.confidence * 100) }}{% if item.value.sampled %} sampled{% endif %} |
{{ artifact.metadata.description }}
{% endif %}| {{ column.label }} | {% endfor %}
|---|
| {{ row[column.key] | format_value }} | {% endfor %}
Things wrong with the data: values that are missing, duplicated, mistyped, or standing in for something else. Ranked by severity.
{% else %}Ranked by severity so the most important issues are reviewed first.
{% endif %}{{ finding.summary }}
{% if finding.recommendation %}{{ finding.recommendation }}
{% endif %}| {{ column.label }} | {% endfor %}
|---|
| {{ row[column.key] | format_value }} | {% endfor %}
True of this data, and worth knowing — but not defects. Nothing here needs fixing; some of it changes how you model.
{{ finding.summary }}
{% if finding.recommendation %}{{ finding.recommendation }}
{% endif %}{{ item.value.review_row_count }} of {{ item.value.evaluated_row_count }} row{% if item.value.evaluated_row_count != 1 %}s{% endif %}, ranked by how many independent checks flagged them. Highlighted cells are the values that stand out; expand a row to see how each check flagged it.
| Agreement | {% for column in item.value.columns %}{{ column }} | {% endfor %}Why it stands out |
|---|---|---|
| {{ row.method_count }}/{{ item.value.total_detectors }} {{ row.methods | join(' · ') }} | {% for column in item.value.columns %}{{ row['values'][column] | format_cell }} | {% endfor %}
{{ row.why }}
{% if ex.get('univariate') %}{{ ex.univariate.contributors | why_bars_svg }}{% endif %}
{% if mv or cond %}
How each check flagged this{% if mv %}Multivariate — joint deviation score {{ mv.score | round(1) }} (flags at ≥ {{ mv.threshold | round(1) }}). Every column's distance from its own typical value:
{{ mv['columns'] | why_bars_svg }}
Conditional — {{ cond.value_column }} is unusual for the {{ cond.condition_column }} ≈ {{ cond.condition_value | format_cell }} peer group. Where this row sits vs its peers:
{{ cond | peer_group_svg }}
|
Every image the checks above called out, shown rather than listed. Duplicate candidates are paired side by side so you can confirm or dismiss them without opening a file browser.
One dot per distinct image size — a single dot means the dataset already agrees on a shape, a spread means your resize policy has to make a decision.
{{ dimension.value.unique_dimensions | format_value }} distinct size{% if dimension.value.unique_dimensions != 1 %}s{% endif %} · dot size = image count
{{ dimension.value | image_dimension_svg }}median {{ brightness.value.box.median | format_cell }} · 0 is black, 1 is white
{{ brightness.value | histogram_svg }}{{ artifact.metadata.description }}
{% endif %}| {{ column.label }} | {% endfor %}
|---|
| {{ row[column.key] | format_value }} | {% endfor %}
Where each numeric column's values sit. Bars are row counts; the strip below each shows the median, the middle 50%, and the whiskers. Red marks the flagged values.
Two populations — {{ item.value.modality.clusters[0].count }} row(s) up to {{ item.value.modality.clusters[0].max | format_cell }}, then {{ item.value.modality.clusters[1].count }} row(s) from {{ item.value.modality.clusters[1].min | format_cell }} up.
{% else %}median {{ item.value.box.median | format_cell }} · middle 50% {{ item.value.box.q1 | format_cell }}–{{ item.value.box.q3 | format_cell }}
{% endif %} {{ item.value | histogram_svg }}Each point is a row; red points are the flagged rows. Shows whether a flagged row is also unusual in context, not just on one column.
{{ artifact.metadata.description }}
{% endif %}| {{ column.label }} | {% endfor %}
|---|
| {{ row[column.key] | format_value }} | {% endfor %}
{{ table.row_count | format_value }} image files · {{ table.column_count | format_value }} manifest fields
{% else %}{{ table.row_count | format_value }} rows · {{ table.column_count | format_value }} columns · {{ table.duplicate_row_count | format_value }} duplicate row{% if table.duplicate_row_count != 1 %}s{% endif %}
{% endif %}{{ shape.descriptors | join('; ') }}
{% endif %} {% elif frequency %} {{ frequency | category_bars_svg }} {% elif timeline %} {{ timeline | timeline_svg }} {% elif column.top_values and column.row_count %}{% if item.value is none %}(missing){% else %}{{ item.value | string | truncate(24, true, '…') }}{% endif %} {{ "%.0f%%" | format(item.count / column.row_count * 100) }}{% endfor %}
No chart for this column.
{% endif %}{% if fit.family %} Closest standard distribution: {{ fit.label }}{% for name, value in fit.parameters.items() %}{% if loop.first %} ({% endif %}{{ name }}={{ value | format_value }}{% if loop.last %}){% else %}, {% endif %}{% endfor %} — {{ fit.quality }} fit, KS distance {{ "%.3f" | format(fit.ks_distance) }}. {% elif fit.reason == 'no_family_fits_well' %} No standard distribution fits this column well (closest is {{ fit.ranked[0].label }} at KS distance {{ "%.3f" | format(fit.best_distance) }}). {% elif fit.reason == 'too_few_values' %} Too few distinct values to fit a distribution. {% endif %} The KS distance is measured with parameters estimated from this same column, so it describes how close the fit is — it is not a hypothesis test, and no p-value is implied.
{% endif %} {% if frequency and frequency.other_category_count %}{{ frequency.other_category_count | format_value }} further label(s) are folded into “other”.
{% endif %}How strongly each pair of columns moves together, on a 0–1 scale. Three statistics share that scale because the columns are of different types: Spearman ρ between two numbers (signed, so the cell shows direction), Cramér's V between two categories, and the correlation ratio between a category and a number. Hover a cell for the exact figure and the statistic used. Association is not causation.
| Pair | Strength | Statistic | Reading |
|---|---|---|---|
| {{ pair.left }} & {{ pair.right }} | {% if pair.method == 'spearman' %}{{ "%+.2f" | format(pair.spearman) }}{% else %}{{ "%.2f" | format(pair.strength) }}{% endif %} | {% if pair.method == 'spearman' %}Spearman ρ{% if pair.pearson is not none %} (Pearson {{ "%+.2f" | format(pair.pearson) }}){% endif %}{% elif pair.method == 'cramers_v' %}Cramér's V{% else %}correlation ratio{% endif %} | {% if pair.strength >= 0.99 %}Effectively one variable.{% elif pair.method == 'spearman' %}Move {% if pair.spearman > 0 %}together{% else %}in opposition{% endif %}.{% else %}One largely determines the other.{% endif %} |
No pair reaches 0.80, so no column here is a restatement of another.
{% endif %}Each point is a row. The pairs below are the most strongly associated ones; use the selector to plot any other pair. A correlation number says how much two columns move together — a scatter says how, which is where curves, clusters, and ceilings show up.
association {{ "%.2f" | format(item.value.association) }}{% if item.value.point_count < item.value.available_point_count %} · {{ item.value.point_count | format_value }} of {{ item.value.available_point_count | format_value }} rows drawn{% endif %}
{{ item.value | scatter_svg(true) }}association {{ "%.2f" | format(item.value.association) }} · {{ item.value.point_count | format_value }} rows drawn
{{ item.value | scatter_svg(true) }}{{ missingness.complete_row_count | format_value }} of {{ missingness.row_count | format_value }} rows are complete. Bars past 20% are the ones the profile promotes to an issue.
How often two columns are blank on the same row. A high overlap usually means one optional section of a form, or a field added partway through collection — which is a different problem from values lost at random.
The first and last rows as they were loaded{% if sample.hidden_column_count %}, showing {{ sample.columns | length }} of {{ sample.columns | length + sample.hidden_column_count }} columns{% endif %}. Every summary above is a claim about these values; this is what they actually look like.
| # | {% for name in sample.columns %}{{ name }} | {% endfor %}
|---|---|
| {{ row.index }} | {% for value in row['values'] %}{{ value | format_cell }} | {% endfor %}
| ⋯ | {% for name in sample.columns %}⋯ | {% endfor %}
| {{ row.index }} | {% for value in row['values'] %}{{ value | format_cell }} | {% endfor %}
{{ duplicates[0].value.duplicate_row_count | format_value }} row(s) repeat an earlier row exactly. Examples:
| Copies | Row indexes | {% for name in duplicates[0].value.columns %}{{ name }} | {% endfor %}
|---|---|---|
| {{ group.row_count }} | {{ group.indexes | join(', ') }} | {% for value in group['values'] %}{{ value | format_cell }} | {% endfor %}
These are recommendations only. Prism EDA has not modified the data.
{{ step.table }}{% if step.columns %}.{{ step.columns | join(', ') }}{% endif %}: {{ step.rationale }}