-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdata-cleaner.html
More file actions
351 lines (309 loc) · 29.2 KB
/
Copy pathdata-cleaner.html
File metadata and controls
351 lines (309 loc) · 29.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
<!DOCTYPE html>
<html lang="en" class="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSV Data Cleaner & Column Transformer | STEMKit</title>
<meta name="title" content="CSV Data Cleaner & Column Transformer | STEMKit">
<meta name="description" content="Clean, normalize, and transform raw scientific datasets locally. Auto-detect missing values, apply Z-scores, and log-transform data directly in your browser.">
<meta name="keywords" content="csv data cleaner, column transformer, z-score calculator, min-max normalization, browser data processing, STEMKit">
<link rel="canonical" href="https://stemkit.net/data-cleaner.html">
<meta property="og:url" content="https://stemkit.net/data-cleaner.html">
<meta property="twitter:url" content="https://stemkit.net/data-cleaner.html">
<meta property="og:title" content="CSV Data Cleaner & Column Transformer | STEMKit">
<meta property="twitter:title" content="CSV Data Cleaner & Column Transformer | STEMKit">
<meta property="og:description" content="Clean, normalize, and transform raw scientific datasets locally without Python or MATLAB.">
<meta property="twitter:description" content="Clean, normalize, and transform raw scientific datasets locally without Python or MATLAB.">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "STEMKit Data Cleaner",
"url": "https://stemkit.net/data-cleaner.html",
"description": "Clean, normalize, and transform raw scientific datasets locally.",
"author": { "@type": "Person", "name": "Olanrewaju M. Daramola" },
"applicationCategory": "UtilityApplication",
"operatingSystem": "All",
"browserRequirements": "Requires JavaScript",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
"featureList": [ "Data Normalization", "Z-Score Processing", "Log Transformation", "Missing Value Handling" ]
}
</script>
<meta name="author" content="Olanrewaju M. Daramola">
<meta name="theme-color" content="#4f46e5">
<meta property="og:type" content="website">
<meta property="og:image" content="https://stemkit.net/assets/og-image.webp">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:image" content="https://stemkit.net/assets/og-image.webp">
<link rel="icon" type="image/x-icon" href="https://stemkit.net/assets/favicon.ico">
<link rel="icon" type="image/png" sizes="32x32" href="https://stemkit.net/assets/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://stemkit.net/assets/favicon-16x16.png">
<link rel="apple-touch-icon" href="https://stemkit.net/assets/apple-touch-icon.png">
<link rel="stylesheet" href="css/all.min.css">
<script src="js/dependencies/papaparse.min.js"></script>
<script type="application/ld+json">
{"@context": "https://schema.org", "@type": "HowTo", "name": "How to clean and transform a CSV", "totalTime": "PT2M", "step": [{"@type": "HowToStep", "position": 1, "name": "Load your file", "text": "Drag a .csv, .tsv or .txt file onto the drop zone, or click to browse. The delimiter is detected automatically."}, {"@type": "HowToStep", "position": 2, "name": "Pick a column & operation", "text": "Choose a target column (or all numeric columns) and a transformation: drop missing values, remove duplicates, log/ln, absolute value, min–max or Z-score."}, {"@type": "HowToStep", "position": 3, "name": "Execute & inspect", "text": "Click Execute. Live per-column statistics (mean, median, σ, min, max, missing) update so you can sanity-check the result."}, {"@type": "HowToStep", "position": 4, "name": "Export", "text": "Use Revert State to undo everything, or Export to download cleaned_dataset.csv."}]}
</script>
<script type="application/ld+json">
{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "Is my file uploaded to a server?", "acceptedAnswer": {"@type": "Answer", "text": "No. PapaParse reads the file in your browser and all processing stays in memory (RAM). Nothing is transmitted."}}, {"@type": "Question", "name": "Which file types work?", "acceptedAnswer": {"@type": "Answer", "text": "Comma-, tab- or semicolon-delimited text with a header row: .csv, .tsv or .txt. The delimiter is auto-detected."}}, {"@type": "Question", "name": "How is Z-Score calculated?", "acceptedAnswer": {"@type": "Answer", "text": "(value − column mean) / population standard deviation. Non-numeric and missing cells are ignored when computing the statistics."}}, {"@type": "Question", "name": "What does Min–Max do to a constant column?", "acceptedAnswer": {"@type": "Answer", "text": "If every value is identical there is no range to scale, so the column is set to 0."}}, {"@type": "Question", "name": "Why does the preview show only 100 rows?", "acceptedAnswer": {"@type": "Answer", "text": "Only the first 100 rows are rendered to keep the interface responsive; every row is still transformed and included in the export."}}]}
</script>
<link rel="stylesheet" href="src/home.css">
<link rel="stylesheet" href="src/output.css">
<link rel="stylesheet" href="src/stemkit-docs.css">
<link rel="stylesheet" href="src/tools/data-cleaner.css">
</head>
<body class="bg-slate-50 dark:bg-slate-900 text-slate-900 dark:text-slate-100 min-h-screen flex flex-col transition-colors duration-300">
<nav class="sticky top-0 z-50 w-full bg-white/80 dark:bg-slate-950/80 backdrop-blur-md border-b border-slate-200 dark:border-slate-800 transition-colors duration-300">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16 sm:h-20">
<a href="index.html" class="flex items-center gap-2 hover:opacity-80 transition-opacity">
<div class="bg-indigo-600 text-white p-2 sm:p-2.5 rounded-lg shadow-sm flex items-center justify-center">
<i class="fa-solid fa-flask text-lg sm:text-xl"></i>
</div>
<span class="text-xl sm:text-2xl font-black tracking-tighter text-slate-900 dark:text-white">STEMKit</span>
</a>
<div class="hidden md:flex items-center gap-6 text-sm font-semibold text-slate-600 dark:text-slate-300">
<a href="index.html#data-tools" class="hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">Data</a>
<a href="index.html#comp-tools" class="hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">Compute</a>
<a href="index.html#pub-tools" class="hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">Writing & Citations</a>
<a href="index.html#focus-tools" class="hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors">Focus</a>
<div class="h-5 w-px bg-slate-200 dark:bg-slate-700"></div>
<button class="themeToggle p-2 rounded-full hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors" aria-label="Toggle dark mode">
<i class="fa-solid fa-moon dark:hidden text-lg"></i>
<i class="fa-solid fa-sun hidden dark:block text-yellow-400 text-lg"></i>
</button>
<a href="plot-digitizer.html" class="bg-indigo-600 hover:bg-indigo-700 text-white px-5 py-2.5 rounded-full text-sm font-bold transition-all shadow-md hover:shadow-lg">
<i class="fa-solid fa-crosshairs mr-1"></i> Plot Digitizer
</a>
</div>
<div class="flex items-center gap-2 md:hidden">
<button class="themeToggle p-2 rounded-full hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors text-slate-600 dark:text-slate-300">
<i class="fa-solid fa-moon dark:hidden text-lg"></i>
<i class="fa-solid fa-sun hidden dark:block text-yellow-400 text-lg"></i>
</button>
<button id="mobile-menu-btn" class="text-slate-600 dark:text-slate-300 hover:text-indigo-600 p-2 focus:outline-none">
<i class="fa-solid fa-bars text-2xl" id="menu-icon"></i>
</button>
</div>
</div>
</div>
<div id="mobile-menu" class="hidden md:hidden bg-white/95 dark:bg-slate-900/95 backdrop-blur-md border-b border-slate-200 dark:border-slate-800 absolute w-full shadow-xl">
<div class="px-4 pt-2 pb-6 space-y-2 text-base font-semibold text-slate-600 dark:text-slate-300">
<a href="index.html#data-tools" class="mobile-link block py-3 px-4 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-indigo-600 transition-colors">Data & Stats</a>
<a href="index.html#comp-tools" class="mobile-link block py-3 px-4 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-indigo-600 transition-colors">Compute</a>
<a href="index.html#pub-tools" class="mobile-link block py-3 px-4 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-indigo-600 transition-colors">Writing & Citations</a>
<a href="index.html#focus-tools" class="mobile-link block py-3 px-4 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-indigo-600 transition-colors">Focus & Flow</a>
<div class="pt-4 mt-2 border-t border-slate-100 dark:border-slate-800">
<a href="plot-digitizer.html" class="block text-center bg-indigo-600 hover:bg-indigo-700 text-white py-3 rounded-xl shadow-md transition-colors">Digitizer Pro</a>
</div>
</div>
</div>
</nav>
<main class="flex-grow max-w-7xl mx-auto px-6 py-8 w-full flex flex-col gap-6">
<section id="uploadZone" class="flex-grow flex flex-col items-center justify-center border-2 border-dashed border-indigo-300 dark:border-indigo-700 rounded-3xl bg-indigo-50/50 dark:bg-indigo-900/10 hover:bg-indigo-50 dark:hover:bg-indigo-900/20 transition-all cursor-pointer p-12 text-center group">
<input type="file" id="fileInput" accept=".csv" class="hidden" aria-label="Upload CSV dataset">
<div class="w-20 h-20 bg-indigo-100 dark:bg-indigo-900/50 rounded-2xl flex items-center justify-center mb-6 text-indigo-600 dark:text-indigo-400 group-hover:scale-110 transition-transform">
<i class="fa-solid fa-file-csv text-4xl"></i>
</div>
<h1 class="text-3xl font-bold mb-3">CSV Data Cleaner</h1>
<p class="text-slate-500 dark:text-slate-400 mb-6 max-w-md">Drag and drop raw data arrays here. Execute mathematical operations and drop null values entirely in RAM.</p>
<button class="bg-indigo-600 hover:bg-indigo-700 text-white px-8 py-3 rounded-xl font-medium transition-colors shadow-sm">
Select CSV Dataset
</button>
</section>
<section id="workspace" class="hidden flex-col gap-6 w-full">
<div class="bg-white dark:bg-slate-950 p-6 rounded-2xl border border-slate-200 dark:border-slate-800 shadow-sm grid grid-cols-1 lg:grid-cols-12 gap-6 items-end">
<div class="lg:col-span-3">
<h2 id="fileName" class="font-bold text-lg truncate" title="Dataset Name">dataset.csv</h2>
<p id="dataMeta" class="text-sm text-slate-500 dark:text-slate-400">0 Rows • 0 Columns</p>
</div>
<div class="lg:col-span-7 flex flex-wrap gap-4 items-end bg-slate-50 dark:bg-slate-900 p-4 rounded-xl border border-slate-200 dark:border-slate-700">
<div class="flex-grow min-w-[150px]">
<label class="block text-xs font-bold text-slate-500 mb-1 uppercase tracking-wider">Target Column</label>
<select id="colSelect" class="w-full bg-white dark:bg-slate-950 border border-slate-300 dark:border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none"></select>
</div>
<div class="flex-grow min-w-[200px]">
<label class="block text-xs font-bold text-slate-500 mb-1 uppercase tracking-wider">Transformation</label>
<select id="opSelect" class="w-full bg-white dark:bg-slate-950 border border-slate-300 dark:border-slate-700 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-500 outline-none">
<option value="drop_na">Drop Missing Values (NaN)</option>
<option value="log10">Log10 Transform</option>
<option value="ln">Natural Log (ln)</option>
<option value="minmax">Min-Max Normalization (0-1)</option>
<option value="zscore">Z-Score Standardization</option>
<option value="abs">Absolute Value</option>
<option value="dedupe">Remove Duplicate Rows</option>
</select>
</div>
<button id="applyBtn" class="bg-slate-900 dark:bg-indigo-600 text-white px-5 py-2 rounded-lg font-medium hover:bg-slate-800 dark:hover:bg-indigo-700 transition-colors shadow-sm">
Execute
</button>
</div>
<div class="lg:col-span-2 flex justify-end">
<button id="exportBtn" class="bg-emerald-600 hover:bg-emerald-700 text-white px-5 py-2 rounded-lg font-medium transition-colors shadow-sm flex items-center w-full justify-center">
<i class="fa-solid fa-download mr-2"></i> Export
</button>
</div>
</div>
<div class="bg-white dark:bg-slate-950 border border-slate-200 dark:border-slate-800 rounded-2xl shadow-sm overflow-hidden flex flex-col h-[500px]">
<div class="bg-slate-50 dark:bg-slate-900 border-b border-slate-200 dark:border-slate-800 p-3 flex justify-between items-center">
<span class="text-sm font-semibold text-slate-600 dark:text-slate-300">Data Array Preview (100 Rows Max)</span>
<button id="resetDataBtn" class="text-xs text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 font-medium transition-colors">
<i class="fa-solid fa-rotate-left mr-1"></i> Revert State
</button>
</div>
<div id="colStats" class="hidden flex flex-wrap gap-x-5 gap-y-2 px-4 py-2 border-b border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-950 text-slate-600 dark:text-slate-300 empty:hidden"></div>
<div class="table-container overflow-auto flex-grow relative">
<table class="w-full text-left text-sm whitespace-nowrap">
<thead id="tableHead" class="bg-slate-100 dark:bg-slate-800 sticky top-0 shadow-sm"></thead>
<tbody id="tableBody" class="divide-y divide-slate-100 dark:divide-slate-800/50"></tbody>
</table>
</div>
</div>
</section>
<section class="stk-section" id="how-to-use" aria-label="How to use this tool">
<div class="stk-wrap">
<span class="stk-chip"><i class="fa-solid fa-broom"></i> How-To Guide</span>
<h2 class="stk-h2">How to clean and transform a CSV</h2>
<p class="stk-lead">Load a CSV (or TSV) file, apply column transformations and drop missing rows, then export a tidy dataset, without opening Python, R or a spreadsheet.</p>
<div class="stk-grid stk-grid-2">
<div class="stk-step"><span class="stk-num">1</span><div><h3>Load your file</h3><p>Drag a <code>.csv</code>, <code>.tsv</code> or <code>.txt</code> file onto the drop zone, or click to browse. The delimiter is detected automatically.</p></div></div><div class="stk-step"><span class="stk-num">2</span><div><h3>Pick a column & operation</h3><p>Choose a target column (or all numeric columns) and a transformation: drop missing values, remove duplicates, log/ln, absolute value, min–max or Z-score.</p></div></div><div class="stk-step"><span class="stk-num">3</span><div><h3>Execute & inspect</h3><p>Click <strong>Execute</strong>. Live per-column statistics (mean, median, σ, min, max, missing) update so you can sanity-check the result.</p></div></div><div class="stk-step"><span class="stk-num">4</span><div><h3>Export</h3><p>Use <strong>Revert State</strong> to undo everything, or <strong>Export</strong> to download <code>cleaned_dataset.csv</code>.</p></div></div>
</div>
</div>
</section>
<section class="stk-section" id="how-it-works" aria-label="How it works">
<div class="stk-wrap">
<h2 class="stk-h2">What each transformation does</h2>
<div class="stk-grid stk-grid-2">
<div class="stk-card"><h3 style="font-weight:700;margin-bottom:.4rem;">Normalisation</h3><p style="font-size:.88rem;color:#64748b;line-height:1.6;"><strong>Min–Max</strong> rescales a column to the 0–1 range: <code>(x − min)/(max − min)</code> (a constant column maps to 0). <strong>Z-Score</strong> standardises to mean 0 and standard deviation 1: <code>(x − mean)/σ</code>.</p></div><div class="stk-card"><h3 style="font-weight:700;margin-bottom:.4rem;">Cleaning & shaping</h3><p style="font-size:.88rem;color:#64748b;line-height:1.6;"><strong>Drop Missing Values</strong> removes rows with blanks in the selected column(s). <strong>Remove Duplicate Rows</strong> keeps only the first copy of identical rows. <strong>Log10 / ln</strong> apply only to positive values; <strong>Absolute Value</strong> takes the magnitude.</p></div>
</div>
<div class="stk-note" style="margin-top:1rem;">
<i class="fa-solid fa-circle-info" style="margin-top:.15rem;"></i>
<div><strong>Which standard deviation?</strong> Z-Score here uses the <em>population</em> standard deviation (divide by N), matching scikit-learn’s <code>StandardScaler</code>. Tools such as pandas default to the <em>sample</em> standard deviation (divide by N− 1), so values can differ slightly for small datasets.</div>
</div>
</div>
</section>
<section class="stk-section" id="method" aria-label="Method and equations">
<div class="stk-wrap" style="max-width:52rem;">
<span class="stk-chip"><i class="fa-solid fa-square-root-variable"></i> Method & Equations</span>
<h2 class="stk-h2">The math behind the transforms</h2>
<p class="stk-lead">The normalisation and standardisation operations follow standard <strong>feature-scaling</strong> definitions.</p>
<p class="stk-eqn-label">Min–Max normalisation rescales a column to the range [0, 1]:</p>
<div class="stk-math"><math display="block"><msup><mi>x</mi><mo>′</mo></msup><mo>=</mo><mfrac><mrow><mi>x</mi><mo>−</mo><msub><mi>x</mi><mi>min</mi></msub></mrow><mrow><msub><mi>x</mi><mi>max</mi></msub><mo>−</mo><msub><mi>x</mi><mi>min</mi></msub></mrow></mfrac></math></div>
<p class="stk-eqn-label">Z-Score standardisation centres to mean 0 and unit standard deviation:</p>
<div class="stk-math"><math display="block"><mi>z</mi><mo>=</mo><mfrac><mrow><mi>x</mi><mo>−</mo><mi>μ</mi></mrow><mi>σ</mi></mfrac><mo>,</mo><mspace width="1.4em"/><mi>μ</mi><mo>=</mo><mfrac><mn>1</mn><mi>N</mi></mfrac><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi>N</mi></mrow></munderover><msub><mi>x</mi><mi>i</mi></msub><mo>,</mo><mspace width="1.4em"/><mi>σ</mi><mo>=</mo><msqrt><mrow><mfrac><mn>1</mn><mi>N</mi></mfrac><munderover><mo>∑</mo><mrow><mi>i</mi><mo>=</mo><mn>1</mn></mrow><mrow><mi>N</mi></mrow></munderover><msup><mrow><mo>(</mo><msub><mi>x</mi><mi>i</mi></msub><mo>−</mo><mi>μ</mi><mo>)</mo></mrow><mn>2</mn></msup></mrow></msqrt></math></div>
<p class="stk-where">where <math><mi>x</mi></math> is a cell value, <math><msub><mi>x</mi><mi>min</mi></msub></math>/<math><msub><mi>x</mi><mi>max</mi></msub></math> the column extremes, <math><mi>μ</mi></math> the column mean, <math><mi>σ</mi></math> the <em>population</em> standard deviation (dividing by <math><mi>N</mi></math>), and <math><mi>N</mi></math> the count of numeric values. Non-numeric and missing cells are excluded from the statistics. A constant column (<math><msub><mi>x</mi><mi>max</mi></msub><mo>=</mo><msub><mi>x</mi><mi>min</mi></msub></math>) maps to 0. Log transforms apply only to positive values.</p>
<h3 class="stk-refs-h">References & documentation</h3>
<ol class="stk-refs-list">
<li>Pedregosa, F., et al. (2011). Scikit-learn: Machine learning in Python. <em>JMLR</em>, 12, 2825–2830. Docs: <a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html" target="_blank" rel="noopener">StandardScaler</a> · <a href="https://scikit-learn.org/stable/modules/preprocessing.html" target="_blank" rel="noopener">Preprocessing guide</a>.</li>
<li>Heckert, N. A., Filliben, J. J., Croarkin, C., et al. (2002). <em>NIST/SEMATECH e-Handbook of Statistical Methods</em>. <a href="https://www.itl.nist.gov/div898/handbook/" target="_blank" rel="noopener">itl.nist.gov/div898/handbook</a>.</li>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Feature_scaling" target="_blank" rel="noopener">Feature scaling</a> · <a href="https://en.wikipedia.org/wiki/Standard_score" target="_blank" rel="noopener">Standard score</a>.</li>
</ol>
</div>
</section>
<section class="stk-section" id="faq" aria-label="Frequently asked questions">
<div class="stk-wrap" style="max-width:52rem;">
<h2 class="stk-h2">Frequently asked questions</h2>
<div style="margin-top:1rem;">
<details class="stk-faq"><summary>Is my file uploaded to a server?</summary><div>No. PapaParse reads the file in your browser and all processing stays in memory (RAM). Nothing is transmitted.</div></details><details class="stk-faq"><summary>Which file types work?</summary><div>Comma-, tab- or semicolon-delimited text with a header row: .csv, .tsv or .txt. The delimiter is auto-detected.</div></details><details class="stk-faq"><summary>How is Z-Score calculated?</summary><div>(value − column mean) / population standard deviation. Non-numeric and missing cells are ignored when computing the statistics.</div></details><details class="stk-faq"><summary>What does Min–Max do to a constant column?</summary><div>If every value is identical there is no range to scale, so the column is set to 0.</div></details><details class="stk-faq"><summary>Why does the preview show only 100 rows?</summary><div>Only the first 100 rows are rendered to keep the interface responsive; every row is still transformed and included in the export.</div></details>
</div>
<div class="stk-refs">
<a href="https://en.wikipedia.org/wiki/Feature_scaling" target="_blank" rel="noopener"><i class="fa-solid fa-ruler"></i> Feature scaling (min–max, standardization)</a><a href="https://en.wikipedia.org/wiki/Standard_score" target="_blank" rel="noopener"><i class="fa-solid fa-chart-simple"></i> Standard score (Z-score)</a><a href="https://www.papaparse.com/" target="_blank" rel="noopener"><i class="fa-solid fa-file-csv"></i> PapaParse CSV library</a>
</div>
<p class="stk-credit">Runs entirely in your browser, no data leaves your device.</p>
</div>
</section>
</main>
<footer class="mt-auto border-t border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-950 py-12">
<div class="max-w-7xl mx-auto px-6">
<div class="stk-dir mb-12">
<div>
<h4><i class="fa-solid fa-chart-pie text-indigo-500 text-xs"></i> Data & Stats</h4>
<ul>
<li><a href="plot-digitizer.html">Plot Digitizer</a></li>
<li><a href="data-cleaner.html">Data Cleaner</a></li>
<li><a href="stats-calculator.html">Stat Calculator</a></li>
<li><a href="error-bar-generator.html">Error Bar Gen</a></li>
<li><a href="outlier-detector.html">Outlier Detector</a></li>
<li><a href="curve-fitter.html">Curve Fitter</a></li>
<li><a href="plot-builder.html">Plot Builder</a></li>
<li><a href="xvg-visualizer.html">XVG Visualizer</a></li>
</ul>
</div>
<div>
<h4><i class="fa-solid fa-microchip text-orange-500 text-xs"></i> Compute</h4>
<ul>
<li><a href="structure-inspector.html">3D Inspector</a></li>
<li><a href="coordinate-manipulator.html">Coordinate Manipulator</a></li>
<li><a href="scientific-converter.html">Energy Conversions</a></li>
<li><a href="script-generator.html">HPC Script Gen</a></li>
</ul>
</div>
<div>
<h4><i class="fa-solid fa-book-open text-teal-500 text-xs"></i> Writing & Citations</h4>
<ul>
<li><a href="latex-formatter.html">Equation Formatter</a></li>
<li><a href="latex-tables.html">Visual LaTeX Tables</a></li>
<li><a href="doi-fetcher.html">DOI to BibTeX</a></li>
<li><a href="bibtex-deduplicator.html">BibTeX Deduplicator</a></li>
<li><a href="bibtex-sanitizer.html">BibTeX Sanitizer</a></li>
<li><a href="journal-abbreviator.html">Journal Abbrev</a></li>
</ul>
</div>
<div>
<h4><i class="fa-solid fa-headphones text-fuchsia-500 text-xs"></i> Focus & Flow</h4>
<ul>
<li><a href="pomodoro.html">Ambient Pomodoro</a></li>
<li><a href="decision.html">Decision Matrix</a></li>
<li><a href="sandbox.html">Kinetic Sandbox</a></li>
</ul>
</div>
</div>
<div class="border-t border-slate-200 dark:border-slate-800 pt-8 flex flex-col md:flex-row items-center justify-between text-slate-500 dark:text-slate-400 gap-4">
<div class="flex items-center gap-2">
<i class="fa-solid fa-flask text-xl text-slate-300 dark:text-slate-700"></i>
<span class="font-bold text-slate-700 dark:text-slate-300">STEMKit</span>
</div>
<div class="text-center text-sm font-medium">© 2026 STEMKit. Built by <a href="https://github.com/LD-Shell" target="_blank" rel="noopener" class="font-semibold hover:text-indigo-500 transition-colors">Olanrewaju M. Daramola</a>.</div>
<div class="flex gap-6 text-sm font-medium">
<a href="privacy.html#privacy" class="hover:text-indigo-500 transition-colors">Privacy Policy</a>
<a href="privacy.html#terms" class="hover:text-indigo-500 transition-colors">Terms of Service</a>
</div>
</div>
</div>
</footer>
<div id="toastContainer" class="fixed bottom-6 right-6 z-50 flex flex-col gap-3"></div>
<script>
// 1. Theme toggle logic (Mobile & Desktop)
document.querySelectorAll('.themeToggle').forEach(btn => {
btn.addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
});
});
// 2. Mobile menu logic
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
const mobileMenu = document.getElementById('mobile-menu');
const menuIcon = document.getElementById('menu-icon');
if(mobileMenuBtn) {
mobileMenuBtn.addEventListener('click', () => {
mobileMenu.classList.toggle('hidden');
if(mobileMenu.classList.contains('hidden')) {
menuIcon.classList.remove('fa-xmark');
menuIcon.classList.add('fa-bars');
} else {
menuIcon.classList.remove('fa-bars');
menuIcon.classList.add('fa-xmark');
}
});
document.querySelectorAll('.mobile-link').forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.add('hidden');
menuIcon.classList.remove('fa-xmark');
menuIcon.classList.add('fa-bars');
});
});
}
</script>
<script type="module" src="js/data-cleaner.js"></script>
</body>
</html>