You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Data-Science-For-Beginners/translations/ru/1-Introduction/04-stats-and-probability/notebook.ipynb

581 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"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": [
"## Нормальное распределение\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",
"Теперь давайте вычислим интервалы доверия для веса и роста бейсболистов. Мы будем использовать код [из этого обсуждения на Stack Overflow](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-value можно рассматривать как вероятность того, что два распределения имеют одинаковое среднее значение. В нашем случае оно очень низкое, что означает наличие веских доказательств в пользу того, что первичные базмены выше ростом.\n",
"* t-value — это промежуточное значение нормализованной разницы средних, которое используется в 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<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**Отказ от ответственности**: \nЭтот документ был переведен с использованием сервиса машинного перевода [Co-op Translator](https://github.com/Azure/co-op-translator). Хотя мы стремимся к точности, пожалуйста, учтите, что автоматический перевод может содержать ошибки или неточности. Оригинальный документ на его родном языке следует считать авторитетным источником. Для важной информации рекомендуется профессиональный перевод носителем языка. Мы не несем ответственности за любые недоразумения или неверные толкования, возникшие в результате использования данного перевода.\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\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-16T08:22:34+00:00",
"source_file": "1-Introduction/04-stats-and-probability/notebook.ipynb",
"language_code": "ru"
}
},
"nbformat": 4,
"nbformat_minor": 4
}