{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Introduction to Probability and Statistics\n", "在本筆記中,我們將試玩一些之前討論過的概念。許多機率與統計的概念都在 Python 的主要資料處理函式庫中得到很好的體現,例如 `numpy` 和 `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 值可以被視為兩個分布具有相同平均值的機率。在我們的例子中,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": [ "## 結論\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:10:46+00:00", "source_file": "1-Introduction/04-stats-and-probability/notebook.ipynb", "language_code": "tw" } }, "nbformat": 4, "nbformat_minor": 4 }