{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Probability and Statistics\n", "In this notebook, we will play around with some of the concepts we have previously discussed. Many concepts from probability and statistics are well-represented in major libraries for data processing in Python, such as `numpy` and `pandas`.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import random\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 隨機變量與分佈\n", "讓我們從介乎 0 至 9 的均勻分佈中抽取 30 個樣本值。 我們亦會計算平均數和變異數。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sample = [ random.randint(0,10) for _ in range(30) ]\n", "print(f\"Sample: {sample}\")\n", "print(f\"Mean = {np.mean(sample)}\")\n", "print(f\"Variance = {np.var(sample)}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "為了視覺上估計樣本中有多少不同的值,我們可以繪製**直方圖**:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.hist(sample)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 分析實際數據\n", "\n", "均值和方差在分析實際世界數據時非常重要。讓我們從[SOCR MLB Height/Weight Data](http://wiki.stat.ucla.edu/socr/index.php/SOCR_Data_MLB_HeightsWeights)載入有關棒球運動員的數據。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df = pd.read_csv(\"../../data/SOCR_MLB.tsv\",sep='\\t', header=None, names=['Name','Team','Role','Weight','Height','Age'])\n", "df\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> 我們在這裏使用一個名為 [**Pandas**](https://pandas.pydata.org/) 的套件來進行數據分析。我們會在這門課程稍後討論更多關於 Pandas 和在 Python 中處理數據的內容。\n", "\n", "讓我們計算年齡、身高和體重的平均值:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df[['Age','Height','Weight']].mean()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "現在讓我們專注於身高,並計算標準差和變異數:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(list(df['Height'])[:20])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mean = df['Height'].mean()\n", "var = df['Height'].var()\n", "std = df['Height'].std()\n", "print(f\"Mean = {mean}\\nVariance = {var}\\nStandard Deviation = {std}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "除了平均值之外,查看中位數和四分位數也是很有意義的。它們可以用**箱型圖**來視覺化:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,2))\n", "plt.boxplot(df['Height'].ffill(), vert=False, showmeans=True)\n", "plt.grid(color='gray', linestyle='dotted')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我哋亦可以對數據集中嘅子集製作箱形圖,例如按球員角色分組。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.boxplot(column='Height', by='Role', figsize=(10,8))\n", "plt.xticks(rotation='vertical')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **注意**:這個圖表表明,平均而言,一壘手的身高較二壘手的身高高。稍後我們會學習如何更正式地檢驗這個假設,以及如何證明我們的數據在統計上顯著支持這一點。 \n", "\n", "年齡、身高和體重都是連續隨機變數。你認為它們的分佈是什麼?一個好方法是繪製值的直方圖: \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df['Weight'].hist(bins=15, figsize=(10,6))\n", "plt.suptitle('Weight distribution of MLB Players')\n", "plt.xlabel('Weight')\n", "plt.ylabel('Count')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 常態分佈\n", "\n", "讓我們創建一個人工重量樣本,該樣本遵循與我們的真實數據具有相同均值和方差的常態分佈:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "generated = np.random.normal(mean, std, 1000)\n", "generated[:20]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.hist(generated, bins=15)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.hist(np.random.normal(0,1,50000), bins=300)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "由於現實生活中大多數數值呈常態分佈,因此我們不應使用均勻隨機數生成器來生成樣本數據。以下是如果我們嘗試使用均勻分佈(由 `np.random.rand` 生成)來生成體重數據時會發生的情況:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "wrong_sample = np.random.rand(1000)*2*std+mean-std\n", "plt.figure(figsize=(10,6))\n", "plt.hist(wrong_sample)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 置信區間\n", "\n", "現在讓我們計算棒球運動員體重及身高的置信區間。我們將使用[這個stackoverflow討論中的程式碼](https://stackoverflow.com/questions/15033511/compute-a-confidence-interval-from-sample-data):\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import scipy.stats\n", "\n", "def mean_confidence_interval(data, confidence=0.95):\n", " a = 1.0 * np.array(data)\n", " n = len(a)\n", " m, se = np.mean(a), scipy.stats.sem(a)\n", " h = se * scipy.stats.t.ppf((1 + confidence) / 2., n-1)\n", " return m, h\n", "\n", "for p in [0.85, 0.9, 0.95]:\n", " m, h = mean_confidence_interval(df['Weight'].fillna(method='pad'),p)\n", " print(f\"p={p:.2f}, mean = {m:.2f} ± {h:.2f}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 假設檢定\n", "\n", "讓我們探索我們棒球選手資料集中的不同角色:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.groupby('Role').agg({ 'Weight' : 'mean', 'Height' : 'mean', 'Age' : 'count'}).rename(columns={ 'Age' : 'Count'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "讓我們測試一個假設,即一壘手的身高比二壘手高。最簡單的方法是檢驗置信區間:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for p in [0.85,0.9,0.95]:\n", " m1, h1 = mean_confidence_interval(df.loc[df['Role']=='First_Baseman',['Height']],p)\n", " m2, h2 = mean_confidence_interval(df.loc[df['Role']=='Second_Baseman',['Height']],p)\n", " print(f'Conf={p:.2f}, 1st basemen height: {m1-h1[0]:.2f}..{m1+h1[0]:.2f}, 2nd basemen height: {m2-h2[0]:.2f}..{m2+h2[0]:.2f}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "我哋可以見到啲區間冇重疊。\n", "\n", "一個統計上更正確嘅方法去證明呢個假設係用**Student t檢定**:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from scipy.stats import ttest_ind\n", "\n", "tval, pval = ttest_ind(df.loc[df['Role']=='First_Baseman',['Height']], df.loc[df['Role']=='Second_Baseman',['Height']],equal_var=False)\n", "print(f\"T-value = {tval[0]:.2f}\\nP-value: {pval[0]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`ttest_ind` 函式返回的兩個值是:\n", "* p 值可以被視為兩個分佈具有相同平均值的機率。在我們的案例中,該值非常低,表示有強烈證據支持一壘手較高。\n", "* t 值是標準化平均差的中間值,用於 t 檢定,並且會與給定信心水準的閾值進行比較。\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 模擬常態分佈與中心極限定理\n", "\n", "Python 的偽隨機生成器設計成給我們一個均勻分佈。如果我們想要建立一個常態分佈的生成器,可以使用中心極限定理。要取得常態分佈的值,我們只需計算均勻生成樣本的平均值。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def normal_random(sample_size=100):\n", " sample = [random.uniform(0,1) for _ in range(sample_size) ]\n", " return sum(sample)/sample_size\n", "\n", "sample = [normal_random() for _ in range(100)]\n", "plt.figure(figsize=(10,6))\n", "plt.hist(sample)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 相關性與邪惡棒球公司\n", "\n", "相關性讓我們能夠找到數據序列之間的關係。在我們的玩具範例中,假設有一家邪惡的棒球公司根據球員的身高支付薪水——球員越高,薪水越多。假設有一個基本薪資為$1000,並根據身高額外獎勵$0到$100。我們將採用MLB的真實球員,並計算他們的虛擬薪水:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "heights = df['Height'].fillna(method='pad')\n", "salaries = 1000+(heights-heights.min())/(heights.max()-heights.mean())*100\n", "print(list(zip(heights, salaries))[:10])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "現在讓我們計算這些序列的協方差和相關係數。`np.cov` 將會給我們所謂的**協方差矩陣**,這是協方差對多個變量的擴展。協方差矩陣 $M$ 的元素 $M_{ij}$ 是輸入變量 $X_i$ 和 $X_j$ 之間的協方差,而對角線上的值 $M_{ii}$ 是 $X_{i}$ 的變異數。類似地,`np.corrcoef` 將會給我們**相關矩陣**。\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(f\"Covariance matrix:\\n{np.cov(heights, salaries)}\")\n", "print(f\"Covariance = {np.cov(heights, salaries)[0,1]}\")\n", "print(f\"Correlation = {np.corrcoef(heights, salaries)[0,1]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "相關係數等於1表示兩個變數之間存在強烈的**線性關係**。我們可以通過繪製一個值與另一個值的圖形來直觀地看到這種線性關係:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.scatter(heights,salaries)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "讓我們看看如果關係不是線性會發生什麼。假設我們的公司決定隱藏身高和薪水之間明顯的線性依賴,並在公式中引入一些非線性,如 `sin`:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "salaries = 1000+np.sin((heights-heights.min())/(heights.max()-heights.mean()))*100\n", "print(f\"Correlation = {np.corrcoef(heights, salaries)[0,1]}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在這種情況下,相關性稍微小一些,但仍然相當高。現在,為了使關係更不明顯,我們可能想通過向薪水中加入一些隨機變量來增加額外的隨機性。讓我們看看會發生什麼:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "salaries = 1000+np.sin((heights-heights.min())/(heights.max()-heights.mean()))*100+np.random.random(size=len(heights))*20-10\n", "print(f\"Correlation = {np.corrcoef(heights, salaries)[0,1]}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.scatter(heights, salaries)\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> 你能猜到為什麼點會排成這樣的垂直線嗎?\n", "\n", "我們已經觀察到像薪水這種人工設計的概念與觀察變量*身高*之間的相關性。現在讓我們也看看兩個觀察變量,比如身高和體重,是否也存在相關性:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.corrcoef(df['Height'].ffill(),df['Weight'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "不幸的是,我們沒有得到任何結果——只有一些奇怪的 `nan` 值。這是因為我們序列中的一些值未定義,表示為 `nan`,這導致運算結果也未定義。從矩陣中我們可以看到,`Weight` 是有問題的欄位,因為已經計算了 `Height` 值之間的自我相關。\n", "\n", "> 此範例顯示了**資料準備**和**清理**的重要性。沒有適當的資料,我們無法計算任何東西。\n", "\n", "讓我們使用 `fillna` 方法來填補缺失值,並計算相關性:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "np.corrcoef(df['Height'].fillna(method='pad'), df['Weight'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "確實存在相關性,但沒有我們人工示例中的那麼強烈。事實上,如果我們觀察一個數值與另一個數值的散點圖,這種關係會不那麼明顯:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(10,6))\n", "plt.scatter(df['Weight'],df['Height'])\n", "plt.xlabel('Weight')\n", "plt.ylabel('Height')\n", "plt.tight_layout()\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Conclusion\n", "\n", "在本筆記本中,我們學會了如何對資料進行基本操作以計算統計函數。我們現在知道如何使用完善的數學和統計工具來驗證某些假設,以及如何根據資料樣本計算任意變數的信賴區間。\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n\n\n**免責聲明**:\n本文件是使用 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 進行翻譯。雖然我們努力確保準確性,但請注意自動翻譯可能包含錯誤或不準確之處。請以原文文件為權威資料來源。對於重要資訊,建議採用專業人工翻譯。對於因使用此翻譯而引起的任何誤解或錯誤詮釋,我們概不負責。\n\n" ] } ], "metadata": { "interpreter": { "hash": "86193a1ab0ba47eac1c69c1756090baa3b420b3eea7d4aafab8b85f8b312f0c5" }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" }, "coopTranslator": { "original_hash": "0f899e3c5019f948e7c787b22f3b2304", "translation_date": "2026-01-16T10:08:43+00:00", "source_file": "1-Introduction/04-stats-and-probability/notebook.ipynb", "language_code": "mo" } }, "nbformat": 4, "nbformat_minor": 4 }