Consistency pass over the parallel-agent work: - Replace 4 divergent inline header wrappers (flex/inline-flex, gap 10/12px, margin-top present/absent across 8 tool pages) with one shared .dt-tool-header-actions class; strip the now-redundant per-button margin-top:0. Every tool header now aligns the local-first pill + Help button identically. - Reconcile downloads row: reorder to the page's exceptions-first order (Review, Unmatched left, Unmatched right, Matched) to match the tabs and metric strip, and drop the lone competing primary — the four are parallel exports of equal weight. Audited and confirmed already-consistent: compact intake banner, privacy pill markup, .dt-next-step strips, the three coming-soon stubs, primary CTAs, and the 3-download CSV/audit/config pattern. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
264 lines
16 KiB
HTML
264 lines
16 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>Layout review — Fix Missing Values</title>
|
|
<link rel="stylesheet" href="app.css">
|
|
</head>
|
|
<body data-page="04_missing_handler">
|
|
<div class="dt-app">
|
|
<aside class="dt-sidebar" id="dt-sidebar"></aside>
|
|
<main class="dt-main">
|
|
<div class="dt-review-banner">
|
|
<span class="dt-mi">visibility</span>
|
|
<span>Static layout preview of <strong>Fix Missing Values</strong>, shown with a file imported and a completed run (per-column missingness profile + before/after results). <a href="index.html">All pages →</a></span>
|
|
</div>
|
|
<div class="dt-main-inner">
|
|
|
|
<!-- Tool header -->
|
|
<div class="dt-tool-header">
|
|
<h1>Fix Missing Values</h1>
|
|
<div class="dt-tool-header-actions">
|
|
<span class="dt-privacy-pill">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
|
<path d="M8 11V7a4 4 0 018 0v4"/>
|
|
</svg>
|
|
Runs 100% locally
|
|
</span>
|
|
<button class="dt-help-btn"><span class="dt-mi">help_outline</span> Help</button>
|
|
</div>
|
|
</div>
|
|
<p class="dt-tool-caption">Find blank cells (even hidden ones) and fill them in or remove them.</p>
|
|
|
|
<div class="dt-spacer"></div>
|
|
|
|
<!-- File pickup banner (using file from upload screen) -->
|
|
<div class="dt-alert info">
|
|
<span class="dt-mi">description</span>
|
|
<span>Using <strong>survey_responses.csv</strong> from the upload screen.</span>
|
|
</div>
|
|
<button class="dt-btn" style="margin-bottom:4px">Use a different file</button>
|
|
|
|
<!-- Preview expander (collapsed after a result exists) -->
|
|
<details class="dt-expander">
|
|
<summary>Preview: survey_responses.csv</summary>
|
|
<div class="dt-expander-body">
|
|
<p class="dt-caption">2,150 rows, 6 columns</p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th class="idx"></th><th>respondent_id</th><th>age</th><th>region</th><th>income</th><th>satisfaction</th><th>comments</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="idx">0</td><td>R-1001</td><td>34</td><td>West</td><td>52000</td><td>4</td><td>great service</td></tr>
|
|
<tr><td class="idx">1</td><td>R-1002</td><td class="dt-cell-flag">N/A</td><td>East</td><td class="dt-cell-flag"></td><td>3</td><td class="dt-cell-flag">?</td></tr>
|
|
<tr><td class="idx">2</td><td>R-1003</td><td>41</td><td class="dt-cell-flag">-</td><td>61000</td><td class="dt-cell-flag">NULL</td><td>none</td></tr>
|
|
<tr><td class="idx">3</td><td>R-1004</td><td>29</td><td>South</td><td class="dt-cell-flag">N/A</td><td>5</td><td>quick</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- Missingness profile — always visible: see the damage before configuring -->
|
|
<h2>Missingness profile</h2>
|
|
<div class="dt-metrics">
|
|
<div class="dt-metric"><div class="label">Rows</div><div class="value">2,150</div></div>
|
|
<div class="dt-metric"><div class="label">Cells missing</div><div class="value">1,043</div></div>
|
|
<div class="dt-metric"><div class="label">% cells missing</div><div class="value">8.1%</div></div>
|
|
<div class="dt-metric"><div class="label">Complete rows</div><div class="value">1,388</div></div>
|
|
</div>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th>column</th><th>dtype</th><th>missing</th><th>missing_pct</th><th>disguised</th><th>has_missing</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>respondent_id</td><td>object</td><td>0</td><td>0.0%</td><td>0</td><td>False</td></tr>
|
|
<tr><td>age</td><td>float64</td><td>187</td><td>8.7%</td><td>61</td><td>True</td></tr>
|
|
<tr><td>region</td><td>object</td><td>142</td><td>6.6%</td><td>142</td><td>True</td></tr>
|
|
<tr><td>income</td><td>float64</td><td>329</td><td>15.3%</td><td>118</td><td>True</td></tr>
|
|
<tr><td>satisfaction</td><td>float64</td><td>95</td><td>4.4%</td><td>40</td><td>True</td></tr>
|
|
<tr><td>comments</td><td>object</td><td>290</td><td>13.5%</td><td>290</td><td>True</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- Options expander (Strategy) — configuration follows the diagnostic -->
|
|
<details class="dt-expander" open>
|
|
<summary>Options</summary>
|
|
<div class="dt-expander-body">
|
|
|
|
<h3>Strategy</h3>
|
|
<div class="dt-precedence">
|
|
<span class="dt-mi">layers</span>
|
|
<span>Resolution order: <strong>per-column override</strong> → <strong>global strategy</strong> → <strong>preset</strong>. The most specific setting wins; layers it overrides are dimmed.</span>
|
|
</div>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Preset</label>
|
|
<div class="dt-help-text" style="color:var(--warn);display:flex;align-items:center;gap:5px;margin-bottom:8px"><span class="dt-mi" style="font-family:'Material Symbols Outlined';font-size:15px;line-height:1">info</span> Overridden by <strong>Global strategy → median</strong> (set under Advanced options). Presets apply only when global is “(use preset)”.</div>
|
|
<div class="dt-radio-row is-overridden" style="flex-direction:column;gap:10px">
|
|
<span class="dt-radio on"><span class="dot"></span> detect-only (standardize sentinels to NaN, no fill or drop)</span>
|
|
<span class="dt-radio"><span class="dot"></span> safe-fill (numeric → median, categorical → mode)</span>
|
|
<span class="dt-radio"><span class="dot"></span> drop-incomplete (drop any row with missing)</span>
|
|
</div>
|
|
<div class="dt-help-text">detect-only: replace 'N/A', '-', 'NULL', etc. with real NaN, then stop. safe-fill: also fill — numeric columns with median, others with mode. drop-incomplete: also drop every row that has any missing cell.</div>
|
|
</div>
|
|
|
|
<!-- Advanced options expander (open — most informative) -->
|
|
<details class="dt-expander" open>
|
|
<summary>Advanced options</summary>
|
|
<div class="dt-expander-body">
|
|
<div class="dt-cols-2">
|
|
<div>
|
|
<h4>Detection</h4>
|
|
<div class="dt-check on"><span class="box"><span class="dt-mi">check</span></span> Standardize disguised nulls to NaN</div>
|
|
<div class="dt-field">
|
|
<label class="dt-label" title="Sentinel values">Blanks in disguise (N/A, dash, NULL) — comma-separated</label>
|
|
<div class="dt-input">N/A, n/a, NA, NULL, null, None, -, --, ?, #N/A</div>
|
|
<div class="dt-help-text">Text that really means “empty.” Matched case-insensitively after stripping whitespace.</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<h4>Strategy override</h4>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Global strategy</label>
|
|
<div class="dt-select">median</div>
|
|
<div class="dt-help-text">drop_row / drop_col use the thresholds below. mean / median / interpolate are numeric only — non-numeric columns fall back to the categorical strategy.</div>
|
|
</div>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Categorical fallback (for non-numeric columns)</label>
|
|
<div class="dt-select">mode</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h4>Drop thresholds</h4>
|
|
<div class="dt-cols-2">
|
|
<div class="dt-field">
|
|
<label class="dt-label">Row drop threshold (drop rows with ≥ this fraction missing across selected cols)</label>
|
|
<div class="dt-slider"><div class="track"><div class="fill" style="width:100%"></div><div class="knob" style="left:calc(100% - 8px)"></div></div><div class="val">1.00</div></div>
|
|
</div>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Column drop threshold (drop columns with ≥ this fraction missing)</label>
|
|
<div class="dt-slider"><div class="track"><div class="fill" style="width:100%"></div><div class="knob" style="left:calc(100% - 8px)"></div></div><div class="val">1.00</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<h4>Scope</h4>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Columns to handle (default: all)</label>
|
|
<div class="dt-multiselect">
|
|
<span class="dt-ms-chip">respondent_id <span class="x">✕</span></span>
|
|
<span class="dt-ms-chip">age <span class="x">✕</span></span>
|
|
<span class="dt-ms-chip">region <span class="x">✕</span></span>
|
|
<span class="dt-ms-chip">income <span class="x">✕</span></span>
|
|
<span class="dt-ms-chip">satisfaction <span class="x">✕</span></span>
|
|
<span class="dt-ms-chip">comments <span class="x">✕</span></span>
|
|
</div>
|
|
</div>
|
|
<div class="dt-field">
|
|
<label class="dt-label">Columns to skip</label>
|
|
<div class="dt-multiselect"><span class="dt-ms-placeholder">Choose columns</span></div>
|
|
</div>
|
|
|
|
<h4>Per-column strategy overrides (optional)</h4>
|
|
<p class="dt-caption">Set a different strategy for specific columns. Leave any row blank to use the global strategy.</p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th>Column</th><th>Override</th><th>Resolves to</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>age</td><td><span class="dt-select" style="display:inline-block;min-width:160px;padding:4px 24px 4px 10px;color:var(--ink-tertiary)">(global)</span></td><td>median <span style="color:var(--ink-tertiary);font-size:11px">· global</span></td></tr>
|
|
<tr><td>region</td><td><span class="dt-select" style="display:inline-block;min-width:160px;padding:4px 24px 4px 10px;color:var(--ink-tertiary)">(global)</span></td><td>mode <span style="color:var(--ink-tertiary);font-size:11px">· global → categorical fallback</span></td></tr>
|
|
<tr><td>income</td><td><span class="dt-select" style="display:inline-block;min-width:160px;padding:4px 24px 4px 10px;color:var(--ink-tertiary)">(global)</span></td><td>median <span style="color:var(--ink-tertiary);font-size:11px">· global</span></td></tr>
|
|
<tr><td>satisfaction</td><td><span class="dt-select" style="display:inline-block;min-width:160px;padding:4px 24px 4px 10px;color:var(--ink-tertiary)">(global)</span></td><td>median <span style="color:var(--ink-tertiary);font-size:11px">· global</span></td></tr>
|
|
<tr><td>comments</td><td><span class="dt-select" style="display:inline-block;min-width:160px;padding:4px 24px 4px 10px">constant</span></td><td><strong>constant</strong> <span style="color:var(--ink-tertiary);font-size:11px">· this column</span></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
</div>
|
|
</details>
|
|
|
|
<hr class="dt-divider">
|
|
<button class="dt-btn dt-btn-primary dt-btn-block">Handle Missing Values</button>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- Results -->
|
|
<div id="missing-results-anchor"></div>
|
|
<h2>Results</h2>
|
|
<div class="dt-metrics">
|
|
<div class="dt-metric"><div class="label">Sentinels → NaN</div><div class="value">651</div></div>
|
|
<div class="dt-metric"><div class="label">Cells filled</div><div class="value">1,043</div></div>
|
|
<div class="dt-metric"><div class="label">Rows dropped</div><div class="value">0</div></div>
|
|
<div class="dt-metric"><div class="label">Columns dropped</div><div class="value">0</div></div>
|
|
</div>
|
|
|
|
<p><strong>Missingness — before vs. after</strong></p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th>column</th><th>before_missing</th><th>before_pct</th><th>after_missing</th><th>after_pct</th><th>strategy</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>respondent_id</td><td>0</td><td>0.0</td><td>0</td><td>0.0</td><td class="dt-cell-flag">—</td></tr>
|
|
<tr><td>age</td><td class="dt-cell-flag">187</td><td>8.7</td><td class="dt-cell-add">0</td><td class="dt-cell-add">0.0</td><td>median</td></tr>
|
|
<tr><td>region</td><td class="dt-cell-flag">142</td><td>6.6</td><td class="dt-cell-add">0</td><td class="dt-cell-add">0.0</td><td>mode</td></tr>
|
|
<tr><td>income</td><td class="dt-cell-flag">329</td><td>15.3</td><td class="dt-cell-add">0</td><td class="dt-cell-add">0.0</td><td>median</td></tr>
|
|
<tr><td>satisfaction</td><td class="dt-cell-flag">95</td><td>4.4</td><td class="dt-cell-add">0</td><td class="dt-cell-add">0.0</td><td>median</td></tr>
|
|
<tr><td>comments</td><td class="dt-cell-flag">290</td><td>13.5</td><td class="dt-cell-add">0</td><td class="dt-cell-add">0.0</td><td>constant</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<p><strong>Audit (first 50 changes)</strong></p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th>row</th><th>column</th><th>old_value</th><th>new_value</th><th>reason</th></tr></thead>
|
|
<tbody>
|
|
<tr><td>2</td><td>age</td><td class="dt-cell-flag">N/A</td><td class="dt-cell-add">37.0</td><td>fill: median</td></tr>
|
|
<tr><td>2</td><td>income</td><td class="dt-cell-flag">(blank)</td><td class="dt-cell-add">54000.0</td><td>fill: median</td></tr>
|
|
<tr><td>2</td><td>comments</td><td class="dt-cell-flag">?</td><td class="dt-cell-add">(no comment)</td><td>fill: constant</td></tr>
|
|
<tr><td>3</td><td>region</td><td class="dt-cell-flag">-</td><td class="dt-cell-add">West</td><td>fill: mode</td></tr>
|
|
<tr><td>3</td><td>satisfaction</td><td class="dt-cell-flag">NULL</td><td class="dt-cell-add">4.0</td><td>fill: median</td></tr>
|
|
<tr><td>4</td><td>income</td><td class="dt-cell-flag">N/A</td><td class="dt-cell-add">54000.0</td><td>fill: median</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<p class="dt-caption">… and 1,037 more (download the full audit below).</p>
|
|
|
|
<p><strong>Handled preview (first 10 rows)</strong></p>
|
|
<div class="dt-table-wrap">
|
|
<table class="dt-table">
|
|
<thead><tr><th class="idx"></th><th>respondent_id</th><th>age</th><th>region</th><th>income</th><th>satisfaction</th><th>comments</th></tr></thead>
|
|
<tbody>
|
|
<tr><td class="idx">0</td><td>R-1001</td><td>34.0</td><td>West</td><td>52000.0</td><td>4.0</td><td>great service</td></tr>
|
|
<tr><td class="idx">1</td><td>R-1002</td><td class="dt-cell-add">37.0</td><td>East</td><td class="dt-cell-add">54000.0</td><td>3.0</td><td class="dt-cell-add">(no comment)</td></tr>
|
|
<tr><td class="idx">2</td><td>R-1003</td><td>41.0</td><td class="dt-cell-add">West</td><td>61000.0</td><td class="dt-cell-add">4.0</td><td>none</td></tr>
|
|
<tr><td class="idx">3</td><td>R-1004</td><td>29.0</td><td>South</td><td class="dt-cell-add">54000.0</td><td>5.0</td><td>quick</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<hr class="dt-divider">
|
|
|
|
<!-- Downloads (html_download_button anchors) -->
|
|
<div class="dt-cols-3">
|
|
<button class="dt-btn dt-btn-primary">Download handled CSV</button>
|
|
<button class="dt-btn">Download changes audit</button>
|
|
<button class="dt-btn">Download config JSON</button>
|
|
</div>
|
|
|
|
<div class="dt-next-step"><span class="dt-mi">arrow_forward</span><span>Missing values handled. Next, most files need: <a href="01_deduplicator.html">Find Duplicates →</a></span><button class="dt-next-step-dismiss" title="Dismiss">✕</button></div>
|
|
|
|
</div>
|
|
</main>
|
|
</div>
|
|
<footer class="dt-footer" id="dt-footer"></footer>
|
|
<script src="shell.js"></script>
|
|
</body>
|
|
</html>
|