DSC3103 · Module 1 of 9

Data Profiling, Validation & Cleaning

Turn messy field CSVs into a trusted analytical dataset (Lab 2)

~57 min to study properly

Every real-world data system begins with untrusted raw data. In our Uganda market monitoring pipeline, field enumerators submit daily CSVs with missing prices, negative quantities, and duplicate market-commodity-date rows.

Checkpoint · the examiner asks

Why should missing market prices be imputed using the per-commodity median rather than the overall dataset mean, and why is raw data kept strictly immutable?

In one minute

Raw data is untrusted and immutable: never edit data/raw/, always write deterministic outputs to data/processed/. Profile first (types, null rates, duplicates, quantiles), then validate the data contract, then clean: standardise strings, drop rows missing the composite key, deduplicate on (date, market, commodity), and impute prices with the per-commodity median because prices are skewed and differ by commodity.

Traps examiners look for
  • Imputing with the global mean — outliers and different commodities distort it.
  • Overwriting the raw CSV so a cleaning bug can never be undone.
  • Deduplicating on all columns instead of the business key.
Read

Study notes

01Why raw data can't be trusted

Field data is produced by people and devices under pressure: enumerators typing prices on a phone at a busy market, sensors dropping packets, spreadsheets merged by hand. Every one of those steps introduces a characteristic kind of error, and a data engineer's first job is to name them before fixing anything:

Error typeExample in the market-price dataWhat it breaks
Missing valuesprice_ugx is blankaverages, model training
Invalid domainprice_ugx = -1500, rainfall_mm = 900every aggregate that touches it
Inconsistent format"owino ", "OWINO", "Owino"joins and group-bys silently split one market into three
Duplicatesthe same (date, market, commodity) row submitted twicesums and counts are inflated
Unparseable valuesdate = "bad-date"time-series logic crashes or drops rows

The point of the table is that each error type has a different fix. Treating them all as "dirty data, drop it" throws away information and hides the real problem from whoever produced the data.

02Profile → validate → clean, in that order

Profiling measures the data without changing it: row count, column types, null rate per column, number of distinct values (cardinality), min/max and quantiles. A profile turns "the data looks messy" into numbers like 4.2% of prices are missing and 38 rows are negative — numbers you can put in a report and check again next week.

Validation checks hard rules — the data contract: required columns exist, date parses as ISO-8601, price_ugx > 0, rainfall_mm is between 0 and 500, and (date, market, commodity) is unique. Validation answers "may this data enter the pipeline?" A failed contract should stop the pipeline loudly rather than let bad rows leak downstream.

Cleaning applies deterministic, documented fixes: standardise strings (strip().title()), parse dates, drop rows missing any part of the business key, de-duplicate on that key, cap or reject impossible values, and impute the rest.

Doing these in order matters. If you clean before profiling, you can't say how dirty the data was. If you validate before standardising strings, " owino" fails a membership check it should pass.

03Why the per-commodity median

Imputation replaces a missing value with a best guess. The question is which guess.

  • The global mean mixes every commodity together. Beans (~3,800 UGX/kg) and maize (~1,800 UGX/kg) average to a price that is wrong for both.
  • The mean is also sensitive to outliers. One typo of 97,000 instead of 9,700 drags it far from the typical value.
  • The median is the middle value of the sorted data, so a single extreme value can't move it much — it is robust. Computing it per commodity (groupby("commodity")) keeps each commodity's own price level.
df["price_ugx"] = df.groupby("commodity")["price_ugx"].transform(lambda s: s.fillna(s.median()))

Imputation is still a guess. Good pipelines add a flag column (e.g. price_imputed = True) so analysts and models can tell real observations from filled ones.

04Raw data is immutable

The rule is simple: never modify data/raw/. Read from it; write cleaned outputs to data/processed/.

Why it matters in practice:

  1. Reproducibility. Anyone can rerun the pipeline from the same inputs and get the same outputs.
  2. Recoverability. Two weeks later you discover the cleaning script dropped valid rows. If you overwrote the raw file, that data is gone. If you didn't, you fix the script and rerun.
  3. Auditability. You can always show exactly what came in and what each step did to it.

In exams, pair this with the IQR rule: values outside [Q1 − 1.5·IQR, Q3 + 1.5·IQR] (IQR = Q3 − Q1) are flagged as outliers. Whether you drop, cap or keep them is a decision you justify — not something the formula decides for you.

Watch it done

Worked examples

Worked example 1

Tukey fences on beans prices

Beans prices in one week have Q1 = 3,200 UGX and Q3 = 4,000 UGX. A row shows 6,900 UGX. Is it an outlier by the 1.5×IQR rule?

    Worked example 2

    Mean vs median imputation

    Five maize prices in Owino: 1,800, 1,900, 2,000, 2,100 and a typo of 9,700. A sixth price is missing. What would mean-imputation and median-imputation fill in?

      Vocabulary

      Key terms

      Garbage In, Garbage Out costs billions

      Gartner estimates poor data quality costs organisations an average of $12.9 million per year. In production ML systems, fixing a schema or unit mismatch in ingestion almost always beats tuning model hyperparameters.

      Practise

      Quick check

      0 of 8 correct so far. Wrong answers come with an explanation — and the buddy can unpack them.

      Q1A dataset has Q1 = 2,000 UGX and Q3 = 3,200 UGX. What is the upper Tukey 1.5×IQR fence?

      Q210,000 raw rows have 4% missing primary keys, and 5% of the remaining valid rows are duplicates. How many clean rows remain?

      Q3Which step should come first in a cleaning pipeline?

      Q4Three rows have market values "Owino", "owino " and "OWINO". What happens if you group by market before cleaning?

      Q5What is the business key for the market-price dataset?

      Q6Why add a price_imputed flag column?

      Q7A validation rule says rainfall_mm must be in [0, 500]. A row has 812. What should validation do?

      Q8Q1 = 1,600 and Q3 = 2,400. What is the lower Tukey fence?

      Exam-style

      Written practice

      Short answer6 marks

      Describe the difference between profiling, validation and cleaning, giving one concrete example of each for the market-price data.

      Short answer6 marks

      A colleague imputed missing prices with the global mean and overwrote data/raw/market_prices.csv. Explain the two problems and how you would fix the pipeline.

      Long answer12 marks

      Design a cleaning pipeline for a raw daily market-price CSV. For each stage, state what it does, what it outputs, and how you would prove that no rows were lost unexpectedly.

      Watch

      Videos

      Fresh picks are chosen by AI every day.
      Hands-on

      Data Profiler, Tukey Outlier Gate & Median Imputer

      5 guided tasks: predict, experiment, explain — then challenge yourself. Plus 2 coding exercises you run right in the browser.

      Start the lab