One focused Python exercise per day. Each file is self-contained and runnable: python dayNN_topic.py. Standard library only unless noted.
| Day | File | What it shows |
|---|---|---|
| 01 | day01_data_quality_checker.py | Column-level CSV quality checks: nulls, duplicate keys, numeric ranges, regex formats |
| 02 | day02_reconcile_datasets.py | Source-vs-target reconciliation: key set diff, duplicate keys, field-level mismatches, match rate |
| 03 | day03_log_parser.py | Web-server log parsing with one named-group regex: request counts, status distribution, error rate, slowest endpoints, top talkers |
| 04 | day04_rag_chunker.py | Overlapping text chunking for RAG: fixed word-size windows with overlap, splitting on paragraph/sentence boundaries so ideas stay whole |
| 05 | day05_pytorch_tensors.py | PyTorch tensor fundamentals: creation (rand/tensor/ones/zeros), rank & shape, indexing, element-wise math, .item(), NumPy bridge, device selection, and a first look at autograd (uses torch + numpy) |
| 06 | day06_sql_with_sqlite.py | SQL via sqlite3: INNER/LEFT joins, GROUP BY/HAVING, and data-validation queries (orphan records, duplicates, business-rule checks) on a tiny claims DB |
| 07 | day07_autograd_training_loop.py | PyTorch autograd: manual linear-regression training loop (forward, MSE, backward(), hand-written SGD, grad zeroing, un-standardizing learned weights). No nn.Module, no optimizer (uses torch) |
| 08 | day08_csv_json_wrangler.py | CSV to JSON wrangler: best-effort type coercion (int/float/string/None), required-field validation that skips bad rows with line-numbered errors instead of crashing, stable-key JSON output with round-trip check |
| 09 | day09_forecast_table.py | National forecast table parser: regex tokenizing of packed "hi/lo/sky" cells with a code legend, dataclasses for today vs next-day, rejects malformed rows instead of crashing, and reports hottest/coolest/widest-swing cities and sky distribution |
| 10 | day10_eval_metrics.py | Evaluation metrics from scratch: precision/recall/F1 with zero-division handling, confusion matrix, macro averaging, sklearn-style report, and a spam-filter demo showing why accuracy flatters lazy models on imbalanced data |
| 11 | day11_one_hot_encoding.py | One-hot encoding from scratch (Low-Code AI, Stripling & Abel): why ordinal codes invent a false magnitude, stable category ordering between train and serve, and unseen categories encoding as all-zeros instead of crashing |
| 12 | day12_mode_imputation.py | Mode imputation for missing categorical values (Low-Code AI, ch. 2): deletion vs imputation, the many spellings of "missing" in real CSVs, deterministic alphabetical tie-breaking, and returning None when a column is entirely missing |
| 13 | day13_feature_scaling.py | Min-max scaling and its two failure modes (Low-Code AI, Stripling & Abel, p. 24): one outlier crushing every real value to 0, a constant column dividing by zero, and median/IQR robust scaling as the alternative |
| 14 | day14_musk_token_economics.py | Tokenizer byte economics (Hands-On LLM, pp. 74/78/82): GPT-style byte fallback means a brand name in Telugu or Hindi bills 3-6x its English cost; measures Musk company names across 6 market scripts, with xAI as the 1.00x control |
| 15 | day15_gil_threads_vs_processes.py | The global interpreter lock measured (Dive into Deep Learning, p. 270): the same CPU-bound work run serially, on threads and on processes, where threads give no speedup at all (0.90x to 1.04x across four runs) while processes give 2.08x, then the same three ways on I/O-bound work where the same threads give 3.97x. verify_day15.py re-times everything independently and asserts both claims. Same tool, opposite result, depending on what the work is waiting for |
| 16 | day16_validation_selection_bias.py | The third dataset (Low-Code AI, Stripling & Abel, PDF pp. 268-269): labels are coin flips, so 0.500 is the honest ceiling. Pick the best of 60 candidate models on one validation set and it scores 0.610 there against 0.507 on the untouched test set - but one run proves nothing, since a single test set is noisy enough to drift a point either way. Over 200 runs the winner averages 0.606 validation against 0.498 test, flattering in 200/200 runs by +0.107. Rerun on data with real signal and the gap falls to +0.011 |
| 17 | day17_drift_alarm_precision.py | Data-drift monitoring scored on false alarms (Practical MLOps, Gift & Deza, ch. 6, PDF pp. 200-204): the book's auto-suggested constraint baseline fires on a harmless 99.7%-integral batch and is silent on a Fahrenheit-to-Celsius unit swap, scoring precision 0.50 / recall 0.33 across six hourly batches; a 99%-tolerance detector with a mean-shift check gets 0.75 / 1.00. The remaining false alarm is not fixable: the seasonal-zero and dead-feed batches profile identically, so a data-only detector fires on both (0.75 precision) or neither (0.67 recall). VS Code run |
| 18 | day18_duplicate_leakage.py | "No duplicates" as a cleaning checkbox (Ribeiro, Complete lifecycle of ML models Part 2, ch. 3, PDF pp. 36 & 44): df.drop_duplicates() removes only rows that are equal in every column, so on 600 rows holding 200 re-entered customers it drops the 50 exact copies and, once every row carries a real primary key, drops 0 of 600 while all 200 stay. The re-keyed, rounded and whitespace-typed copies survive either way, and a random split then puts 30% of test rows in the same customer as a training row. Averaged over 10 splits, 1-NN reports 0.726 on the random split against 0.606 on a customer-grouped split of the same cleaned data - a +0.120 memory bonus, on a task whose Bayes ceiling is 0.75 and whose majority baseline is 0.505 |
| 18b | book_check_fillna_inplace.py | The same book's cleaning block run verbatim on current pandas (Data Engineering Made Simple: SQL, Python, PySpark, ch. 7, p. 41): patients['Diagnosis'].fillna('Unknown', inplace=True) fills 3 of 3 nulls on pandas 2.3.1 with only a FutureWarning, and fills 0 of 3 under Copy-on-Write - the pandas 3.0 default - raising ChainedAssignmentError and leaving the frame untouched. The chained selection is filled and discarded. Assigning the result back works in both modes. VS Code run |
| — | labelencoder_alphabet.py | LabelEncoder assigns the positive class by the alphabet, and the book's own fix does not fix it (Low-Code AI, ch. 6, PDF p. 247). The book says "Yes is being treated as the positive class, or 1" - true only because Y sorts after N. On one identical model over 5,000 rows, recall reads 0.544 when the event word sorts last and 0.954 when it sorts first: a fraud detector finding 54% of fraud reports 95%, because sklearn's metrics default to pos_label=1 and are quietly scoring the legitimate rows. The book's aside - "ensure the order by fitting on ['No','Yes']" - was tested both ways on scikit-learn 1.8.0 and changes nothing, because LabelEncoder sorts whatever you fit it on. Naming the class at the metric restores 0.544. VS Code run |
| — | keras_lr_silent_default.py | The book's compile line does not run, and the obvious repair is not the book's model (Deep Learning Illustrated, Krohn, Beyleveld & Bassens, ch. 8, Examples 8.1-8.2, pp. 127-128). SGD(lr=0.1) raises ValueError: Argument(s) not recognized: {'lr': 0.1} on Keras 3.15.1 - lr was removed. The message names the argument it rejected but not learning_rate, which replaced it, so "not recognized" reads like "delete this". Deleting it is silent: SGD falls back to learning_rate=0.01, a tenth of the book's value, with no warning and no error. Trained on MNIST at seed 19 for the book's 20 epochs, keeping 0.1 gives val_acc 0.9752 and dropping the argument gives 0.9467, a 0.0285 gap - and 10 epochs to reach what the book's rate reaches in 1. The architecture itself is untouched: 4,160 parameters in the second Dense layer, exactly as printed on p. 127, and the book's own figures still hold (92.34% to a measured 92.78% at epoch 1, ~97.6% to 97.52% at epoch 20). The book was right. Its code just stopped running. VS Code run |
| — | kmeans_ninit_default.py | The book never passes n_init, so its line inherits scikit-learn's default — and that default changed from 10 to 'auto' in v1.4 (50 Algorithms Every Programmer Should Know, Imran Ahmad, Packt, ch. 6, Unsupervised_Machine_Learning_Algorithms.ipynb cell 5: cluster.KMeans(n_clusters=2)). Fitted rather than read from the docs, 'auto' resolves to 1 under the default init='k-means++' and to 10 under init='random' — one default silently depending on another argument the book also never sets. k-means is only guaranteed a local optimum per restart, so on UCI handwritten digits (1797x64, k=10, ships with scikit-learn; directory source Datascience public datasets.pdf p.23) across 30 seeds the printed line lands worse on 28/30, median gap +0.40%, worst seed +4.58%. Inertia spread widens from 637.2 to 53,470.4 — the same line is 84x less stable — and mean ARI against the true digit falls 0.6677 to 0.6378, worst-seed ARI 0.6603 to 0.5628. No error and no warning in either direction. The change bought real speed (4.66s to 1.36s for 30 fits, 3.4x), so the repair is not to revert it but to write the default down: KMeans(n_clusters=10, n_init=10). VS Code run |