diff --git a/fig-ml/lin-class.png b/fig-ml/lin-class.png new file mode 100644 index 0000000..abebfa6 Binary files /dev/null and b/fig-ml/lin-class.png differ diff --git a/ml.ipynb b/ml.ipynb new file mode 100644 index 0000000..f93bb37 --- /dev/null +++ b/ml.ipynb @@ -0,0 +1,2757 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# Машинное обучиение с использованием библиотек Python\n", + "\n", + " \n", + "**С.В. Лемешевский** (email: `sergey.lemeshevsky@gmail.com`), Институт математики НАН Беларуси\n", + "\n", + "Date: **May 4, 2020**\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "# Основные определения и постановки задач машинного обучения\n", + "
\n", + "\n", + "*Машинное обучение* — это раздел математики, изучающий способы\n", + "извлечения закономерностей из ограниченного числа примеров. \n", + "\n", + "\n", + "## Примеры задач машинного обучения\n", + "
\n", + "\n", + "Рассмотрим несколько примеров задач, которые решаются с помощью\n", + "машинного обучения.\n", + "\n", + "** Кредитный скоринг.**\n", + "\n", + " *Задача*: выяснить, какие заявки на кредит можно одобрить.\n", + "\n", + "**Лента Facebook/Дзен по интересности (вместо сортировки по времени).**\n", + "\n", + " *Задача*: показать посты, наиболее интересные для конкретного человека.\n", + "\n", + "**Детектирование некорректной работы.**\n", + "\n", + " Предположим, что у нас есть завод, на котором происходят некоторые\n", + " процессы (стоят какие-то котлы, станки, работают сотрудники). На\n", + " предприятии может произойти поломка, например, сломается датчик\n", + " уровня жидкости в баке, из-за чего насос не остановится при\n", + " достижении нужного уровня и нефть начнёт разливаться по полу, что\n", + " может привести к неизвестным последствиям. Или же сотрудники объявят\n", + " забастовку и вся работа остановится. Мы хотим, чтобы завод работал\n", + " исправно, а обо всех проблемах узнавать как можно раньше. \n", + "\n", + " *Задача*: предсказать поломки/нештатные ситуации на заводе.\n", + "\n", + "**Вопросно-ответная система (как Siri).**\n", + "\n", + " *Задача*: ответить голосом на вопрос, заданный голосом.\n", + "\n", + "**Self-driving cars.**\n", + "\n", + " *Задача*: доехать из точки $А$ в точку $В$.\n", + "\n", + "**Перенос стиля изображения.**\n", + "\n", + " *Задача*: перенести стиль одного изображения на другое (смешать\n", + " стиль одного с контекстом другого). \n", + "\n", + "\n", + "Как видим, задачи очень разнообразны. Мы начнем наш путь со следующей\n", + "классической постановки (к которой, кстати, сводятся многие\n", + "вышеперечисленные задачи): по имеющемуся признаковому описанию объекта\n", + "$x \\in \\mathbb{R}^m$ предсказать значение целевой переменной $y \\in\n", + "\\mathbb{R}^k$ для данного объекта. Обычно $k=1$. \n", + "\n", + "Например, в случае кредитного скоринга $x$-ом являются все известные о\n", + "клиенте данные (доход, пол, возраст, кредитная история и т.д.), а\n", + "$y$-ом одобрение или неодобрение заявки на кредит.\n", + "\n", + "Библиотеки с алгоритмами машинного обучения, которые будем изучать:\n", + "* [scikit-learn](http://scikit-learn.github.io/stable),\n", + "\n", + "* [XGBoost](https://xgboost.readthedocs.io/en/latest/) и\n", + "\n", + "* [pytorch](https://pytorch.org). \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "## Линейная регрессия\n", + "
\n", + "\n", + "Начнем с подключения необходимых библиотек" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import seaborn as sns\n", + "import pandas as pd\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "*Линейная регрессия* — это модель следующего вида:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "\n", + "$$\n", + "\\begin{equation}\n", + "\\label{ml:linear-regression:eq:lin-reg} \\tag{1}\n", + "a(x) = \\langle a, w \\rangle + w_0 = \\sum_{i = 1}^{d} w_i x_i + w_0,\n", + "\\end{equation}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "где $w \\in \\mathbb{R}^d$, $w_0 \\in \\mathbb{R}$. Параметрами модели\n", + "являются *веса* или *коэффициенты* $w_i$. Вес $w_0$ также называется \n", + "*свободным коэффициентом* или *сдвигом* (bias). Обучить линейную\n", + "регрессию — значит найти $w$ и $w_0$.\n", + "\n", + "В машинном обучении часто говорят об *обобщающей способности модели*,\n", + "то есть о способности модели работать на новых, тестовых данных\n", + "хорошо. Если модель будет идеально предсказывать выборку, на которой\n", + "она обучалась, но при этом просто ее запомнит, не «вытащив» из данных\n", + "никакой закономерности, от нее будет мало толку. Такую модель\n", + "называют *переобученной*: она слишком подстроилась под обучающие\n", + "примеры, не выявив никакой полезной закономерности, которая позволила\n", + "бы ей совершать хорошие предсказания на данных, которые она ранее не\n", + "видела. \n", + "\n", + "Рассмотрим следующий пример, на котором будет хорошо видно, что значит\n", + "переобучение модели. Для этого нам понадобится сгенерировать\n", + "синтетические данные. Рассмотрим зависимость $y(x) = \\cos(1.5\\pi x)$,\n", + "$y$ — целевая переменная (таргет), а $x$ — объект (просто число от $0$ до\n", + "$1$). В жизни мы наблюдаем какое-то конечное количество пар\n", + "объект-таргет, поэтому смоделируем это, взяв $30$ случайных точек $x_i$\n", + "в отрезке $[0;1]$. Более того, в реальной жизни целевая переменная\n", + "может быть зашумленной (измерения в жизни не всегда точны),\n", + "смоделируем это, зашумив значение функции нормальным шумом:\n", + "$\\tilde{y}_i = y(x_i) + \\mathcal{N}(0, 0.01)$:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.random.seed(36)\n", + "x = np.linspace(0, 1, 100)\n", + "y = np.cos(1.5 * np.pi * x)\n", + "\n", + "x_objects = np.random.uniform(0, 1, size=30)\n", + "y_objects = np.cos(1.5 * np.pi * x_objects) + np.random.normal(scale=0.1, size=x_objects.shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Попытаемся обучить три разных линейных модели: признаки для первой\n", + "--- $\\{x\\}$, для второй --- $\\{x, x^2, x^3, x^4\\}$, для\n", + "третьей --- $\\{x, \\dots, x^{20}\\}$:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import LinearRegression\n", + "from sklearn.preprocessing import PolynomialFeatures\n", + "\n", + "\n", + "fig, axs = plt.subplots(figsize=(16, 4), ncols=3)\n", + "for i, degree in enumerate([1, 4, 20]):\n", + " X_objects = PolynomialFeatures(degree).fit_transform(x_objects[:, None])\n", + " X = PolynomialFeatures(degree).fit_transform(x[:, None])\n", + " regr = LinearRegression().fit(X_objects, y_objects)\n", + " y_pred = regr.predict(X)\n", + " axs[i].plot(x, y, label=\"Real function\")\n", + " axs[i].scatter(x_objects, y_objects, label=\"Data\")\n", + " axs[i].plot(x, y_pred, label=\"Prediction\")\n", + " if i == 0:\n", + " axs[i].legend()\n", + " axs[i].set_title(\"Degree = %d\" % degree)\n", + " axs[i].set_xlabel(\"$x$\")\n", + " axs[i].set_ylabel(\"$f(x)$\")\n", + " axs[i].set_ylim(-2, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Чтобы избежать переобучения, модель регуляризуют. Обычно переобучения\n", + "в линейных моделях связаны с большими весами, а поэтому модель часто\n", + "штрафуют за большие значения весов, добавляя к функционалу качества,\n", + "например, квадрат $\\ell^2$-нормы вектора $w$:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "
\n", + "\n", + "$$\n", + "\\label{ml:linear-regression:eq:2} \\tag{2}\n", + "Q_{reg}(X, y, a) = Q(X, y, a) + \\lambda \\|w\\|_2^2\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Это слагаемое называют $\\ell_2$-регуляризатором, а коэффициент\n", + "$\\lambda$ --- коэффициентом регуляризации.\n", + "\n", + "## Загрузка данных\n", + "
\n", + "\n", + "Мы будем работать с данными из соревнования\n", + "[House Prices: Advanced Regression Techniques](https://www.kaggle.com/c/house-prices-advanced-regression-techniques/overview),\n", + "в котором требовалось предсказать стоимость жилья. Давайте сначала\n", + "загрузим и немного изучим данные (`train.csv` со страницы соревнования)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = pd.read_csv(\"train.csv\")\n", + "data.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Первое, что стоит заметить — у нас в данных есть уникальное для\n", + "каждого объекта поле `id`. Обычно такие поля только мешают и\n", + "способствуют переобучению. Удалим это поле из данных:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = data.drop(columns=[\"Id\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Разделим данные на обучающую и тестовую выборки. Для простоты не будем\n", + "выделять дополнительно валидационную выборку (хотя это обычно стоит\n", + "делать, она нужна для подбора гиперпараметров модели, то есть\n", + "параметров, которые нельзя подбирать по обучающей\n", + "выборке). Дополнительно нам придется отделить значения целевой\n", + "переменной от данных." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "y = data[\"SalePrice\"]\n", + "X = data.drop(columns=[\"SalePrice\"])\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Посмотрим сначала на значения целевой переменной:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.distplot(y_train)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Судя по гистограмме, у нас есть примеры с нетипично большой\n", + "стоимостью, что может помешать нам, если наша функция потерь слишком\n", + "чувствительна к выбросам. В дальнейшем мы рассмотрим способы, как\n", + "минимизировать ущерб от этого.\n", + "\n", + "Так как для решения нашей задачи мы бы хотели обучить линейную\n", + "регрессию, было бы хорошо найти признаки, «наиболее линейно» связанные\n", + "с целевой переменной, иначе говоря, посмотреть на коэффициент\n", + "корреляции Пирсона между признаками и целевой переменной. Заметим, что\n", + "не все признаки являются числовыми, пока что мы не будем рассматривать\n", + "такие признаки.\n", + "\n", + "> **Коэффициент корреляции Пирсона.**\n", + ">\n", + "> Коэффициент корреляции Пирсона характеризует существование линейной\n", + "> зависимости между двумя величинами.\n", + "> \n", + "> Пусть даны две выборки $x = (x_1, x_2, \\ldots, x_m)$ и $y = (y_1, y_2,\n", + "> \\ldots, y_m$; коэффициент корреляции Пирсона по формуле:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "r_{xy} = \\frac{\\sum_{i=1}^{m}(x_i-\\bar{x})(y_i -\n", + "\\bar{y})}{\\sqrt{\\sum_{i=1}^{m}(x_i - \\bar{x})^2 \\sum_{i=1}^{m}(y_i -\n", + "\\bar{y})^2}} = \\frac{cov(x, y)}{\\sqrt{s_x^2s_y^2}},\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> где $\\bar{x}$, $\\bar{y}$ — выборочные средние, $s_x^2$, $s_y^2$ —\n", + "> выборочные дисперсии, $r_{xy} \\in [-1, 1]$.\n", + "> * $|r_{xy}| = 1 \\Rightarrow$ $x$, $y$ — линейно зависимы,\n", + "> \n", + "> * $|r_{xy}| = 0 \\Rightarrow$ $x$, $y$ — линейно не зависимы." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "numeric_data = X_train.select_dtypes([np.number])\n", + "numeric_data_mean = numeric_data.mean()\n", + "numeric_features = numeric_data.columns\n", + "\n", + "X_train = X_train.fillna(numeric_data_mean)\n", + "X_test = X_test.fillna(numeric_data_mean)\n", + "\n", + "correlations = {\n", + " feature: np.corrcoef(X_train[feature], y_train)[0][1]\n", + " for feature in numeric_features\n", + "}\n", + "sorted_correlations = sorted(correlations.items(), key=lambda x: x[1], reverse=True)\n", + "features_order = [x[0] for x in sorted_correlations]\n", + "correlations = [x[1] for x in sorted_correlations]\n", + "\n", + "plot = sns.barplot(y=features_order, x=correlations)\n", + "plot.figure.set_size_inches(15, 10)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Посмотрим на признаки из начала списка. Для этого нарисуем график\n", + "зависимости целевой переменной от каждого из признаков. На этом\n", + "графике каждая точка соответствует паре признак-таргет (такие графики\n", + "называются `scatter-plot`)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "fig, axs = plt.subplots(figsize=(16, 5), ncols=3)\n", + "for i, feature in enumerate([\"GrLivArea\", \"GarageArea\", \"TotalBsmtSF\"]):\n", + " axs[i].scatter(X_train[feature], y_train, alpha=0.2)\n", + " axs[i].set_xlabel(feature)\n", + " axs[i].set_ylabel(\"SalePrice\")\n", + "plt.tight_layout()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Видим, что между этими признаками и целевой переменной действительно\n", + "наблюдается линейная зависимость. \n", + "\n", + "## Первая модель\n", + "
\n", + "\n", + "В арсенале дата-саентиста кроме `pandas` и `matplotlib` должны быть\n", + "библиотеки, позволяющие обучать модели. Для простых моделей (линейные\n", + "модели, решающее дерево, ...) отлично подходит `sklearn`: в нем очень\n", + "понятный и простой интерфейс. Несмотря на то, что в `sklearn` есть\n", + "реализация бустинга и простых нейронных сетей, ими все же не\n", + "пользуются и предпочитают специализированные библиотеки: `XGBoost`,\n", + "`LightGBM` и пр. для градиентного бустинга над деревьями, `PyTorch`,\n", + "`Tensorflow` и пр. для нейронных сетей. Так как мы будем обучать\n", + "линейную регрессию, нам подойдет реализация из `sklearn`. \n", + "\n", + "\n", + "Попробуем обучить линейную регрессию на числовых признаках из нашего\n", + "датасета. В `sklearn` есть несколько классов, реализующих линейную\n", + "регрессию: \n", + "\n", + "* [`LinearRegression`](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html) — «классическая» линейная регрессия с оптимизацией MSE. Веса находятся как точное решение: $w^* = (X^TX)^{-1}X^Ty$\n", + "\n", + "* [`Ridge`](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html) — линейная регрессия с оптимизацией MSE и $\\ell_2$-регуляризацией\n", + "\n", + "* [`Lasso`](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Lasso.html) — линейная регрессия с оптимизацией MSE и $\\ell_1$-регуляризацией\n", + "\n", + "У моделей из `sklearn` есть методы `fit` и `predict`. Первый принимает\n", + "на вход обучающую выборку и вектор целевых переменных и обучает\n", + "модель, второй, будучи вызванным после обучения модели, возвращает\n", + "предсказание на выборке. Попробуем обучить нашу первую модель на\n", + "числовых признаках, которые у нас сейчас есть:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import Ridge\n", + "from sklearn.metrics import mean_squared_error\n", + "\n", + "model = Ridge()\n", + "model.fit(X_train[numeric_features], y_train)\n", + "y_pred = model.predict(X_test[numeric_features])\n", + "y_train_pred = model.predict(X_train[numeric_features])\n", + "\n", + "print(\"Test MSE = %.4f\" % mean_squared_error(y_test, y_pred))\n", + "print(\"Train MSE = %.4f\" % mean_squared_error(y_train, y_train_pred))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Мы обучили первую модель и даже посчитали ее качество на отложенной\n", + "выборке! Давайте теперь посмотрим на то, как можно оценить качество\n", + "модели с помощью кросс-валидации. Принцип кросс-валидации изображен на\n", + "рисунке\n", + "\n", + "\n", + "\n", + "При кросс-валидации мы делим обучающую выборку на $n$ частей\n", + "(fold). Затем мы обучаем $n$ моделей: каждая модель обучается при\n", + "отсутствии соответствующего фолда, то есть $i$-ая модель обучается на\n", + "всей обучающей выборке, кроме объектов, которые попали в $i$-ый фолд\n", + "(out-of-fold). Затем мы измеряем качество $i$-ой модели на $i$-ом\n", + "фолде. Так как он не участвовал в обучении этой модели, мы получим\n", + "«честный результат». После этого, для получения финального значения\n", + "метрики качества, мы можем усреднить полученные нами $n$ значений." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import cross_val_score\n", + "\n", + "cv_scores = cross_val_score(model, X_train[numeric_features], y_train, cv=10, scoring=\"neg_mean_squared_error\")\n", + "print(\"Cross validation scores:\\n\\t\", \"\\n\\t\".join(\"%.4f\" % x for x in cv_scores))\n", + "print(\"Mean CV MSE = %.4f\" % np.mean(-cv_scores))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Обратите внимание на то, что результаты `cv_scores` получились\n", + "отрицательными. Это соглашение в `sklearn` (скоринговую функцию нужно\n", + "максимизировать). Поэтому все стандартные скореры называются `neg_*`,\n", + "например, `neg_mean_squared_error`.\n", + "\n", + "В качестве метрики качества в соревновании использовалось RMSE (\n", + "Root Mean Squared Error), а не MSE, которое мы считали выше (и по\n", + "отложенной выборке и при кросс-валидации):" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\text{RMSE}(X, y, a) = \\sqrt{\\frac{1}{\\ell}\\sum_{i=1}^{\\ell} (y_i -\n", + "a(x_i))^2}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "RMSE в чистом виде не входит в стандартные метрики `sklearn`, но мы\n", + "всегда можем определить свою метрику и использовать ее в некоторых\n", + "функциях `sklearn`, например, `cross_val_score`. Для этого нужно\n", + "воспользоваться `sklearn.metrics.make_scorer`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.metrics import make_scorer\n", + "\n", + "def rmse(y_true, y_pred):\n", + " error = (y_true - y_pred) ** 2\n", + " return np.sqrt(np.mean(error))\n", + "\n", + "rmse_scorer = make_scorer(\n", + " rmse,\n", + " greater_is_better=False\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import Ridge\n", + "\n", + "model = Ridge()\n", + "model.fit(X_train[numeric_features], y_train)\n", + "y_pred = model.predict(X_test[numeric_features])\n", + "y_train_pred = model.predict(X_train[numeric_features])\n", + "\n", + "print(\"Test RMSE = %.4f\" % rmse(y_test, y_pred))\n", + "print(\"Train RMSE = %.4f\" % rmse(y_train, y_train_pred))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import cross_val_score\n", + "\n", + "cv_scores = cross_val_score(model, X_train[numeric_features], y_train, cv=10, scoring=rmse_scorer)\n", + "print(\"Cross validation scores:\\n\\t\", \"\\n\\t\".join(\"%.4f\" % x for x in cv_scores))\n", + "print(\"Mean CV RMSE = %.4f\" % np.mean(-cv_scores))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Для того, чтобы иметь некоторую точку отсчета, удобно посчитать\n", + "оптимальное значение функции потерь при константном предсказании." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "best_constant = y_train.mean()\n", + "print(\"Test RMSE with best constant = %.4f\" % rmse(y_test, best_constant))\n", + "print(\"Train RMSE with best constant = %.4f\" % rmse(y_train, best_constant))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Давайте посмотрим на то, какие же признаки оказались самыми\n", + "«сильными». Для этого визуализируем веса, соответствующие\n", + "признакам. Чем больше вес — тем более сильным является признак." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def show_weights(features, weights, scales):\n", + " fig, axs = plt.subplots(figsize=(14, 10), ncols=2)\n", + " sorted_weights = sorted(zip(weights, features, scales), reverse=True)\n", + " weights = [x[0] for x in sorted_weights]\n", + " features = [x[1] for x in sorted_weights]\n", + " scales = [x[2] for x in sorted_weights]\n", + " sns.barplot(y=features, x=weights, ax=axs[0])\n", + " axs[0].set_xlabel(\"Weight\")\n", + " sns.barplot(y=features, x=scales, ax=axs[1])\n", + " axs[1].set_xlabel(\"Scale\")\n", + " plt.tight_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "show_weights(numeric_features, model.coef_, X_train[numeric_features].std())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Будем масштабировать наши признаки перед обучением модели. Это, среди,\n", + "прочего, сделает нашу регуляризацию более честной: теперь все признаки\n", + "будут регуляризоваться в равной степени.\n", + "\n", + "Для этого воспользуемся трансформером\n", + "[`StandardScaler`](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html).\n", + "Трансформеры в `sklearn` имеют методы `fit` и `transform` (а еще\n", + "`fit_transform`). Метод `fit` принимает на вход обучающую выборку и\n", + "считает по ней необходимые значения (например статистики, как\n", + "`StandardScaler`: среднее и стандартное отклонение каждого из\n", + "признаков); `transform` применяет преобразование к переданной\n", + "выборке." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "scaler = StandardScaler()\n", + "X_train_scaled = scaler.fit_transform(X_train[numeric_features])\n", + "X_test_scaled = scaler.transform(X_test[numeric_features])\n", + "\n", + "model = Ridge()\n", + "model.fit(X_train_scaled, y_train)\n", + "y_pred = model.predict(X_test_scaled)\n", + "y_train_pred = model.predict(X_train_scaled)\n", + "\n", + "print(\"Test RMSE = %.4f\" % rmse(y_test, y_pred))\n", + "print(\"Train RMSE = %.4f\" % rmse(y_train, y_train_pred))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "scales = pd.Series(data=X_train_scaled.std(axis=0), index=numeric_features)\n", + "show_weights(numeric_features, model.coef_, scales)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Наряду с параметрами (веса $w$, $w_0$), которые модель оптимизирует на\n", + "этапе обучения, у модели есть и гиперпараметры. У нашей модели это\n", + "`alpha` — коэффициент регуляризации. Подбирают его обычно по\n", + "сетке, измеряя качество на валидационной (не тестовой) выборке или с\n", + "помощью кросс-валидации. Посмотрим, как это можно сделать (заметьте,\n", + "что мы перебираем `alpha` по логарифмической сетке, чтобы узнать\n", + "оптимальный порядок величины)." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import GridSearchCV\n", + "\n", + "alphas = np.logspace(-2, 3, 20)\n", + "searcher = GridSearchCV(Ridge(), [{\"alpha\": alphas}], scoring=rmse_scorer, cv=10)\n", + "searcher.fit(X_train_scaled, y_train)\n", + "\n", + "best_alpha = searcher.best_params_[\"alpha\"]\n", + "print(\"Best alpha = %.4f\" % best_alpha)\n", + "\n", + "plt.plot(alphas, -searcher.cv_results_[\"mean_test_score\"])\n", + "plt.xscale(\"log\")\n", + "plt.xlabel(\"alpha\")\n", + "plt.ylabel(\"CV score\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Попробуем обучить модель с подобранным коэффициентом\n", + "регуляризации. Заодно воспользуемся очень удобным классом\n", + "[`Pipeline`](https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html):\n", + "обучение модели часто представляется как последовательность некоторых\n", + "действий с обучающей и тестовой выборками (например, сначала нужно\n", + "отмасштабировать выборку (причем для обучающей выборки нужно применить\n", + "метод `fit`, а для тестовой --- `transform`), а затем\n", + "обучить/применить модель (для обучающей `fit`, а для тестовой ---\n", + "`predict`). `Pipeline` позволяет хранить эту последовательность шагов\n", + "и корректно обрабатывает разные типы выборок: и обучающую, и\n", + "тестовую." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.pipeline import Pipeline\n", + "\n", + "simple_pipeline = Pipeline([\n", + " ('scaling', StandardScaler()),\n", + " ('regression', Ridge(best_alpha))\n", + "])\n", + "\n", + "model = simple_pipeline.fit(X_train[numeric_features], y_train)\n", + "y_pred = model.predict(X_test[numeric_features])\n", + "print(\"Test RMSE = %.4f\" % rmse(y_test, y_pred))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Работа с категориальными признаками\n", + "
\n", + "\n", + "Сейчас мы явно вытягиваем из данных не всю информацию, что у нас есть,\n", + "просто потому, что мы не используем часть признаков. Эти признаки в\n", + "датасете закодированы строками, каждый из них обозначает некоторую\n", + "категорию. Такие признаки называются категориальными. Давайте выделим\n", + "такие признаки и сразу заполним пропуски в них специальным значением\n", + "(то, что у признака пропущено значение, само по себе может быть\n", + "хорошим признаком)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "categorical = list(X_train.dtypes[X_train.dtypes == \"object\"].index)\n", + "X_train[categorical] = X_train[categorical].fillna(\"NotGiven\")\n", + "X_test[categorical] = X_test[categorical].fillna(\"NotGiven\")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_train[categorical].sample(5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Сейчас нам нужно как-то закодировать эти категориальные признаки\n", + "числами, ведь линейная модель не может работать с такими\n", + "абстракциями. Два стандартных трансформера из `sklearn` для работы с\n", + "категориальными признаками\n", + "* [`LabelEncoder`](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html) просто перенумеровывает значения признака натуральными числами\n", + "\n", + "* [`OneHotEncoder`](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html) ставит в соответствие каждому признаку целый вектор, состоящий из нулей и одной единицы (которая стоит на месте, соответствующем принимаемому значению, таким образом кодируя его)." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import OneHotEncoder\n", + "from sklearn.compose import ColumnTransformer\n", + "\n", + "column_transformer = ColumnTransformer([\n", + " ('ohe', OneHotEncoder(handle_unknown=\"ignore\"), categorical),\n", + " ('scaling', StandardScaler(), numeric_features)\n", + "])\n", + "\n", + "pipeline = Pipeline(steps=[\n", + " ('ohe_and_scaling', column_transformer),\n", + " ('regression', Ridge())\n", + "])\n", + "\n", + "model = pipeline.fit(X_train, y_train)\n", + "y_pred = model.predict(X_test)\n", + "print(\"Test RMSE = %.4f\" % rmse(y_test, y_pred))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Посмотрим на размеры матрицы после OneHot-кодирования:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(\"Size before OneHot:\", X_train.shape)\n", + "print(\"Size after OneHot:\", column_transformer.transform(X_train).shape)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Как видим, количество признаков увеличилось более, чем в 3 раза. Это\n", + "может повысить риски переобучиться: соотношение количества объектов к\n", + "количеству признаков сильно сократилось. \n", + "\n", + "Попытаемся обучить линейную регрессию с $\\ell_1$-регуляризатором. На\n", + "лекциях вы узнаете, что $\\ell_1$-регуляризатор разреживает признаковое\n", + "пространство, иными словами, такая модель зануляет часть весов." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import Lasso\n", + "\n", + "column_transformer = ColumnTransformer([\n", + " ('ohe', OneHotEncoder(handle_unknown=\"ignore\"), categorical),\n", + " ('scaling', StandardScaler(), numeric_features)\n", + "])\n", + "\n", + "lasso_pipeline = Pipeline(steps=[\n", + " ('ohe_and_scaling', column_transformer),\n", + " ('regression', Lasso())\n", + "])\n", + "\n", + "model = lasso_pipeline.fit(X_train, y_train)\n", + "y_pred = model.predict(X_test)\n", + "print(\"RMSE = %.4f\" % rmse(y_test, y_pred))" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "ridge_zeros = np.sum(pipeline.steps[-1][-1].coef_ == 0)\n", + "lasso_zeros = np.sum(lasso_pipeline.steps[-1][-1].coef_ == 0)\n", + "print(\"Zero weights in Ridge:\", ridge_zeros)\n", + "print(\"Zero weights in Lasso:\", lasso_zeros)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Подберем для нашей модели оптимальный коэффициент\n", + "регуляризации. Обратите внимание, как перебираются параметры у\n", + "`Pipeline`." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "alphas = np.logspace(-2, 4, 20)\n", + "searcher = GridSearchCV(lasso_pipeline, [{\"regression__alpha\": alphas}], scoring=rmse_scorer, cv=10)\n", + "searcher.fit(X_train, y_train)\n", + "\n", + "best_alpha = searcher.best_params_[\"regression__alpha\"]\n", + "print(\"Best alpha = %.4f\" % best_alpha)\n", + "\n", + "plt.plot(alphas, -searcher.cv_results_[\"mean_test_score\"])\n", + "plt.xscale(\"log\")\n", + "plt.xlabel(\"alpha\")\n", + "plt.ylabel(\"CV score\")" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "column_transformer = ColumnTransformer([\n", + " ('ohe', OneHotEncoder(handle_unknown=\"ignore\"), categorical),\n", + " ('scaling', StandardScaler(), numeric_features)\n", + "])\n", + "\n", + "pipeline = Pipeline(steps=[\n", + " ('ohe_and_scaling', column_transformer),\n", + " ('regression', Lasso(best_alpha))\n", + "])\n", + "\n", + "model = pipeline.fit(X_train, y_train)\n", + "y_pred = model.predict(X_test)\n", + "print(\"Test RMSE = %.4f\" % rmse(y_test, y_pred))" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "lasso_zeros = np.sum(pipeline.steps[-1][-1].coef_ == 0)\n", + "print(\"Zero weights in Lasso:\", lasso_zeros)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Иногда очень полезно посмотреть на распределение остатков. Нарисуем\n", + "гистограмму распределения квадратичной ошибки на обучающих объектах:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "error = (y_train - model.predict(X_train)) ** 2\n", + "sns.distplot(error)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Как видно из гистограммы, есть примеры с очень большими\n", + "остатками. Попробуем их выбросить из обучающей выборки. Например,\n", + "выбросим примеры, остаток у которых больше 0.95-квантили." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "mask = (error < np.quantile(error, 0.95))" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "column_transformer = ColumnTransformer([\n", + " ('ohe', OneHotEncoder(handle_unknown=\"ignore\"), categorical),\n", + " ('scaling', StandardScaler(), numeric_features)\n", + "])\n", + "\n", + "pipeline = Pipeline(steps=[\n", + " ('ohe_and_scaling', column_transformer),\n", + " ('regression', Lasso(best_alpha))\n", + "])\n", + "\n", + "model = pipeline.fit(X_train[mask], y_train[mask])\n", + "y_pred = model.predict(X_test)\n", + "print(\"Test RMSE = %.4f\" % rmse(y_test, y_pred))" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_train = X_train[mask]\n", + "y_train = y_train[mask]" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "error = (y_train - model.predict(X_train)) ** 2\n", + "sns.distplot(error)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Видим, что качество модели заметно улучшилось! Также бывает очень\n", + "полезно посмотреть на примеры с большими остатками и попытаться\n", + "понять, почему же модель на них так сильно ошибается: это может дать\n", + "понимание, как модель можно улучшить. \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "# Предобработка данных\n", + "
\n", + "\n", + "\n", + "Начнем с подключения необходимых библиотек и модулей:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import seaborn as sns\n", + "from tqdm import tqdm\n", + "from sklearn.datasets import fetch_20newsgroups\n", + "\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.linear_model import Ridge\n", + "from sklearn.metrics import mean_squared_error" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Работа с текстовыми данными\n", + "
\n", + "\n", + "\n", + "Как правило, модели машинного обучения действуют в предположении, что\n", + "матрица «объект-признак» является вещественнозначной, поэтому при\n", + "работе с текстами сперва для каждого из них необходимо составить его\n", + "признаковое описание. Для этого широко используются техники\n", + "векторизации, tf-idf и пр.\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Сперва загрузим данные:" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = fetch_20newsgroups(subset='all', categories=['comp.graphics', 'sci.med'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Данные содержат тексты новостей, которые надо классифицировать на разделы." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data['target_names']" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "texts = data['data']\n", + "target = data['target']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Например:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "texts[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data['target_names'][target[0]]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Bag-of-words\n", + "\n", + "
\n", + "\n", + "Самый очевидный способ формирования признакового описания текстов —\n", + "векторизация. Простой способ заключается в подсчёте, сколько раз встретилось каждое слово\n", + "в тексте. Получаем вектор длиной в количество уникальных слов, встречающихся во\n", + "всех объектах выборки. В таком векторе много нулей, поэтому его удобнее хранить\n", + "в разреженном виде. \n", + "\n", + "Пусть у нас имеется коллекция текстов $D = \\{d_i\\}_{i=1}^l$\n", + "и словарь всех слов, встречающихся в выборке $V = \\{v_j\\}_{j=1}^d.$ В\n", + "этом случае некоторый текст $d_i$ описывается вектором\n", + "$(x_{ij})_{j=1}^d,$ где" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "x_{ij} = \\sum_{v \\in d_i} [v = v_j].\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Таким образом, текст $d_i$ описывается вектором количества вхождений\n", + "каждого слова из словаря в данный текст." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.feature_extraction.text import CountVectorizer\n", + "\n", + "vectorizer = CountVectorizer(encoding='utf8', min_df=1)\n", + "_ = vectorizer.fit(texts)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Результатом является разреженная матрица." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "vectorizer.transform(texts[:1])" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(vectorizer.transform(texts[:1]).indptr)\n", + "print(vectorizer.transform(texts[:1]).indices)\n", + "print(vectorizer.transform(texts[:1]).data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Такой способ представления текстов называют *мешком слов* (bag-of-words).\n", + "\n", + "### TF-IDF\n", + "\n", + "
\n", + "\n", + "Очевидно, что не все слова полезны в задаче прогнозирования. Например, мало\n", + "информации несут слова, встречающиеся во всех текстах. Это могут быть\n", + "как стоп-слова, так и слова, свойственные всем текстам выборки (в\n", + "текстах про автомобили употребляется слово «автомобиль»). Эту проблему\n", + "решает TF-IDF (*T*erm *F*requency–*I*nverse *D*ocument *F*requency)\n", + "преобразование текста.\n", + "\n", + "Рассмотрим коллекцию текстов $D$. Для каждого уникального слова $t$\n", + "из документа $d \\in D$ вычислим следующие величины: \n", + "\n", + "* TD (Term Frequency) – количество вхождений слова в отношении к общему числу слов в тексте:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\textrm{tf}(t, d) = \\frac{n_{td}}{\\sum_{t \\in d} n_{td}},\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "где $n_{td}$ — количество вхождений слова $t$ в текст $d$.\n", + "\n", + "* IDF (Inverse Document Frequency):" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\textrm{idf}(t, D) = \\log \\frac{\\left| D \\right|}{\\left| \\{d\\in D: t \\in d\\} \\right|},\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "где $\\left| \\{d\\in D: t \\in d\\} \\right|$ – количество текстов в коллекции, содержащих слово $t$.\n", + "\n", + "Тогда для каждой пары (слово, текст) $(t, d)$ вычислим величину:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\textrm{tf-idf}(t,d, D) = \\text{tf}(t, d)\\cdot \\text{idf}(t, D).\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Отметим, что значение $\\text{tf}(t, d)$ корректируется для часто\n", + "встречающихся общеупотребимых слов при помощи значения\n", + "$\\textrm{idf}(t, D)$." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.feature_extraction.text import TfidfVectorizer\n", + "\n", + "vectorizer = TfidfVectorizer(encoding='utf8', min_df=1)\n", + "_ = vectorizer.fit(texts)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "На выходе получаем разреженную матрицу." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "vectorizer.transform(texts[:1])" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(vectorizer.transform(texts[:1]).indptr)\n", + "print(vectorizer.transform(texts[:1]).indices)\n", + "print(vectorizer.transform(texts[:1]).data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Заметим, что оба метода возвращают вектор длины 32548 (размер нашего словаря).\n", + "\n", + "Заметим, что одно и то же слово может встречаться в различных формах\n", + "(например, «сотрудник» и «сотрудника»), но описанные выше методы\n", + "интерпретируют их как различные слова, что делает признаковое описание\n", + "избыточным. Устранить эту проблему можно при помощи **лемматизации** и\n", + "**стемминга**. \n", + "\n", + "\n", + "### Стемминг\n", + "\n", + "
\n", + "\n", + "*Стемминг* — это процесс нахождения основы слова. В результате применения\n", + "данной процедуры однокоренные слова, как правило, преобразуются к одинаковому\n", + "виду. \n", + "\n", + "\n", + "## Таблица 1 : Примеры стемминга\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
Слово Основа
вагон вагон
вагона вагон
вагоне вагон
вагонов вагон
вагоном вагон
вагоны вагон
важная важн
важнее важн
важнейшие важн
важнейшими важн
важничал важнича
важно важн
\n", + "\n", + "\n", + "\n", + "[Snowball](http://snowball.tartarus.org/) — фрэймворк для написания\n", + "алгоритмов стемминга (библиотека `nltk`). Алгоритмы стемминга отличаются для разных языков\n", + "и используют знания о конкретном языке — списки окончаний для разных\n", + "чистей речи, разных склонений и т.д. Пример алгоритма для русского\n", + "языка – [Russian stemming](http://snowballstem.org/algorithms/russian/stemmer.html)." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import nltk\n", + "stemmer = nltk.stem.snowball.RussianStemmer()" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(stemmer.stem(u'машинное'), stemmer.stem(u'обучение'))" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "stemmer = nltk.stem.snowball.EnglishStemmer()\n", + "\n", + "def stem_text(text, stemmer):\n", + " tokens = text.split()\n", + " return ' '.join(map(lambda w: stemmer.stem(w), tokens))\n", + "\n", + "stemmed_texts = []\n", + "for t in tqdm(texts[:1000]):\n", + " stemmed_texts.append(stem_text(t, stemmer))" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(texts[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(stemmed_texts[0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Как видим, стеммер работает не очень быстро и запускать его для всей\n", + "выборки достаточно накладно. \n", + "\n", + "\n", + "### Лематизация\n", + "\n", + "
\n", + "\n", + "*Лемматизация* — процесс приведения слова к его нормальной форме (лемме):\n", + "* для существительных — именительный падеж, единственное число;\n", + "\n", + "* для прилагательных — именительный падеж, единственное число, мужской род;\n", + "\n", + "* для глаголов, причастий, деепричастий — глагол в инфинитиве.\n", + "\n", + "Лемматизация — процесс более сложный по сравнению со стеммингом. Стеммер\n", + "просто «режет» слово до основы.\n", + "\n", + "Например, для русского языка есть библиотека `pymorphy2`." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "import pymorphy2\n", + "morph = pymorphy2.MorphAnalyzer()" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "morph.parse('играющих')[0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Сравним работу стеммера и лемматизатора на примере:" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "stemmer = nltk.stem.snowball.RussianStemmer()\n", + "print(stemmer.stem('играющих'))" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(morph.parse('играющих')[0].normal_form)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Трансформация признаков и целевой переменной\n", + "
\n", + "\n", + "Разберёмся, как может влиять трансформация признаков или целевой\n", + "переменной на качество модели.\n", + "\n", + "### Логарифмирование\n", + "\n", + "
\n", + "\n", + "Воспользуется датасетом с ценами на дома, с которым мы уже\n", + "сталкивались ранее\n", + "([House Prices: Advanced Regression Techniques](https://www.kaggle.com/c/house-prices-advanced-regression-techniques/overview))." + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "!wget https://slemeshevsky.github.io/python-course/ml/html/src-ml/train.csv" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = pd.read_csv('train.csv')\n", + "\n", + "data = data.drop(columns=[\"Id\"])\n", + "y = data[\"SalePrice\"]\n", + "X = data.drop(columns=[\"SalePrice\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Посмотрим на распределение целевой переменной" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 5))\n", + "\n", + "plt.subplot(1, 2, 1)\n", + "sns.distplot(y, label='target')\n", + "plt.title('target')\n", + "\n", + "plt.subplot(1, 2, 2)\n", + "sns.distplot(data.GrLivArea, label='area')\n", + "plt.title('area')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Видим, что распределения несимметричные с тяжёлыми правыми хвостами.\n", + "\n", + "Оставим только числовые признаки, пропуски заменим средним значением." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_train, X_test, y_train, y_test = train_test_split(\n", + " X, y, test_size=0.3, random_state=10)\n", + "\n", + "numeric_data = X_train.select_dtypes([np.number])\n", + "numeric_data_mean = numeric_data.mean()\n", + "numeric_features = numeric_data.columns\n", + "\n", + "X_train = X_train.fillna(numeric_data_mean)[numeric_features]\n", + "X_test = X_test.fillna(numeric_data_mean)[numeric_features]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Если разбирать линейную регрессия с\n", + "вероятностной точки зрения, то можно получить, что шум должен быть\n", + "распределён нормально. Поэтому лучше, когда целевая переменная\n", + "распределена также нормально.\n", + "\n", + "Если прологарифмировать целевую переменную, то её распределение станет\n", + "больше похоже на нормальное:" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.distplot(np.log(y+1), label='target')\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Сравним качество линейной регрессии в двух случаях:\n", + "* Целевая переменная без изменений.\n", + "\n", + "* Целевая переменная прологарифмирована.\n", + "\n", + "> **Предупреждение.**\n", + ">\n", + "> Не забудем во втором случае взять экспоненту от предсказаний!" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = Ridge()\n", + "model.fit(X_train, y_train)\n", + "y_pred = model.predict(X_test)\n", + "\n", + "print(\"Test RMSE = %.4f\" % mean_squared_error(y_test, y_pred) ** 0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = Ridge()\n", + "model.fit(X_train, np.log(y_train+1))\n", + "y_pred = np.exp(model.predict(X_test))-1\n", + "\n", + "print(\"Test RMSE = %.4f\" % mean_squared_error(y_test, y_pred) ** 0.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Попробуем аналогично логарифмировать один из признаков, имеющих также\n", + "смещённое распределение (этот признак был вторым по важности!)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_train.GrLivArea = np.log(X_train.GrLivArea + 1)\n", + "X_test.GrLivArea = np.log(X_test.GrLivArea + 1)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = Ridge()\n", + "model.fit(X_train[numeric_features], y_train)\n", + "y_pred = model.predict(X_test[numeric_features])\n", + "\n", + "print(\"Test RMSE = %.4f\" % mean_squared_error(y_test, y_pred) ** 0.5)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "model = Ridge()\n", + "model.fit(X_train[numeric_features], np.log(y_train+1))\n", + "y_pred = np.exp(model.predict(X_test[numeric_features]))-1\n", + "\n", + "print(\"Test RMSE = %.4f\" % mean_squared_error(y_test, y_pred) ** 0.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Как видим, преобразование признаков влияет слабее. Признаков много, а\n", + "вклад размывается по всем. К тому же, проверять распределение\n", + "множества признаков технически сложнее, чем одной целевой переменной. \n", + "\n", + "## Бинаризация\n", + "
\n", + "\n", + "Мы уже смотрели, как полиномиальные признаки могут помочь при\n", + "восстановлении нелинейной зависимости линейной моделью. Альтернативный\n", + "подход заключается в бинаризации признаков. Мы разбиваем ось значений\n", + "одного из признаков на куски (бины) и добавляем для каждого куска-бина\n", + "новый признак-индикатор попадения в этот бин." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import LinearRegression\n", + "\n", + "np.random.seed(36)\n", + "X = np.random.uniform(0, 1, size=100)\n", + "y = np.cos(1.5 * np.pi * X) + np.random.normal(scale=0.1, size=X.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "plt.scatter(X, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = X.reshape((-1, 1))\n", + "thresholds = np.arange(0.2, 1.1, 0.2).reshape((1, -1))\n", + "\n", + "X_expand = np.hstack((\n", + " X,\n", + " ((X > thresholds[:, :-1]) & (X <= thresholds[:, 1:])).astype(int)))" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import KFold\n", + "from sklearn.model_selection import cross_val_score" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "-np.mean(cross_val_score(\n", + " LinearRegression(), X, y, cv=KFold(n_splits=3, random_state=123),\n", + " scoring='neg_mean_squared_error'))" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "-np.mean(cross_val_score(\n", + " LinearRegression(), X_expand, y, cv=KFold(n_splits=3, random_state=123),\n", + " scoring='neg_mean_squared_error'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Так линейная модель может лучше восстанавливать нелинейные зависимости.\n", + "\n", + "## Транзакционные данные\n", + "
\n", + "\n", + "Напоследок посмотрим, как можно извлекать признаки из транзакционных данных.\n", + "\n", + "Транзакционные данные характеризуются тем, что есть много строк,\n", + "характеризующихся моментов времени и некоторым числом (суммой денег,\n", + "например). При этом если это банк, то каждому человеку принадлежит не\n", + "одна транзакция, а чаще всего надо предсказывать некоторые сущности\n", + "для клиентов. Таким образом, надо получить признаки для пользователей\n", + "из множества их транзакций. Этим мы и займёмся. \n", + "\n", + "Для примера возьмём данные [отсюда](https://www.kaggle.com/regivm/retailtransactiondata/). Задача\n", + "детектирования фродовых клиентов." + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "!wget https://slemeshevsky.github.io/python-course/ml/html/src-ml/Retail_Data_Response.csv\n", + "!wget https://slemeshevsky.github.io/python-course/ml/html/src-ml/Retail_Data_Transactions.csv" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "customers = pd.read_csv('Retail_Data_Response.csv')\n", + "transactions = pd.read_csv('Retail_Data_Transactions.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "customers.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "transactions.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "transactions.trans_date = transactions.trans_date.apply(\n", + " lambda x: datetime.datetime.strptime(x, '%d-%b-%y'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Посмотрим на распределение целевой переменной:" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "customers.response.mean()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Получаем примерно 1 к 9 положительных примеров. Если такие данные\n", + "разбивать на части для кросс валидации, то может получиться так, что в\n", + "одну из частей попадёт слишком мало положительных примеров, а в другую\n", + "— наоборот. На случай такого неравномерного баланса классов есть\n", + "`StratifiedKFold`, который бьёт данные так, чтобы баланс классов во всех\n", + "частях был одинаковым." + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import StratifiedKFold" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Когда строк на каждый объект много, можно считать различные\n", + "статистики. Например, средние, минимальные и максимальные суммы,\n", + "потраченные клиентом, количество транзакий, ..." + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "agg_transactions = transactions.groupby('customer_id').tran_amount.agg(\n", + " ['mean', 'std', 'count', 'min', 'max']).reset_index()\n", + "\n", + "data = pd.merge(customers, agg_transactions, how='left', on='customer_id')\n", + "\n", + "data.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "np.mean(cross_val_score(\n", + " LogisticRegression(),\n", + " X=data.drop(['customer_id', 'response'], axis=1),\n", + " y=data.response,\n", + " cv=StratifiedKFold(n_splits=3, random_state=123),\n", + " scoring='roc_auc'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Но каждая транзакция снабжена датой! Можно посчитать статистики только\n", + "по свежим транзакциям. Добавим их." + ] + }, + { + "cell_type": "code", + "execution_count": 82, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "transactions.trans_date.min(), transactions.trans_date.max()" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "agg_transactions = transactions.loc[transactions.trans_date.apply(\n", + " lambda x: x.year == 2014)].groupby('customer_id').tran_amount.agg(\n", + " ['mean', 'std', 'count', 'min', 'max']).reset_index()" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "data = pd.merge(data, agg_transactions, how='left', on='customer_id', suffixes=('', '_2014'))\n", + "data = data.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "np.mean(cross_val_score(\n", + " LogisticRegression(),\n", + " X=data.drop(['customer_id', 'response'], axis=1),\n", + " y=data.response,\n", + " cv=StratifiedKFold(n_splits=3, random_state=123),\n", + " scoring='roc_auc'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Можно также считать дату первой и последней транзакциями\n", + "пользователей, среднее время между транзакциями и прочее. \n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "# Простые модели классификации\n", + "
\n", + "\n", + "*Классификация* — отнесение объекта к одной из категорий на основании его признаков.\n", + "\n", + "Рассмотрим задачу бинарной классификации. Пусть $X = \\mathbb{R}^d$ —\n", + "пространство объектов, $Y = {−1, +1}$ — множество допустимых ответов,\n", + "$X = {(x_i , y_i )}_{i=1}^{\\ell}$ — обучающая выборка. Иногда мы будем \n", + "класс «+1» называть положительным, а класс «−1» — отрицательным.\n", + "\n", + "Будем считать, что классификатор имеет вид" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "a(x) = \\mathrm{sign}(b(x)−t) = 2[b(x) > t] − 1.\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "В такого рода задачах возникает необходимость в изучении различных\n", + "аспектов качества уже обученного классификатора. Сначала обсудим один\n", + "из подходов к измерению качества таких моделей.\n", + "\n", + "## Матрица ошибок\n", + "
\n", + "\n", + "*Матрица ошибок* — это способ разбить объекты на четыре категории в\n", + "зависимости от комбинации истинного ответа и ответа алгоритма\n", + "(см. таблицу [ml:class:tbl:2](#ml:class:tbl:2)). Через элементы этой матрицы можно,\n", + "например, выразить долю правильных ответов:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\text{accuracy} = \\frac{\\mathrm{TP} + \\mathrm{TN}}{\\mathrm{TP} +\n", + "\\mathrm{FP} +\\mathrm{FN} + \\mathrm{TN}}.\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Таблица 2 : Матрица ошибок
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
$y=1$ $y = -1$
TP (True Positive) FP (False Positive)
FN (False Negative) TN (True Negatiive)
\n", + "\n", + "\n", + "Данная матрика имеет существенный недостаток — её значение необходимо\n", + "оценивать в контексте баланса классов. Eсли в выборке $950$\n", + "отрицательных и $50$ положительных объектов, то при абсолютно случайной\n", + "классификации мы получим долю правильных ответов $0.95$. Это означает,\n", + "что доля положительных ответов сама по себе не несет никакой\n", + "информации о качестве работы алгоритма $a(x)$, и вместе с ней следует \n", + "-анализировать соотношение классов в выборке. \n", + "\n", + "Гораздо более информативными критериями являются *точность* (precision)\n", + "и *полнота* (recall).\n", + "\n", + "Точность показывает, какая доля объектов, выделенных классификатором\n", + "как положительные, действительно является положительными:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\text{precision} = \\frac{\\mathrm{TP}}{\\mathrm{TP} + \\mathrm{FP}}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Полнота показывает, какая часть положительных объектов была выделена\n", + "классификатором:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "\\text{precision} = \\frac{\\mathrm{TP}}{\\mathrm{TP} + \\mathrm{FN}}\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Существует несколько способов получить один критерий качества на основе\n", + "точности и полноты. Один из них — $F$-мера, гармоническое среднее\n", + "точности и полноты:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$$\n", + "F_\\beta = (1+\\beta^2) \\frac{\\text{precision}\\cdot\n", + "\\text{recall}}{\\beta^2 \\cdot \\text{precision} + \n", + "\\text{recall}}.\n", + "$$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Среднее гармоническое обладает важным свойством — оно близко к нулю,\n", + "если хотя бы один из аргументов близок к нулю. Именно поэтому оно\n", + "является более предпочтительным, чем среднее арифметическое (если\n", + "алгоритм будет относить все объекты к положительному классу, то он\n", + "будет иметь $\\text{recall} = 1$ и $\\text{precision} > 0$, а их среднее\n", + "арифметическое будет больше $1/2$, что недопустимо).\n", + "\n", + "Чаще всего берут $\\beta = 1$ хотя иногда встречаются и другие\n", + "модификации. $F_2$ острее реагирует на recall (т. е. на долю\n", + "ложноположительных ответов), а $F_{0.5}$ чувствительнее к точности\n", + "(ослабляет влияние ложноположительных ответов).\n", + "\n", + "В `sklearn` есть удобная функция\n", + "`sklearn.metrics.classification_report`, которая возвращает recall,\n", + "precision и $F$-меру для каждого из классов, а также количество\n", + "экземпляров каждого класса." + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.metrics import classification_report\n", + "y_true = [0, 1, 2, 2, 2]\n", + "y_pred = [0, 0, 2, 2, 1]\n", + "target_names = ['class 0', 'class 1', 'class 2']\n", + "print(classification_report(y_true, y_pred, target_names=target_names))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Линейная классификация\n", + "
\n", + "\n", + "Основная идея линейного классификатора заключается в том, что\n", + "признаковое пространство может быть разделено гиперплоскостью на две\n", + "полуплоскости, в каждой из которых прогнозируется одно из двух\n", + "значений целевого класса. Если это можно сделать без ошибок, то\n", + "обучающая выборка называется *линейно разделимой*.\n", + "\n", + "\n", + "\n", + "![](fig-ml/lin-class.png)\n", + "\n", + "\n", + "Указанная разделяющая плоскость называется *линейным дискриминантом*.\n", + "\n", + "\n", + "### Логистическая регрессия\n", + "\n", + "
\n", + "\n", + "*Логистическая регрессия* является частным случаем линейного\n", + "классификатора, но она обладает хорошим «умением» – прогнозировать\n", + "вероятность отнесения наблюдения к классу. Таким образом, результат\n", + "логистической регрессии всегда находится на отрезке $[0, 1]$. Возьмем\n", + "данные по [ирисам](https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv)" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "!wget https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv" + ] + }, + { + "cell_type": "code", + "execution_count": 88, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "iris = pd.read_csv(\"https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv\")" + ] + }, + { + "cell_type": "code", + "execution_count": 89, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "iris.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.pairplot(iris, hue=\"species\")" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "sns.lmplot(x=\"petal_length\", y=\"petal_width\", data=iris)" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X = iris.iloc[:, 2:4].values\n", + "y = iris['species'].values" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "y[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import LabelEncoder\n", + "\n", + "le = LabelEncoder()\n", + "le.fit(y)\n", + "y = le.transform(y)\n", + "y[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "iris_pred_names = le.classes_\n", + "iris_pred_names" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(\n", + " X, y, test_size=0.3, random_state=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "sc = StandardScaler()\n", + "sc.fit(X_train)\n", + "X_train_std = sc.transform(X_train)\n", + "X_test_std = sc.transform(X_test)" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_train[:5], X_train_std[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "from sklearn.linear_model import LogisticRegression\n", + "\n", + "lr = LogisticRegression(C=100.0, random_state=1)\n", + "lr.fit(X_train_std, y_train)" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "lr.predict_proba(X_test_std[:3, :])" + ] + }, + { + "cell_type": "code", + "execution_count": 101, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "lr.predict_proba(X_test_std[:3, :]).sum(axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "y_test[:3]" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "lr.predict_proba(X_test_std[:3, :]).argmax(axis=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Предсказываем класс первого наблюдения" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "lr.predict(X_test_std[0, :].reshape(1, -1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "На основе его коэффициентов:" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_test_std[0, :]" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "X_test_std[0, :].reshape(1, -1)" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "y_pred = lr.predict(X_test_std)" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "len(iris_pred_names)" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "print(classification_report(y_test, y_pred, target_names=iris_pred_names))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "# Задание\n", + "
\n", + "\n", + "Задание состоит из двух основных частей. В первой части необходимо сделать\n", + "простой препроцессинг и произвести разведывательный анализ данных. \n", + "\n", + "Во второй части у Вас будет выбор между двумя вариантами: Вы можете\n", + "провести регрессионный анализ данных или заняться обработкой\n", + "естественного языка и построением классификатора текстов.\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "## Задание по базе wine\n", + "
\n", + "\n", + "\n", + "**a)**\n", + "**Загрузка и разведывательный анализ.**\n", + "* Загрузите данные ([скачать](src-ml/wine_reviews.csv.zip)).\n", + "\n", + "* Посчитайте размерность данных.\n", + "\n", + "* Посчитайте количество пропущенных значений в каждой переменной.\n", + "\n", + "* Выведите тип данных каждой переменной. Переконвертируйте при необходимости.\n", + "\n", + "* Вина какой области (province) получают наилучшие рейтинги?\n", + "\n", + "* На основе словаря color оздайте переменную, в которой закодирован цвет вина.\n", + "\n", + "* Удалите наблюдения для которых цвет (color) не указан.\n", + "\n", + "* Визуализируйте распределения числовых переменных.\n", + "\n", + "* Для каждой страны рассчитайте долю каждого вида вина. В какой стране доля белого вина наибольшая, а в какой красного? (Нужен ответ вида: в стране А наибольшая доля белого вина, а в стране B — красного.\n", + "\n", + "* Разделите выборку на обучающую и тестовую\n", + "\n", + "**b)**\n", + "**Регрессионная модель.**\n", + "* На обучающей выборке постройте регрессионную модель, показывающую зависимость между баллом (зависимая переменная) и ценой. Визуализируйте эту зависимость. На сколько изменяется оценка при изменении цены на одну условную единицу?\n", + "\n", + "* Оцените качество модели на основе предсказаний по тестовой выборке по помощи стандартных метрик качества для регрессионных моделей.\n", + "\n", + "* Добавьте в модель переменную, в которой закодирован цвет вина. Как изменилось качество?\n", + "\n", + "ИЛИ\n", + "\n", + "**c)**\n", + "**Классификация текстов.**\n", + "* Сделайте препроцессинг текстов в поле description.\n", + "\n", + "* На обучающей выборке постройте модель классификации текста, которая бы классифицировала вина по цвету на основе текстов из описания.\n", + "\n", + "* Оцените качество работы модели по помощи стандартных метрик качества для алгоритмов классификации. Использование автоматических методов подбора параметров (Grid Search) не обязательно, но в случае наличия — зачтётся.\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Имя файла: `task_surname.ipynb`.\n", + "\n", + "" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/numpy.ipynb b/numpy.ipynb index 2f97e72..e64c3fb 100644 --- a/numpy.ipynb +++ b/numpy.ipynb @@ -142,7 +142,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "collapsed": false, "jupyter": { @@ -156,7 +156,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "collapsed": false, "jupyter": { @@ -170,7 +170,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": { "collapsed": false, "jupyter": { @@ -184,28 +184,46 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 32.3 ms, sys: 16.8 ms, total: 49.1 ms\n", + "Wall time: 126 ms\n" + ] + } + ], "source": [ "%time for _ in range(10): my_arr2 = my_arr*2" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 817 ms, sys: 172 ms, total: 989 ms\n", + "Wall time: 1.06 s\n" + ] + } + ], "source": [ "%time for _ in range(10): my_list2 = [x * 2 for x in my_list]" ] @@ -228,7 +246,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": { "collapsed": false, "jupyter": { @@ -242,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": { "collapsed": false, "jupyter": { @@ -256,42 +274,78 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 0.62955711, 0.22558868, -0.15732107],\n", + " [-1.23977365, -0.04357442, 0.82177343]])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 6.29557109, 2.25588682, -1.57321072],\n", + " [-12.39773646, -0.43574416, 8.21773429]])" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data * 10" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1.25911422, 0.45117736, -0.31464214],\n", + " [-2.47954729, -0.08714883, 1.64354686]])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data + data" ] @@ -308,28 +362,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(2, 3)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data.shape" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('float64')" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "data.dtype" ] @@ -349,7 +425,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": { "collapsed": false, "jupyter": { @@ -363,7 +439,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": { "collapsed": false, "jupyter": { @@ -377,16 +453,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([6. , 7.5, 8. , 0. , 1. ])" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "In [15]: arr1" + "arr1" ] }, { @@ -399,7 +486,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": { "collapsed": false, "jupyter": { @@ -413,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": { "collapsed": false, "jupyter": { @@ -422,19 +509,31 @@ }, "outputs": [], "source": [ - "arr2 = np.array(data2)" + "arr2 = np.array(data2, dtype='float64')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 2., 3., 4.],\n", + " [5., 6., 7., 8.]])" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2" ] @@ -451,17 +550,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "dtype('float64')" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "In [19]: arr1.dtype\n", - "In [20]: arr2.dtype" + "In [19]: arr2.dtype" ] }, { @@ -474,60 +583,133 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 31, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.zeros(10)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0., 0.]])" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.zeros((3, 6))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[4.68232729e-310, 0.00000000e+000],\n", + " [0.00000000e+000, 0.00000000e+000],\n", + " [0.00000000e+000, 0.00000000e+000]],\n", + "\n", + " [[0.00000000e+000, 0.00000000e+000],\n", + " [0.00000000e+000, 0.00000000e+000],\n", + " [0.00000000e+000, 0.00000000e+000]]])" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.empty((2, 3, 2))" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.arange(15)" ] }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 1., 1., 1.],\n", + " [1., 1., 1., 1.]])" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.empty_like(arr2)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -568,7 +750,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": { "collapsed": false, "jupyter": { @@ -582,7 +764,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": { "collapsed": false, "jupyter": { @@ -596,42 +778,78 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1., 2., 3.],\n", + " [4., 5., 6.]])" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "In [3]: arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1., 4., 9.],\n", + " [16., 25., 36.]])" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr * arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0., 0., 0.],\n", + " [0., 0., 0.]])" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr - arr" ] @@ -646,35 +864,59 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1. , 0.5 , 0.33333333],\n", + " [0.25 , 0.2 , 0.16666667]])" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "1 / arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1. , 1.41421356, 1.73205081],\n", + " [2. , 2.23606798, 2.44948974]])" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr ** 0.5" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 46, "metadata": { "collapsed": false, "jupyter": { @@ -688,30 +930,54 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 0., 4., 1.],\n", + " [ 7., 2., 12.]])" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[False, True, False],\n", + " [ True, False, True]])" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "arr2 > arrщ" + "arr2 > arr" ] }, { @@ -728,7 +994,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 49, "metadata": { "collapsed": false, "jupyter": { @@ -742,49 +1008,82 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 50, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 51, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "5" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr[5]" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 52, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([5, 6, 7])" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr[5:8]" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 53, "metadata": { "collapsed": false, "jupyter": { @@ -798,14 +1097,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 54, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0, 1, 2, 3, 4, 12, 12, 12, 8, 9])" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] @@ -826,7 +1136,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 55, "metadata": { "collapsed": false, "jupyter": { @@ -840,14 +1150,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 56, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([12, 12, 12])" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr_slice" ] @@ -862,7 +1183,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 57, "metadata": { "collapsed": false, "jupyter": { @@ -876,14 +1197,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 58, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0, 1, 2, 3, 4, 12, 12345, 12, 8,\n", + " 9])" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] @@ -897,7 +1230,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 59, "metadata": { "collapsed": false, "jupyter": { @@ -911,14 +1244,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 61, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] @@ -947,7 +1291,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 62, "metadata": { "collapsed": false, "jupyter": { @@ -961,14 +1305,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 63, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([7, 8, 9])" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2d[2]" ] @@ -984,30 +1339,52 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 64, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([7, 8, 9])" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2d[2]" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 66, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "3" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "arr2d[0][2]" + "arr2d[0, 2]" ] }, { @@ -1021,7 +1398,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 67, "metadata": { "collapsed": false, "jupyter": { @@ -1035,14 +1412,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 1, 2, 3],\n", + " [ 4, 5, 6]],\n", + "\n", + " [[ 7, 8, 9],\n", + " [10, 11, 12]]])" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr3d" ] @@ -1056,14 +1448,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 69, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6]])" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr3d[0]" ] @@ -1077,7 +1481,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 70, "metadata": { "collapsed": false, "jupyter": { @@ -1091,7 +1495,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 71, "metadata": { "collapsed": false, "jupyter": { @@ -1105,21 +1509,36 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 72, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[42, 42, 42],\n", + " [42, 42, 42]],\n", + "\n", + " [[ 7, 8, 9],\n", + " [10, 11, 12]]])" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr3d" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 73, "metadata": { "collapsed": false, "jupyter": { @@ -1133,14 +1552,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 74, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[[ 1, 2, 3],\n", + " [ 4, 5, 6]],\n", + "\n", + " [[ 7, 8, 9],\n", + " [10, 11, 12]]])" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr3d" ] @@ -1155,14 +1589,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 75, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([7, 8, 9])" + ] + }, + "execution_count": 75, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr3d[1, 0]" ] @@ -1176,7 +1621,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 76, "metadata": { "collapsed": false, "jupyter": { @@ -1190,28 +1635,51 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 77, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 7, 8, 9],\n", + " [10, 11, 12]])" + ] + }, + "execution_count": 77, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "x" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 78, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([7, 8, 9])" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "x[0]" ] @@ -1230,28 +1698,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 79, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0, 1, 2, 3, 4, 64, 64, 64, 8, 9])" + ] + }, + "execution_count": 79, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 80, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 3, 4, 64])" + ] + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr[1:6]" ] @@ -1266,30 +1756,55 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 81, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]])" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2d" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 83, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[1, 2, 3],\n", + " [4, 5, 6]])" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "arr[:2]" + "arr2d[:2]" ] }, { @@ -1305,14 +1820,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 84, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[2, 3],\n", + " [5, 6]])" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2d[:2, 1:]" ] @@ -1328,28 +1855,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 85, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([4, 5])" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2d[1, :2]" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 86, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([3, 6])" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr2d[:2, 2]" ] @@ -1380,7 +1929,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 87, "metadata": { "collapsed": false, "jupyter": { @@ -1394,7 +1943,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 88, "metadata": { "collapsed": false, "jupyter": { @@ -1408,28 +1957,56 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 89, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'], dtype='" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], "source": [ - "plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()" + "plt.imshow(z, cmap=plt.cm.gray); plt.colorbar()\n", + "plt.title('Визуализация функции $\\sqrt{x^2 + y^2}$ на сетке')" ] }, { @@ -2634,7 +3789,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 164, "metadata": { "collapsed": false, "jupyter": { @@ -2648,7 +3803,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 165, "metadata": { "collapsed": false, "jupyter": { @@ -2662,7 +3817,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 166, "metadata": { "collapsed": false, "jupyter": { @@ -2686,7 +3841,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 167, "metadata": { "collapsed": false, "jupyter": { @@ -2700,14 +3855,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 168, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[1.1, 2.2, 1.3, 1.4, 2.5]" + ] + }, + "execution_count": 168, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "result" ] @@ -2725,7 +3891,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 169, "metadata": { "collapsed": false, "jupyter": { @@ -2739,16 +3905,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 171, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.1, 2.2, 1.3, 1.4, 2.5])" + ] + }, + "execution_count": 171, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "result = np.where(cond, xarr, yarr)" + "result" ] }, { @@ -2766,7 +3943,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 172, "metadata": { "collapsed": false, "jupyter": { @@ -2780,42 +3957,84 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 173, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.66464048, -1.11134705, 1.37884191, 1.43021577],\n", + " [-1.40300745, -0.75443307, 0.30600186, -0.0735264 ],\n", + " [-0.70828539, 1.0644955 , -1.20046209, -0.70420307],\n", + " [ 1.12269842, -0.43256645, -0.13543629, -0.02699621]])" + ] + }, + "execution_count": 173, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 174, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[False, False, True, True],\n", + " [False, False, True, False],\n", + " [False, True, False, False],\n", + " [ True, False, False, False]])" + ] + }, + "execution_count": 174, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr > 0" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 175, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-2, -2, 2, 2],\n", + " [-2, -2, 2, -2],\n", + " [-2, 2, -2, -2],\n", + " [ 2, -2, -2, -2]])" + ] + }, + "execution_count": 175, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.where(arr > 0, 2, -2)" ] @@ -2831,14 +4050,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 176, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.66464048, -1.11134705, 2. , 2. ],\n", + " [-1.40300745, -0.75443307, 2. , -0.0735264 ],\n", + " [-0.70828539, 2. , -1.20046209, -0.70420307],\n", + " [ 2. , -0.43256645, -0.13543629, -0.02699621]])" + ] + }, + "execution_count": 176, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.where(arr > 0, 2, arr)" ] @@ -2863,7 +4096,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 177, "metadata": { "collapsed": false, "jupyter": { @@ -2877,56 +4110,104 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 178, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-1.2167025 , 0.26684004, 0.37933051, 0.92982079],\n", + " [-0.02473966, 0.73980057, -0.44155996, -2.06986949],\n", + " [ 0.03720622, 0.92436984, -1.58246901, -0.28881663],\n", + " [-0.06352456, 0.55178233, -1.43402474, 2.07847906],\n", + " [-0.55224505, -0.40871248, 0.78480968, -1.41514863]])" + ] + }, + "execution_count": 178, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 179, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "-0.14026868401976725" + ] + }, + "execution_count": 179, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.mean()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 180, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "-0.14026868401976725" + ] + }, + "execution_count": 180, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "np.mean(arr)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 181, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "-2.805373680395345" + ] + }, + "execution_count": 181, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.sum()" ] @@ -2942,28 +4223,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 185, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 0.08982221, -0.44909214, -0.2274274 , 0.28317802, -0.39782412])" + ] + }, + "execution_count": 185, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.mean(axis=1)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 186, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([-1.82000556, 2.07408029, -2.29391351, -0.7655349 ])" + ] + }, + "execution_count": 186, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.sum(axis=0)" ] @@ -2981,7 +4284,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 190, "metadata": { "collapsed": false, "jupyter": { @@ -2990,21 +4293,32 @@ }, "outputs": [], "source": [ - "arr = np.array([0, 1, 2, 3, 4, 5, 6, 7])" + "arr = np.array([1, 2, 3, 4, 5, 6, 7])" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 191, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([ 1, 2, 6, 24, 120, 720, 5040])" + ] + }, + "execution_count": 191, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "arr.cumsum()" + "arr.cumprod()" ] }, { @@ -3019,7 +4333,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 193, "metadata": { "collapsed": false, "jupyter": { @@ -3033,42 +4347,81 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 194, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0, 1, 2],\n", + " [3, 4, 5],\n", + " [6, 7, 8]])" + ] + }, + "execution_count": 194, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 195, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 0, 1, 2],\n", + " [ 3, 5, 7],\n", + " [ 9, 12, 15]])" + ] + }, + "execution_count": 195, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.cumsum(axis=0)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 196, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 0, 0, 0],\n", + " [ 3, 12, 60],\n", + " [ 6, 42, 336]])" + ] + }, + "execution_count": 196, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.cumprod(axis=1)" ] @@ -3117,28 +4470,70 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 199, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([-1.32390275, -1.0677737 , -0.75591368, -0.4284737 , 1.60423034,\n", + " 0.69672924, 1.68048259, 0.43237566, -0.9369892 , -1.11156679,\n", + " 0.58929476, 0.08824475, 0.4212576 , 0.39878124, 0.7383381 ,\n", + " -0.35939768, 0.16423203, -0.92236598, 0.46042767, -0.98861342,\n", + " -0.18848097, -0.79606263, 0.83448883, -1.8489933 , 0.11509521,\n", + " -0.52785657, 0.16546174, 1.62477592, 1.04921672, -0.08406352,\n", + " 1.33675875, -0.36904779, -0.25620027, 1.06950333, -0.05524866,\n", + " 2.31922315, -0.17508064, 0.51216759, -0.31534566, -1.61432312,\n", + " -0.61582557, -0.38697939, -0.28057501, -1.32700842, 0.08026122,\n", + " 0.38809548, 0.30056911, 0.90816488, -0.14090822, -0.46949723,\n", + " -0.26949007, 0.23234959, -1.01386424, 0.37079297, -0.92979541,\n", + " -0.36825517, -1.51214788, -0.56054003, -0.84206451, 0.39224787,\n", + " -0.77837871, -1.79345594, 1.1404122 , -2.05518387, 1.32978656,\n", + " 1.55939981, -1.12400554, 0.31047509, -0.30145171, 0.39520486,\n", + " -1.37320207, -1.30786396, -0.17600666, 0.32959543, -0.92741269,\n", + " -0.22968272, 0.62521425, -0.70538808, 0.99930085, 0.04282135,\n", + " 0.97037357, 1.18057706, -0.47032835, -0.24715178, -0.35812267,\n", + " 0.41219654, -1.07288305, -1.45971665, -0.31750339, -1.69654704,\n", + " 0.05366386, -0.09160625, 0.38790378, 0.46497527, 0.70792639,\n", + " -0.74452609, 0.21340049, -0.17084294, -0.54755447, 1.62859608])" + ] + }, + "execution_count": 199, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "arr = np.random.randn(100)" + "arr = np.random.randn(100)\n", + "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 198, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "47" + ] + }, + "execution_count": 198, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "(arr > 0).sum()" ] @@ -3155,7 +4550,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 200, "metadata": { "collapsed": false, "jupyter": { @@ -3169,28 +4564,50 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 201, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 201, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.any()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 202, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 202, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr.all()" ] @@ -3216,7 +4633,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 203, "metadata": { "collapsed": false, "jupyter": { @@ -3230,21 +4647,33 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 204, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([-1.12155186, -0.54081677, 0.22116091, -0.31268949, 1.06918716,\n", + " -0.77957656])" + ] + }, + "execution_count": 204, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 205, "metadata": { "collapsed": false, "jupyter": { @@ -3258,14 +4687,26 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 206, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([-1.12155186, -0.77957656, -0.54081677, -0.31268949, 0.22116091,\n", + " 1.06918716])" + ] + }, + "execution_count": 206, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] @@ -3280,7 +4721,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 207, "metadata": { "collapsed": false, "jupyter": { @@ -3294,21 +4735,36 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 208, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.3831215 , 1.44012127, 0.4054832 ],\n", + " [ 1.471936 , -0.28302072, -1.52508038],\n", + " [ 0.16557173, 0.91101596, 1.20965603],\n", + " [ 0.24972433, 0.4125885 , -2.13286408],\n", + " [-0.38250019, 1.37333992, 0.26052155]])" + ] + }, + "execution_count": 208, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 209, "metadata": { "collapsed": false, "jupyter": { @@ -3322,14 +4778,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 210, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array([[-0.3831215 , 0.4054832 , 1.44012127],\n", + " [-1.52508038, -0.28302072, 1.471936 ],\n", + " [ 0.16557173, 0.91101596, 1.20965603],\n", + " [-2.13286408, 0.24972433, 0.4125885 ],\n", + " [-0.38250019, 0.26052155, 1.37333992]])" + ] + }, + "execution_count": 210, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "arr" ] @@ -3346,7 +4817,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 211, "metadata": { "collapsed": false, "jupyter": { @@ -3360,7 +4831,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 212, "metadata": { "collapsed": false, "jupyter": { @@ -3374,14 +4845,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 213, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "-1.731823670640617" + ] + }, + "execution_count": 213, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "large_arr[int(0.05 * len(large_arr))] # 5% квантиль" ] @@ -3400,7 +4882,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 214, "metadata": { "collapsed": false, "jupyter": { @@ -3414,21 +4896,32 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 215, "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "array(['Bob', 'Joe', 'Will'], dtype='