# Statistics for Data Science — Averages, Spread, Distributions, Correlation and A/B Tests

Source: https://skills101.in/learn/data-science-ml/statistics-for-data-science — Skills101 free tutorial (Python for Data Science & Machine Learning). Updated 2026-11-09.

You don't need a statistics degree for data work — but you do need these ideas to avoid embarrassing (and expensive) wrong conclusions.

## 1. Mean vs median: the salary trap

```python
import numpy as np

packages = np.array([3.5, 3.6, 3.6, 4.0, 4.2, 4.5, 5.0, 6.0, 7.5, 45.0])   # LPA, one star offer
print("mean  ", packages.mean().round(2))
print("median", np.median(packages))
print("mean without the 45 LPA offer", packages[:-1].mean().round(2))
```

```text
mean   8.69
median 4.35
mean without the 45 LPA offer 4.66
```

One 45 LPA offer pulls the mean up a lot; the median barely moves. When a coaching ad says "average package 8.69 LPA", ask for the **median** (here 4.35) — most students got far less. Use the median for skewed data (salaries, house prices, delivery times).

## 2. Spread: standard deviation and percentiles

```python
import numpy as np

batch_a = np.array([68, 70, 71, 72, 69, 70, 71])
batch_b = np.array([40, 95, 60, 88, 52, 79, 76])
print("means:", batch_a.mean().round(1), batch_b.mean().round(1))
print("std:  ", batch_a.std().round(1), batch_b.std().round(1))
print("90th percentile of B:", np.percentile(batch_b, 90).round(1))
```

```text
means: 70.1 70.0
std:   1.2 18.5
90th percentile of B: 90.8
```

Same average, completely different batches: **standard deviation** measures how spread out values are. A **percentile** says "90% of values are below this" — that's how exam ranks like "98 percentile" work.

## 3. The normal distribution

Many measurements (heights, exam marks, measurement errors) form a bell curve. For a normal distribution about **68%** of values lie within 1 standard deviation of the mean, **95%** within 2, and **99.7%** within 3:

```python
import numpy as np

rng = np.random.default_rng(0)
marks = rng.normal(loc=60, scale=10, size=100_000)
for k in (1, 2, 3):
    inside = np.mean(np.abs(marks - 60) <= k * 10) * 100
    print(f"within {k} sd: {inside:.1f}%")
```

```text
within 1 sd: 68.4%
within 2 sd: 95.5%
within 3 sd: 99.7%
```

```python
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(20, 100, 400)
pdf = np.exp(-((x - 60) ** 2) / (2 * 10 ** 2)) / (10 * np.sqrt(2 * np.pi))

fig, ax = plt.subplots(figsize=(7, 3.4))
ax.plot(x, pdf, color="#0f1626")
for k, colour in ((3, "#dbeafe"), (2, "#93c5fd"), (1, "#2563eb")):
    band = (x >= 60 - k * 10) & (x <= 60 + k * 10)
    ax.fill_between(x[band], pdf[band], color=colour)
ax.set_title("Marks ~ normal(mean 60, sd 10): 68% within 50–70, 95% within 40–80")
ax.set_xlabel("Marks")
ax.set_yticks([])
ax.spines[["top", "right", "left"]].set_visible(False)
plt.savefig("normal-68-95.svg")
```

![Bell curve of marks with mean 60 and standard deviation 10: the dark band from 50 to 70 holds 68 percent, the middle band from 40 to 80 holds 95 percent, the light band from 30 to 90 holds 99.7 percent](https://skills101.in/learn-img/data-science-ml/normal-68-95.svg)

The simulation confirms the 68–95–99.7 rule. A **z-score** `(x − mean) / sd` tells how unusual a value is: z = 2.5 is rare.

## 4. Correlation is not causation

```python
import numpy as np

rng = np.random.default_rng(2)
study_hours = rng.uniform(0, 6, 200)
marks = 40 + 8 * study_hours + rng.normal(0, 8, 200)
phone_hours = 8 - study_hours + rng.normal(0, 1, 200)
print("study vs marks:", np.corrcoef(study_hours, marks)[0, 1].round(2))
print("phone vs marks:", np.corrcoef(phone_hours, marks)[0, 1].round(2))
```

```text
study vs marks: 0.85
phone vs marks: -0.74
```

Correlation runs from −1 (perfect opposite) through 0 (none) to +1 (perfect together). Here phone time is strongly **negatively** correlated with marks — but in our simulation phone time doesn't affect marks at all; both are driven by **study hours** (a *confounder*). Only a controlled experiment can show cause.

## 5. A/B testing: did the change work?

**Case:** an ed-tech app tests a new "Start free trial" button. Version A (old) and B (new) are each shown to 2,000 random visitors.

```python
import numpy as np

rng = np.random.default_rng(11)
a = rng.random(2000) < 0.050          # old button: true rate 5.0%
b = rng.random(2000) < 0.065          # new button: true rate 6.5%
print(f"A: {a.mean():.2%}  B: {b.mean():.2%}")

# Permutation test: if the button made no difference, how often would random shuffling
# produce a gap at least this big?
observed = b.mean() - a.mean()
both = np.concatenate([a, b])
gaps = []
for _ in range(5000):
    rng.shuffle(both)
    gaps.append(both[2000:].mean() - both[:2000].mean())
p_value = np.mean(np.array(gaps) >= observed)
print(f"observed gap {observed:.2%}, p-value {p_value:.3f}")
```

```text
A: 5.10%  B: 6.00%
observed gap 0.90%, p-value 0.116
```

The **p-value** is the chance of seeing a gap this large if the button changed nothing. Below 0.05 we usually call the result **statistically significant**.

Look closely at this run: we *built* B to be better (6.5% vs 5.0%), yet with 2,000 visitors each the observed gap is only 0.9 points and the p-value is about 0.12 — **not significant**. The data can't yet rule out luck. That's the real lesson of A/B testing: small true improvements need large samples. Decide the sample size *before* the test (from the smallest gain worth detecting), never stop early because the result "looks good", and also ask whether the gain is big enough to matter for the business.

## Common mistakes

- Reporting the mean for skewed data.
- Saying "X causes Y" from a correlation.
- Small samples: 30 users can't settle an A/B test.
- Running many tests and reporting only the one with p < 0.05.

## Quick recap

- Median for skewed data; standard deviation and percentiles for spread.
- Normal distribution: 68–95–99.7 rule; z-scores measure unusualness.
- Correlation (−1 to +1) ≠ causation; watch for confounders.
- A/B tests: random split, fixed sample size, p-value < 0.05 plus a meaningful effect.


Practice quiz and free certificate: https://skills101.in/learn/data-science-ml/statistics-for-data-science