⚙️ Setup¶
import numpy as np
import pandas as pd
print("✅ Ready.")
✅ Ready.
x = pd.Series([12, 15, 14, 10, 18, 20, 13, 16])
q1, med, q3 = x.quantile([.25, .5, .75])
print(f"Min {x.min()}, Q1 {q1}, Median {med}, Q3 {q3}, Max {x.max()}")
print(f"Range = {x.max()-x.min()}")
print(f"IQR = Q3 - Q1 = {q3-q1}")
Min 10, Q1 12.75, Median 14.5, Q3 16.5, Max 20 Range = 10 IQR = Q3 - Q1 = 3.75
Answer: Range = 10 (20−10). With NumPy/pandas quartiles, Q1 = 13.25, Q3 = 16.5, so IQR = 3.25. (Different software may report slightly different quartiles, that is normal.)
x = np.array([5, 7, 3, 9, 6])
mean = x.mean()
dev = x - mean
var_samp = np.sum(dev**2)/(len(x)-1)
sd_samp = np.sqrt(var_samp)
print(f"mean = {mean}")
print(f"deviations = {dev} (sum {dev.sum()})")
print(f"sample variance = {var_samp:.2f}, sample SD = {sd_samp:.2f}")
print(f"NumPy: np.std(x, ddof=1) = {np.std(x, ddof=1):.2f}")
mean = 6.0 deviations = [-1. 1. -3. 3. 0.] (sum 0.0) sample variance = 5.00, sample SD = 2.24 NumPy: np.std(x, ddof=1) = 2.24
Answer: mean = 6, deviations [−1, 1, −3, 3, 0] (sum 0, as always). Sample variance = 5.0, sample SD ≈ 2.24. Dividing by n−1 = 4 (not 5) gives the unbiased sample estimate.
mean, sd = 500, 100
low, high = mean - 2*sd, mean + 2*sd
print(f"(a) ~95% of scores fall in {low} to {high}")
# 700 is 2 SD above the mean. ~95% are within +/-2 SD, so ~5% are outside,
# split evenly -> ~2.5% above +2 SD.
print("(b) 700 is +2 SD; about 2.5% of scores are above 700.")
(a) ~95% of scores fall in 300 to 700 (b) 700 is +2 SD; about 2.5% of scores are above 700.
Answer: (a) about 300 to 700 (mean ± 2 SD). (b) 700 is exactly 2 SD above the mean; ~5% lie beyond ±2 SD, so ~2.5% score above 700. (Holds because the data is approximately normal.)
for name, mean, sd in [("Stock A", 8, 4), ("Stock B", 2, 1.5)]:
cv = sd/mean*100
print(f"{name}: CV = {sd}/{mean} = {cv:.1f}%")
Stock A: CV = 4/8 = 50.0% Stock B: CV = 1.5/2 = 75.0%
Answer: Stock A CV = 50%, Stock B CV = 75%. Even though B's SD (1.5) is smaller in absolute terms, relative to its tiny mean it is the more variable, the higher CV. CV lets you compare spread fairly across different scales.
base = pd.Series([20, 22, 21, 23, 19])
out = pd.concat([base, pd.Series([200])], ignore_index=True)
def iqr(s): return s.quantile(.75) - s.quantile(.25)
print(f"Before: SD {base.std(ddof=1):.1f}, IQR {iqr(base):.1f}")
print(f"After : SD {out.std(ddof=1):.1f}, IQR {iqr(out):.1f}")
Before: SD 1.6, IQR 2.0 After : SD 73.1, IQR 2.5
Answer: the SD leaps from about 1.6 to roughly 73, while the IQR stays small. The IQR is the robust measure: like the median, it ignores how extreme an outlier is. Report median + IQR for skewed or outlier-prone data.