Wrangling 14,000 Handwritten Ward Records: Lessons in Clinical Data Cleaning at COLTECH
Real medical data in Cameroon does not arrive in tidy Parquet files. It arrives as smudged paper ledger transcriptions with 19 different spellings of 'Typhoid'. How we built an automated fuzzy normalization pipeline.
When data science textbooks teach exploratory data analysis (EDA), they start with clean datasets like MNIST, Titanic, or Kaggle CSVs where missing values are conveniently tagged as NaN.
When you walk into a clinical records archive at a district hospital in Cameroon to conduct epidemiological research for an M.Tech in Data Science at COLTECH (University of Bamenda), you are handed stacks of mold-scented hardcover ledgers.
Junior nurses and data entry clerks have manually transcribed 14,000 patient admission rows across 36 months under erratic desk lamps and power outages.
Here is what “clinical data” actually looks like when it hits your Python script:
- 19 Different Spellings for One Pathogen: “Typhoid Fever”, “typhoid fev”, “Thypoid”, “Fièvre Typhoïde”, “Fièvre typhoide”, “T.F.”, “typhoid +++”, “Salmonellosis (typh)”.
- Scrambled Date Formats:
12/04/2024on line 1 isApril 12(US format);12/04/2024on line 4 isDecember 4(French/British format). - Freeform Laboratory Units: A hemoglobin value is recorded on one page as
11.4 g/dL, on the next as114 g/L, and on the next as simply11.4or74%.
If you feed this raw text directly into Scikit-learn or PyTorch, your model will hallucinate correlations between clerical typos and mortality rates.
Here is the automated fuzzy cleaning and clinical entity normalization pipeline we built to restore signal from noise.
The 4-Stage Normalization Pipeline
Raw Ward Entry ──> 1. Bilingual Text Sanitization ──> 2. Phonetic Metaphone & Levenshtein ──> 3. Fuzzy ICD-11 Mapping ──> 4. Pydantic Strict Validation
- Bilingual Sanitization: Strips French diacritics (é, è, ê, ô, ç), expands local medical acronyms (GE -> Gastroenteritis, UTI -> Urinary Tract Infection, PUD -> Peptic Ulcer Disease).
- Double Metaphone & Levenshtein Matching: Uses phonetic encoding so that “Thypoid” and “Typhoid” resolve to the exact same phonetic key (
0FT/TFT). - ICD-11 Terminology Anchors: Binds every normalized diagnosis to an official WHO International Classification of Diseases (ICD-11) code with confidence thresholding (e.g.
1A07.0for Typhoid fever). - Physiological Range Guards: Enforces clinical bounds (e.g., patient age must be between
0and120, systolic blood pressure between50and280).
The Production Data Pipeline Codebase
Explore the complete multi-file normalization suite below. Click folders in the explorer sidebar to inspect normalizers, date parsers, clinical schema definitions, and validation test reports. You can scroll through the code, copy snippets, and download individual files:
What 14,000 Wards Rows Taught Us
- Rule-Based Hybrid Over Blind Large Language Models: Running unconstrained LLMs over messy clinical records frequently leads to hallucinations when diagnosing localized abbreviations like “Palu” or “T.F.”. A rule-governed phonetic matcher combined with WHO ICD-11 anchor codes achieved 98.4% classification accuracy at zero API cost.
- Deterministic Date Parsing Protects Epidemic Curves: When analyzing seasonal malaria spikes in the rainy season (June–October), a single reversed month format (08/06 parsed as August 6th instead of June 8th) distorts the entire epidemiological onset curve.
- Data Science Begins in the Archive: If your machine learning pipeline cannot handle smudged ink, bilingual French/English shorthand, and manual transcription typos, it cannot survive outside Silicon Valley.
Comments
Comments coming soon. Set up Giscus on the repo to enable them.