Skip to content

সমাধান — অধ্যায় ১.২ · 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   : 81.0833
unweighted : 78.75

হাতে: 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 mean : -0.0
z std  : 1.0

গঠনগতভাবেই (§৪.৪-এর প্রমাণ) z-score-এর mean \(0\) ও std \(1\) — যেকোনো data-তে, সবস