Learn
Mini widget·

Exploratory Data Analysis (EDA)

The average customer was 100.4 years old and nobody noticed

You call describe() on a fresh customer extract. Two hundred rows, mean visits 17.3, mean revenue 24.4. Nothing jumps out. Into the model it goes.

The mean age in that table was 100.4. Thirteen records had stored "age unknown" as 999. Drop those and the mean is 37.9. No summary statistic will ever tell you this, because 999 is a perfectly good number and the mean does exactly what it is asked to do.

How the age column was averagedRows usedMean ageBelievable?
999 left in200100.4no
13 rows of 999 removed18737.9yes

Both rows came out of the same file, the same column, the same mean function. The only difference is whether anyone looked at the numbers before feeding them in. That one look was worth 62.5 years.

Exploratory data analysis is the habit of looking at what you actually have before you model it. John Tukey named it in 1977, and his argument was a simple one: let the data suggest the questions instead of arriving with them.

None of it is difficult. It gets skipped because it is tedious, produces no deliverable, and appears to cost nothing — right up until week three.

The key question

Is this the dataset I think it is?

1

Concept

Letting the data tell you what to ask

Two modes, and why they do not mix

Statistical work runs in two opposite modes. One generates hypotheses; the other tests them. Run them at once and you lose both.

ExploratoryConfirmatory
Starts fromThe dataA hypothesis written in advance
ProducesQuestions and candidatesA p-value and a decision
AttitudeLook at everythingLook at one thing
p-valuesNot to be reported as evidenceThe point of the exercise
DangerSeeing patterns in noiseTesting the wrong thing well

This is why a p-value found while exploring cannot simply be reported. If you looked at a hundred relationships and picked the most striking one, that p-value has already been through a hundred rounds of selection. Exploration produces candidates; judgement has to come from somewhere else.

Four passes over the table

There is no fixed procedure, but the order matters — each pass is only worth doing once the pass before it has come back clean.

Structure

what is here

Rows, types, duplicates, missing.

One variable

shape and tails

Centre, spread, skew, odd values.

Two variables

does it move together

Scatter, crosstab, grouped box.

With a group

who is it true for

Split by a third variable and check again.

PassYou look atToolsYou are hunting for
StructureThe table as an objectshape, dtypes, head, duplicatedWrong types, duplicates, missing blocks
One variableEach column aloneHistogram, box plot, value countsSkew, sentinels, impossible values
Two variablesPairs of columnsScatter, crosstab, grouped boxRelationships, separate clouds
With a groupPairs split by a thirdColour, facets, correlation matrixConfounders, effects in one segment only

Why the order is not arbitrary

Plot a scatter before you have dealt with the 999s and one point pins itself to the far right while everything else collapses into the corner. Finish the univariate pass and the bivariate one becomes readable. Do it the other way round and you will draw every chart twice.

Why it leans on pictures

EDA is visual by necessity, not by taste. A summary statistic is a device for collapsing many values into one, and problems hide inside what got collapsed. Building datasets that share a mean, a standard deviation and a correlation while looking nothing alike is not even hard.

A plot, by contrast, shows you things you did not think to ask for: a second hump, values piling up on round numbers, a series that simply stops after some date. None of those can be requested from a statistic; all of them are obvious on sight.

2

Why It Matters

The half hour you skip costs three weeks

The bill arrives later, with interest

Skip EDA and the code still runs, the model still trains, the numbers still come out. Whatever is wrong goes wrong quietly, and usually surfaces at the worst possible moment.

What you missedWhen it surfacesWhat it costs then
A 999 sentinelWhen the model behaves oddlyRedo the preprocessing
Duplicate rowsWhen validation looks too goodRerun every experiment
Target leakageAfter it shipsThrow the model away
A 99:1 class imbalanceAfter you reported 99% accuracyRedesign the evaluation
"NY" and "New York"When the totals refuse to reconcileRecompute every aggregate
Collection method changed midwayIn peer reviewRetract the conclusion

Assumptions can only be checked by looking

Nearly every technique comes with conditions attached, and there is no way to know whether they hold without inspecting the data.

  • t-tests and ANOVA — normality and equal variances. A histogram and a box plot per group settle it.
  • Linear regression — linearity and constant error variance. Invisible until you plot the residuals.
  • Correlation — the relationship has to be a straight line. A clean curve reports r near zero.
  • k-means — roughly spherical clusters of similar size. A scatter plot tells you in seconds.
  • Time-series models — stationarity. One plot against time exposes trend and seasonality.

You learn what the model is about to learn

EDA is not purely defensive. Which features will matter, what needs transforming, which interactions to include — most of that gets decided here. Expecting the model to work it out on its own is usually optimistic.

If you find a feature correlating at 0.99

That is not a discovery, it is almost certainly leakage. Predicting churn with a "cancellation reason" column in the frame; predicting order value with the sales tax still in there. Miss it during EDA and you find out when the model hits production and falls over.

3

How It Works

Four passes over the same table

1. The first half hour

Run these in this order on any table you have not seen before. Most disasters get caught somewhere in these eight lines.

Table 1 What to check, in order, on a table you have just received
CheckWhat a bad answer looks likeAct on it by
Row and column countNot what the source system saysFind the filter that dropped rows
Duplicate rowsAny at all, on a keyed tableTrace the export, then de-duplicate
Data typesDates or numbers stored as textCast before anything else
Min and max of every numeric column999, −1, 0 where 0 is impossibleConvert sentinels to missing
Distinct values of every text column"Seoul", "seoul", "SEOUL "Normalise before grouping
Missing rate per columnConcentrated in one segmentAsk why before imputing
Distribution of the target99 : 1 imbalance, unnoticedChoose the metric accordingly
Correlation with the targetr above 0.95Suspect leakage, not a great feature

2. Start with the min and the max

For a numeric column these two numbers earn their keep faster than anything else. A mean can hide almost any problem; the extremes cannot.

What you seeWhat to suspect
999, 9999, −1, −99A sentinel standing in for missing
A maximum age over 120Data entry error, or another sentinel
A negative amountRefunds mixed in, or a sign convention
A date of 1900-01-01A default value that was never replaced
Far too many zerosMissing values filled with zero
A maximum of exactly 100 or 255Something got truncated upstream

3. Count the distinct values in text columns

Categorical problems are almost always the same value spelled several ways, and a distinct count exposes them instantly. If a column that should hold fifty states holds a hundred and twelve, you already have your answer.

The usual ways one value becomes several

Whitespace: "NY" vs "NY "

Naming: "NY" vs "New York" vs "New York State"

Case: "Pro" vs "pro" vs "PRO"

Missing: "" vs "N/A" vs "unknown" vs NULL

4. Missingness is a pattern, not a percentage

"8% missing" is far less informative than where those 8% sit. Scattered evenly, most imputation methods are harmless. Concentrated in one segment, imputing manufactures facts that were never collected.

The patternWhat it really meansWhat to do
Evenly scatteredOrdinary entry gapsMean or median imputation is fine
One whole periodIt was not being collected yetHandle that period separately
One segment onlyThat group was never askedThe group itself is the variable
Blank alongside other columnsA whole screen got skippedTreat them as a block
More missing at higher valuesHigh earners declined to answerImputing here amplifies the bias

5. Keep exploring and testing apart

The better your EDA, the less entitled you are to test on the same rows. There is exactly one way out of that bind: split off a portion of the data before you start looking, and touch it only at the end.

  • Hold out a test set before the first plot
  • Write down each hypothesis as it occurs to you
  • Test only the written-down ones, only on the held-out data
  • Never report a p-value you found while browsing
  • Log which transformations you applied and why
4

Example

Day one with a churn dataset

In practice: day one on a churn model

You have been handed churn prediction for a subscription product. The data team sends a CSV: fifty thousand rows, twenty-eight columns. Working through the checklist turns up the following.

CheckResultRead
Row count50,000 → 48,317 after de-duplicationThe export ran twice
Churn rate3.2%Accuracy is a useless metric here
Type of last_loginTextNeeds casting to a date
Minimum tenure_days−7Signup dated after first payment
Distinct values of plan11, for a four-tier productSpelling variants plus retired tiers
Missing rate of cancel_reason96.8%Only churners have it — leakage
Correlation of monthly_fee with churnr = 0.02Useless on its own
Churn rate by tier8.1% free, 1.4% paidEverything has to be split by tier

Row six is the day's real find. cancel_reason only gets populated after somebody churns, so including it pushes validation accuracy past 99% — and makes the model worthless in production, where that field is still empty at the moment you need a prediction.

The lesson here

Leakage does not announce itself by performing badly. It announces itself by performing suspiciously well. When a first model comes in better than you expected, go looking for the column that already knows the answer before you celebrate.

In practice: one plot, opposite conclusion

The same dataset showed that higher monthly fees came with lower churn. A proposal to raise prices was nearly written. Splitting the plot by tier told a different story.

Before and after the split

Pooled: fee ↑ → churn ↓ (r = −0.31)

Within free users: no relationship

Within paying users: no relationship

What was actually operating was not the fee but whether somebody pays at all. People who decided to pay stay; they do not stay because they pay more.

Read the correlation on its own and you land on a price increase — precisely the wrong move. Skipping the fourth pass, where you split by a third variable and look again, is how that happens.

Common misunderstandings

Misconception 1

❌ EDA is what you do if there is time left over.

It works the other way round. Skipping it does not remove the cost, it defers it — and it gets steeper the longer it waits. Redoing preprocessing and pulling a deployed model are not the same size of problem.

Misconception 2

❌ An automated profiling report counts as EDA.

The tool will tell you the maximum of age is 999. It has no idea that this is strange. Profilers surface anomalies; only somebody who knows the domain can say what they mean.

Misconception 3

❌ It came out significant while exploring, so it is significant.

Look at twenty relationships in data with no signal in it and one of them, on average, comes back at p < 0.05. What exploration gives you is not a finding but the next thing worth testing.

Misconception 4

❌ Outliers should be removed once found.

The goal is to understand them, not to delete them. Sentinel? Convert it to missing. Typo? Fix it. A genuine extreme value? Keep it. Deleting without making that distinction throws away the most informative rows you have.

5

Interactive

The same 200 rows, seen four ways

The same 200 rows, seen four ways

One customer table. The three numbers at the top — row count, mean visits, mean revenue — never change as you step through. Each pass, meanwhile, turns up something the previous one had no way of showing you.

Rows

200

Mean visits

17.3

Mean revenue

24.4

These three numbers are all you get from a summary call — and they do not move as you step through.

ColumnStored asNote
idintegeridentifier — never a quantity
signuptextshould be a date
ageintegermax is 999
plantext2 categories
visitsintegercount, ratio scale
revenuedecimalratio scale

The export job ran twice: 6 rows are exact duplicates. "signup" arrived as text, so it sorts 2025-10 before 2025-2.

Every one of these was already in the table. None of them is in the three numbers above.

What to look for

  • In pass 1, why does it matter that signup arrived as text?
  • In pass 2, how far does the mean age fall once the 999 bar is removed?
  • In pass 3, what would make the points fall into two separate clouds?
  • In pass 4, what does an r near zero for free users actually tell you?
  • If you had only reported the pooled r = 0.84, what would you have concluded?

Learning points

The three summary numbers held steady through all four passes — which is to say they told you nothing.

Each pass can only see what the one before it cleared. Change the order and you miss things.

A pooled correlation describes no group at all while distinct groups are mixed together.

Key takeaways

Look first

Plots before models

  • Summary statistics hide shape
  • Four passes, in order
  • Cheap now, expensive later

Question everything

Every column is guilty until checked

  • 999 and −1 are not ages
  • Text columns hide spelling variants
  • r = 0.99 usually means leakage

Stay honest

Exploring is not testing

  • A p-value found by looking is not evidence
  • Hold out data before you explore
  • Write the hypothesis down first

The question is not "which chart should I draw?"

It is "is this the dataset I think it is?" EDA is less a list of techniques than a refusal to believe anything you have not checked.

How does this apply to real data?

EDA happens once when the data lands, and again every time you change the preprocessing. In SKARI you can check the following alongside.

Data profile

Column Types, Unique Counts, Missing Map

Univariate

Histogram, Box Plot, Frequency Table

Bivariate and beyond

Scatter Matrix, Correlation Heatmap, Crosstab

Quality checks

Duplicate Check, Outlier Scan, Leakage Check

Once this clicks, you can answer questions like these.

  • Do the minimum and maximum of this column make sense?
  • Is the missingness scattered, or concentrated somewhere?
  • Is this correlation just an artefact of mixing two groups?
  • Does that suspiciously good feature already know the answer?
  • What data is left over to test what I just found?
Now try it on real dataOpen in Lab

Go Deeper

Data Cleaning & Preprocessing