{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 確率と統計の入門\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` 関数によって返される2つの値は以下の通りです:\n", "* p値は、2つの分布が同じ平均を持つ確率と考えることができます。今回の場合、この値は非常に低く、ファーストベースマンの方が身長が高いという強い証拠を示しています。\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であるということは、2つの変数の間に強い**線形関係**があることを意味します。片方の値をもう片方に対してプロットすることで、線形関係を視覚的に確認できます。\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", "私たちは、給与のような人工的に設計された概念と観測された変数*身長*との相関を観察しました。では、身長や体重のような2つの観測変数も相関するか見てみましょう:\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:59:16+00:00", "source_file": "1-Introduction/04-stats-and-probability/notebook.ipynb", "language_code": "ja" } }, "nbformat": 4, "nbformat_minor": 4 }