{ "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", "Почнемо з вибірки з 30 значень з рівномірного розподілу від 0 до 9. Також обчислимо середнє та дисперсію.\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": [ "## Normal Distribution\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": [ "## Довіpчі інтервали\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", "Статистично більш коректний спосіб підтвердити гіпотезу — використати **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_{ij}$ матриці коваріації $M$ є коваріацією між вхідними змінними $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Цей документ було перекладено за допомогою сервісу автоматичного перекладу [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-16T21:24:40+00:00", "source_file": "1-Introduction/04-stats-and-probability/notebook.ipynb", "language_code": "uk" } }, "nbformat": 4, "nbformat_minor": 4 }