Free tutorials & notes in Hindi & English · Clean code examples 🧰 Free PDF & Image Tools →
📐 Week 5 – Statistics & Probability  ·  Day 30

Day 30: Hypothesis Testing: t-test & Chi-square

⏱ 2.5 hours📶 Intermediate🎯 100% self-study
Day 30 / 60

✅ Today you will learn

  • H₀, H₁, α and the 5-step process
  • p-value – what it is and is not
  • t-tests: one-sample, two-sample, paired
  • Chi-square for categories, ANOVA for 3+ groups
  • Type I/II errors, effect size, which test when

🏢 In the real world

Customers with tech support churn 25%, those without 37%. A manager says “obviously tech support works – give it to everyone!” A sceptic says “it is just random noise”. A hypothesis test settles the argument with evidence, not opinions.

The 5-step process

  1. 1State H₀ (no effect) and H₁ (effect)
  2. 2Choose α (usually 0.05)
  3. 3Pick the test and compute the statistic
  4. 4Get the p-value
  5. 5Decide and explain in business words
Every test follows these steps
⚖️ Like a court

H₀ = “innocent” (no difference). We need strong evidence (small p-value) to reject it. Not rejecting H₀ does not prove innocence – it means not enough evidence.

The p-value by simulation

p-value = if H₀ were true, how often would we see a difference at least this big just by chance?

Python
import numpy as np
import pandas as pd
t = pd.read_csv("telecom_churn.csv")
t = t[t["tech_support"] != "No internet"]
churn = (t["churn"] == "Yes").to_numpy()
has_ts = (t["tech_support"] == "Yes").to_numpy()
observed = churn[~has_ts].mean() - churn[has_ts].mean()
rng = np.random.default_rng(0)
diffs = []
for _ in range(5000):                       # shuffle labels = world where H0 is true
    lab = rng.permutation(has_ts)
    diffs.append(churn[~lab].mean() - churn[lab].mean())
diffs = np.array(diffs)
print(f"Observed difference: {observed:.3f}")
print(f"Largest shuffled difference: {diffs.max():.3f}")
print(f"p-value ≈ {(np.abs(diffs) >= abs(observed)).mean():.4f}")
Observed difference: 0.119 Largest shuffled difference: 0.077 p-value ≈ 0.0000
Python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
t = pd.read_csv("telecom_churn.csv"); t = t[t["tech_support"] != "No internet"]
churn = (t["churn"] == "Yes").to_numpy(); ts = (t["tech_support"] == "Yes").to_numpy()
obs = churn[~ts].mean() - churn[ts].mean()
rng = np.random.default_rng(0)
d = [churn[~(l := rng.permutation(ts))].mean() - churn[l].mean() for _ in range(5000)]
plt.figure(figsize=(9, 3.5))
plt.hist(d, bins=40, color="#cbd5e1", label="differences if H₀ were true")
plt.axvline(obs, color="#dc2626", lw=2.5, label=f"observed = {obs:.3f}")
plt.title("The real difference is far outside what chance produces → reject H₀"); plt.legend()
plt.show()
Histogram of shuffled differences with the observed difference far to the right
Chance alone almost never produces a 12-point gap – the p-value is tiny.
p-valuePlain meaning
< 0.01Very strong evidence against H₀
0.01–0.05Evidence against H₀ (significant at 5%)
0.05–0.10Weak evidence – collect more data
> 0.10No evidence of a difference
⚠️ What a p-value is NOT

It is not the probability that H₀ is true, and it says nothing about how big or important the effect is. With huge data, tiny useless differences become “significant”. Always report the effect size too.

t-tests

Python
import pandas as pd
from scipy import stats
s = pd.read_csv("sales_data.csv")
r = s["rating"].dropna()
# One-sample: is the average rating different from the target 4.0?
res = stats.ttest_1samp(r, 4.0)
print(f"mean {r.mean():.3f} | t={res.statistic:.2f} p={res.pvalue:.4f}")
mean 4.059 | t=3.93 p=0.0001
Python
import pandas as pd
from scipy import stats
t = pd.read_csv("telecom_churn.csv")
yes = t.loc[t["churn"] == "Yes", "monthly_charges"]
no = t.loc[t["churn"] == "No", "monthly_charges"]
res = stats.ttest_ind(yes, no, equal_var=False)          # Welch’s t-test (safer)
pooled = ((yes.var() + no.var()) / 2) ** 0.5
print(f"churners ₹{yes.mean():.0f} vs stayers ₹{no.mean():.0f}")
print(f"t={res.statistic:.2f}, p={res.pvalue:.2e}, Cohen's d={(yes.mean() - no.mean()) / pooled:.2f}")
churners ₹759 vs stayers ₹636 t=12.31, p=1.95e-33, Cohen's d=0.49
Python
import numpy as np
from scipy import stats
rng = np.random.default_rng(4)
before = rng.normal(62, 9, 40).round()                 # mock-test marks before a crash course
after = (before + rng.normal(3, 4, 40)).round()        # same 40 students after
res = stats.ttest_rel(after, before)
print(f"avg gain {np.mean(after - before):.2f} marks | t={res.statistic:.2f} p={res.pvalue:.4f}")
avg gain 2.92 marks | t=4.77 p=0.0000

Chi-square: two categorical variables

Python
import pandas as pd
from scipy import stats
t = pd.read_csv("telecom_churn.csv")
for col in ["contract", "gender"]:
    table = pd.crosstab(t[col], t["churn"])
    chi2, p, dof, expected = stats.chi2_contingency(table)
    print(f"{col:9s} chi2={chi2:7.1f} dof={dof} p={p:.3g}")
print(pd.crosstab(t["gender"], t["churn"]))
contract chi2= 386.3 dof=2 p=1.31e-84 gender chi2= 0.0 dof=1 p=0.83 churn No Yes gender Female 1052 428 Male 1074 446

ANOVA: three or more groups

Python
import pandas as pd
from scipy import stats
h = pd.read_csv("house_prices.csv")
h["ppsf"] = h["price_lakh"] * 1e5 / h["area_sqft"]
groups = [g["ppsf"] for _, g in h.groupby("furnishing")]
f, p = stats.f_oneway(*groups)
print(h.groupby("furnishing")["ppsf"].mean().round(0).to_dict())
print(f"ANOVA F={f:.2f}, p={p:.4f}")
{'Furnished': 5057.0, 'Semi-Furnished': 5004.0, 'Unfurnished': 4777.0} ANOVA F=3.98, p=0.0191

Errors and power

H₀ trueH₀ false
Reject H₀Type I error (false alarm), rate = αCorrect (power)
Keep H₀CorrectType II error (missed effect), rate = β
Python
import numpy as np
from scipy import stats
rng = np.random.default_rng(0)
false_alarms = sum(stats.ttest_ind(rng.normal(0, 1, 50), rng.normal(0, 1, 50)).pvalue < 0.05 for _ in range(2000))
print(f"No real difference, yet 'significant' in {false_alarms / 2000:.1%} of tests (≈ α)")
for n in [20, 50, 200]:
    power = np.mean([stats.ttest_ind(rng.normal(0, 1, n), rng.normal(0.3, 1, n)).pvalue < 0.05 for _ in range(2000)])
    print(f"real small effect (d=0.3), n={n:3d} per group → detected {power:.0%} of the time")
No real difference, yet 'significant' in 5.0% of tests (≈ α) real small effect (d=0.3), n= 20 per group → detected 16% of the time real small effect (d=0.3), n= 50 per group → detected 31% of the time real small effect (d=0.3), n=200 per group → detected 85% of the time

Which test should I use?

QuestionDataTest (scipy.stats)
Mean vs a target1 numericttest_1samp
Two group meansnumeric + 2 groupsttest_ind (equal_var=False)
Before vs after, same people2 paired numericttest_rel
3+ group meansnumeric + 3+ groupsf_oneway
Two categories related?2 categoricalchi2_contingency
Two proportionscountsproportions_ztest (statsmodels) – Day 31
Non-normal / ranksnumericmannwhitneyu, kruskal

🏅 Job skills unlocked today

✔ Write H₀/H₁ and interpret p-values✔ Run t-tests, chi-square and ANOVA✔ Report effect size, not just significance✔ Choose the right test for the data

Practice tasks

🏋️ Practice – do it yourself

Task 1: Do students with internet score higher on average? Run Welch’s t-test and report the mean difference.
Show solution
Python
import pandas as pd
from scipy import stats
st = pd.read_csv("students_performance.csv")
a = st.loc[st["internet"] == "Yes", "average"]; b = st.loc[st["internet"] == "No", "average"]
print(f"difference {a.mean() - b.mean():.2f} marks, p = {stats.ttest_ind(a, b, equal_var=False).pvalue:.4f}")
difference 3.83 marks, p = 0.0006
Task 2: Is payment mode related to region in the sales data? Use chi-square.
Show solution
Python
import pandas as pd
from scipy import stats
s = pd.read_csv("sales_data.csv")
chi2, p, dof, _ = stats.chi2_contingency(pd.crosstab(s["region"], s["payment_mode"]))
print(round(chi2, 2), dof, round(p, 4))
1.76 9 0.9947
Task 3: Do average house prices per sqft differ between cities? Use ANOVA.
Show solution
Python
import pandas as pd
from scipy import stats
h = pd.read_csv("house_prices.csv"); h["ppsf"] = h["price_lakh"] * 1e5 / h["area_sqft"]
r = stats.f_oneway(*[g["ppsf"] for _, g in h.groupby("city")])
print(f"F={r.statistic:.1f}, p={r.pvalue:.1e}")
F=298.1, p=1.6e-156

Quick quiz

🧠 Quick Quiz – test yourself

1. p = 0.003 at α = 0.05 means…
p < α → reject. Size needs effect size.
2. Same students before and after training → which test?
Paired data.
3. Testing if gender and churn are related uses…
Both are categorical.
4. Type I error is…
Rejecting a true H₀.
5. To increase power you can…
More data → smaller SE → easier to detect effects.

Interview corner

💼 Interview Corner

Explain a p-value to a non-technical person.

“If there were truly no difference, a gap this big would happen by chance only p×100 times in 100. Since that is very rare, we believe the difference is real.” Then add how big the difference is.

Statistically significant vs practically significant?

Statistical significance says the effect is unlikely to be noise. Practical significance asks if it is big enough to matter (₹ impact, effect size). With millions of rows, a 0.1% lift can be significant but worthless.

Summary

  • H₀ = no effect; small p-value = evidence against it.
  • p-value is not P(H₀ true) and not effect size.
  • t-tests for means, chi-square for categories, ANOVA for 3+ groups.
  • Mind Type I/II errors, power and multiple testing.
➡️ Next

Day 31: correlation and A/B testing – run a complete website experiment from design to decision.

Frequently Asked Questions

Why 0.05?
It is a convention, not a law. High-stakes decisions (medicine) use 0.01 or stricter; quick product tests sometimes use 0.10.
I ran 20 tests and one was significant. Is it real?
Maybe not – at α = 0.05 you expect 1 false alarm in 20 tests. Correct for multiple testing (Bonferroni: α / number of tests).
← Data Science (60 Days)
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

Next topicDay 31: Correlation & A/B Testing Your progress: 30 / 60 (50%) · Lesson 31 / 60 Continue →

How was this lesson?

Your feedback helps us make the content better.

Write a review or suggestion (optional)

After approval, your name and city may appear publicly with your review. Please do not write your email or phone number.

▶ CodingEasily on YouTube

Learn with video lessons in easy Hindi

Featured video: RDBMS Part 3 – Class XI Commerce IT

▶ Subscribe – it’s free