সমাধান — অধ্যায় ১.২ · Location & Variability¶
এই ফাইলে ১.২ অধ্যায়ের সব অনুশীলনীর পূর্ণ সমাধান। কোড-প্রশ্নে seed fixed থাকায় নিচের সংখ্যা হুবহু পুনরুৎপাদনযোগ্য; প্রকৃত output দেওয়া আছে।
Conceptual¶
সমাধান ১ — সাধারণ কর্মীর বেতন: mean না median [easy]¶
median ভালো। data: \(30,32,33,35,36,38,40,42,45,800\)। মাঝের আটজন \(30\)–\(45\)-এর মধ্যে, একজন CEO \(800\) — একটি চরম outlier। median মাঝের দুই মানের গড় \(=(36+38)/2 = 37\), যা ঠিকই "সাধারণ কর্মী"-র ছবি দেয়। কিন্তু mean \(= (30+\dots+45+800)/10 = 1131/10 = 113.1\) — অর্থাৎ mean বলছে গড় বেতন ১১৩ হাজার, অথচ একজন বাদে কেউ ৫০-ও পায় না। CEO-র বিশাল মান mean-কে উপরে টেনেছে কিন্তু median অটল (mean \(\gg\) median)। তাই skewed/outlier-যুক্ত আয়-data-তে median-ই সঠিক "typical" পরিমাপ — এজন্যই বাস্তবে "median income/salary" রিপোর্ট করা হয়, mean নয়।
সমাধান ২ — robust না non-robust [easy]¶
- (ক) range — non-robust: কেবল min ও max-এর উপর নির্ভর, একটি outlier-ই সরাসরি বদলে দেয়।
- (খ) median — robust: মাঝের অবস্থান দেখে, প্রান্তের মান কত বড় তাতে কিছু আসে-যায় না (breakdown point \(50\%\))।
- (গ) standard deviation — non-robust: mean থেকে বর্গ-দূরত্বভিত্তিক; দূরের একটি বিন্দু বর্গ হয়ে বিশাল অবদান রাখে।
- (ঘ) IQR — robust: কেবল মাঝের \(50\%\) (\(Q_1\)–\(Q_3\)) দেখে, দুই প্রান্ত উপেক্ষা করে।
- (ঙ) mean — non-robust: breakdown point \(0\%\); একটিমাত্র মান অসীম হলে mean-ও অসীম।
সমাধান ৩ — deviation বর্গ না করলে [medium]¶
deviation (বিচ্যুতি) সরাসরি যোগ করলে পাওয়া যায় \(\sum_i (x_i - \bar{x}) = 0\) — সবসময়, সব data-তে (অধ্যায়ের §৪.১-এর প্রমাণ)। ধনাত্মক ও ঋণাত্মক deviation পরস্পর কাটাকাটি হয়ে যায়, তাই এটি ছড়ানো সম্পর্কে কোনো তথ্যই দেয় না — সরু আর চওড়া উভয় data-তেই উত্তর \(0\)। তাই চিহ্ন বাদ দেওয়া দরকার।
বর্গ করার বদলে পরম মান নিলে পাই mean absolute deviation \(=\frac{1}{n}\sum_i \lvert x_i - \bar{x}\rvert\), যা একটি বৈধ (এবং বর্গের তুলনায় কিছুটা বেশি robust) spread-পরিমাপ। তবে বর্গ-ভিত্তিক variance-ই বেশি ব্যবহৃত, কারণ এটি গাণিতিকভাবে সুবিধাজনক (differentiable, mean-কে least-squares কেন্দ্র বানায় — §৪.২)।
সমাধান ৪ — কেন \(n-1\) [medium]¶
sample variance-এ আমরা অজানা population mean \(\mu\)-র বদলে একই data থেকে হিসাব করা \(\bar{x}\) ব্যবহার করি। কিন্তু \(\bar{x}\) হলো ঠিক সেই বিন্দু যা \(\sum(x_i - c)^2\)-কে সর্বনিম্ন করে (§৪.২), তাই \(\sum(x_i - \bar{x})^2\) সর্বদা \(\sum(x_i - \mu)^2\)-এর চেয়ে ছোট বা সমান। ফলে \(n\) দিয়ে ভাগ করলে variance গড়ে \(\sigma^2\)-কে কম মূল্যায়ন করে (downward bias)। ঠিক এই কম-পড়াটা পূরণ হয় \(n\)-এর বদলে \(n-1\) দিয়ে ভাগ করলে — তখন \(\mathbb{E}[s^2] = \sigma^2\) (unbiased)। অন্যভাবে বললে, \(\bar{x}\) আনুমান করতে একটি degree of freedom খরচ হয়েছে, বাকি \(n-1\)টি স্বাধীন।
পার্থক্য বেশি গুরুত্বপূর্ণ \(n=5\)-এ: তখন \(n/(n-1) = 5/4 = 1.25\), অর্থাৎ ২৫% পার্থক্য। \(n=5000\)-এ অনুপাত \(5000/4999 \approx 1.0002\) — প্রায় অভিন্ন। ছোট sample-এ Bessel's correction গুরুত্বপূর্ণ; বড় sample-এ নগণ্য।
সমাধান ৫ — breakdown point: median \(50\%\), mean \(0\%\) [hard]¶
breakdown point = একটি statistic-কে অর্থহীন (যেমন \(\pm\infty\)) করতে যত ভগ্নাংশ data-কে নষ্ট/আত্যন্তিক করতে হয়, তার সর্বনিম্ন মান।
-
mean: \(0\%\)। \(\bar{x} = \frac{1}{n}\sum x_i\)-তে যদি কেবল একটি \(x_i \to \infty\) পাঠানো হয়, তবে \(\bar{x} \to \infty\)। মাত্র \(1/n\) ভগ্নাংশ (যা \(n\) বড় হলে \(\to 0\)) নষ্ট করেই mean ভেঙে পড়ে।
-
median: \(50\%\)। median নির্ভর করে মাঝের অবস্থানে থাকা মানের উপর। অর্ধেকের কম (\(<50\%\)) বিন্দুকে \(+\infty\) পাঠালেও মাঝের অবস্থানটি এখনও আসল data-র একটি সসীম মানেই থাকে — median সসীম থাকে। median-কে অসীম করতে হলে অন্তত অর্ধেক বিন্দু নষ্ট করতে হবে। তাই breakdown point \(= 50\%\), সম্ভাব্য সর্বোচ্চ।
§৩-এর উদাহরণের সাথে মিল: ওখানে \(y\)-তে \(10\)টির মধ্যে \(1\)টি (\(10\%\), যা \(<50\%\)) মান ছিল \(400\)। তাই median অবিচল রইল (\(46.5\)), কিন্তু mean (\(81.6\)) টানা পড়ল — breakdown point \(0\%\) বনাম \(50\%\)-এর ঠিক বাস্তব প্রতিফলন।
Computational¶
সমাধান ৬ — হাতে \(\{4,8,8,10,15\}\) [easy]¶
- mean \(= (4+8+8+10+15)/5 = 45/5 = 9.0\)
- median (sorted, মাঝের মান \(x_{(3)}\)) \(= 8.0\)
- mode \(= 8\) (দুইবার এসেছে)
- range \(= 15 - 4 = 11.0\)
- sample variance: deviation \((x-9): -5, -1, -1, 1, 6\); বর্গের যোগফল \(25+1+1+1+36 = 64\); তাই \(s^2 = 64/(5-1) = 16.0\)
- sample standard deviation \(= \sqrt{16} = 4.0\)
সমাধান ৭ — from scratch বনাম library [medium]¶
import numpy as np
rng = np.random.default_rng(2024)
data = rng.normal(100, 15, size=50)
# from scratch
mean_s = data.sum() / data.size
xs = np.sort(data)
median_s = (xs[24] + xs[25]) / 2 # n=50 জোড়
ss = ((data - mean_s) ** 2).sum()
std_s = (ss / (data.size - 1)) ** 0.5
q1, q3 = np.percentile(data, [25, 75])
iqr_s = q3 - q1
mad_s = np.median(np.abs(data - median_s))
# library
print("mean :", round(mean_s, 4), "| numpy:", round(data.mean(), 4))
print("median :", round(median_s, 4),"| numpy:", round(np.median(data), 4))
print("std :", round(std_s, 4), "| numpy:", round(data.std(ddof=1), 4))
print("IQR :", round(iqr_s, 4))
print("MAD :", round(mad_s, 4))
Output:
mean : 100.1708 | numpy: 100.1708
median : 100.793 | numpy: 100.793
std : 14.8153 | numpy: 14.8153
IQR : 22.3054
MAD : 11.1305
scratch ও library হুবহু মেলে। লক্ষণীয়: mean \(\approx\) median (data প্রায় symmetric, যেহেতু normal থেকে আসা), এবং std \(\approx 14.8\) ≈ যে \(\sigma=15\) দিয়ে data বানানো — sample size \(50\)-এ estimate বেশ কাছাকাছি।
সমাধান ৮ — weighted বনাম unweighted mean [medium]¶
import numpy as np
v = np.array([75, 88, 92, 60], dtype=float)
w = np.array([3, 4, 3, 2], dtype=float) # credit hours
print("weighted :", round((v * w).sum() / w.sum(), 4))
print("unweighted :", round(v.mean(), 4))
Output:
হাতে: weighted \(= \dfrac{3(75)+4(88)+3(92)+2(60)}{3+4+3+2} = \dfrac{225+352+276+120}{12} = \dfrac{973}{12} \approx 81.08\)।
পার্থক্যের কারণ: unweighted mean চারটি নম্বরকে সমান গুরুত্ব দেয়। weighted mean বেশি credit-এর বিষয়কে বেশি ওজন দেয় — এখানে সর্বোচ্চ নম্বর \(92\) (weight \(3\)) ও \(88\) (weight \(4\)) মিলে বেশি প্রভাব ফেলেছে, আর সবচেয়ে কম নম্বর \(60\)-র weight মাত্র \(2\), তাই তার টান কম। ফলে weighted mean (\(81.08\)) unweighted (\(78.75\))-এর চেয়ে কিছুটা বেশি।
সমাধান ৯ — outlier-এর প্রভাব পরিমাপ [hard]¶
import numpy as np
rng = np.random.default_rng(2024)
data = rng.normal(100, 15, size=50)
def five(d):
m = d.mean()
md = np.median(d)
sd = d.std(ddof=1)
q1, q3 = np.percentile(d, [25, 75]); iqr = q3 - q1
mad = np.median(np.abs(d - md))
return dict(mean=m, median=md, std=sd, IQR=iqr, MAD=mad)
before = five(data)
after = five(np.append(data, 500.0)) # একটি চরম outlier যোগ
print(f"{'stat':7s}{'before':>10s}{'after':>10s}{'% change':>11s}")
for k in ["mean", "median", "std", "IQR", "MAD"]:
pc = 100 * (after[k] - before[k]) / before[k]
print(f"{k:7s}{before[k]:10.3f}{after[k]:10.3f}{pc:+10.1f}%")
Output:
stat before after % change
mean 100.171 108.011 +7.8%
median 100.793 100.852 +0.1%
std 14.815 57.876 +290.7%
IQR 22.305 22.422 +0.5%
MAD 11.130 11.321 +1.7%
ব্যাখ্যা:
- সবচেয়ে বেশি বদলাল std (+২৯০.৭%) — outlier (\(500\)) mean থেকে বহু দূরে, আর std সেই দূরত্বকে বর্গ করে যোগ করে, তাই বিস্ফোরকভাবে বাড়ল। range/std সবচেয়ে ভঙ্গুর।
- mean বাড়ল +৭.৮% — একটি বড় মান যোগফল টেনে তোলে।
- সবচেয়ে কম বদলাল median (+০.১%) — মাঝের অবস্থান প্রায় একই; এর পরে IQR (+০.৫%) ও MAD (+১.৭%)।
উপসংহার: robust পরিমাপ (median, IQR, MAD) outlier-এ কার্যত নিশ্চল; non-robust পরিমাপ (mean, বিশেষত std) প্রবলভাবে প্রভাবিত। দূষিত বা skewed data-তে তাই robust statistic-কেই প্রাধান্য দিন।
সমাধান ১০ — z-score দিয়ে তুলনা [hard]¶
হাতে z-score: $$ z_{\text{Rita}} = \frac{82 - 70}{8} = \frac{12}{8} = 1.5, \qquad z_{\text{Karim}} = \frac{75 - 60}{10} = \frac{15}{10} = 1.5 . $$
ফলাফল: raw নম্বরে Rita (\(82\)) Karim (\(75\))-এর চেয়ে বেশি, কিন্তু নিজ-নিজ ক্লাসের তুলনায় দুজনেই ঠিক \(1.5\) standard deviation উপরে — অর্থাৎ সমান-সমান। ভিন্ন mean ও std-এর দুটি পরীক্ষার raw নম্বর সরাসরি তুলনাযোগ্য নয়; z-score সেই এককের পার্থক্য মুছে দিয়ে ন্যায্য তুলনা সম্ভব করে। এটাই standardization-এর মূল উপযোগ।
কোডে যাচাই (z-score-এর mean \(\approx 0\), std \(\approx 1\)):
import numpy as np
rng = np.random.default_rng(7)
x = rng.normal(50, 12, size=1000)
z = (x - x.mean()) / x.std(ddof=1)
print("z mean :", round(float(z.mean()), 12)) # ≈ 0
print("z std :", round(float(z.std(ddof=1)), 6)) # = 1.0
Output:
গঠনগতভাবেই (§৪.৪-এর প্রমাণ) z-score-এর mean \(0\) ও std \(1\) — যেকোনো data-তে, সবস