{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Объектно-ориентированное программирование и информационная безопасность\n",
    "\n",
    "*Валерий Семенов, Самарский университет*\n",
    "\n",
    "<h1 style=\"text-align:center; margin-top:60px\">Библиотека NumPy</h1>\n",
    "\n",
    "<div style=\"text-align:center\"><img src=\"numpy.png\"></div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\"><a href=\"https://numpy.org\"><strong>NumPy</strong></a> (<strong>Num</strong>erical <strong>Py</strong>thon) – одна из самых популярных библиотек для инженерных и научных вычислений в Python, она содержит множество методов для работы с многомерными массивами и отличается высокой скоростью обработки больших объемов данных. </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Мощная функциональность этой библиотеки используется в нескольких других популярных библиотеках – Pandas, SciPy, Matplotlib, Scikit-learn и TensorFlow. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Про историю создания этой библиотеки можно почитать, например, <a href=\"https://ru.wikipedia.org/wiki/NumPy\"><strong>здесь</strong></a>. </div>\n",
    "\n",
    "\n",
    "<h2 style=\"text-align:center; margin-top:20px\">Зачем нужен NumPy</h2>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Необходимость в специальной библиотеке для работы с большими массивами чисел возникает из-за скорости работы Python, поскольку он печально известен своей медлительностью. Программы, написанные на таких языках, как C#, C++, Node.js, Go и многих других, могут выполняться в 30-40 раз быстрее, чем эквивалентные программы Python. Причин несколько, включая тот факт, что Python является интерпретируемым языком (код должен быть скомпилирован во время выполнения), имеет динамическую типизацию и работает в одном потоке. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Библиотека NumPy частично написана на Python, а частично на C/C++ и Fortran — в тех местах, которые требуют скорости. Кроме того, код NumPy оптимизирован под большинство современных процессоров. </div>\n",
    "\n",
    "\n",
    "<h2 style=\"text-align:center; margin-top:20px\">Где используется NumPy</h2>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">NumPy используется везде, где нужна быстрая обработка больших массивов данных: </div>\n",
    "\n",
    "<div style=\" margin-left:50px\">\n",
    "<li style=\"text-align:justify\">Инженерные и научные вычисления в области физики, математики, биологии, химии и т.д. </li>\n",
    "<li style=\"text-align:justify\">Обработка изображений и звука – изменение размера, применение фильтров, сжатие и т.п. </li>\n",
    "<li style=\"text-align:justify\">Машинное обучение, искусственный интеллект и Data Science – обработка и статистический анализ данных. </li>\n",
    "<li style=\"text-align:justify\">Компьютерное зрение – распознавание образов и сегментация изображений. </li>\n",
    "<li style=\"text-align:justify\">Финансовые вычисления – расчет доходности активов и инвестиций, риск-менеджмент, оценка дисконтированной стоимости и т.д. </li>\n",
    "<li style=\"text-align:justify\">Геофизические и геологические исследования – обработка геоданных и анализ графиков. </li>\n",
    "</div>\n",
    "\n",
    "<h2 style=\"text-align:center; margin-top:20px\">Установка NumPy</h2>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">В Anaconda пакет NumPy установлен по умолчанию. Поэтому нет необходимости устанавливать его через менеджер пакетов. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Для использования NumPy в программе, необходимо его импортировать. </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Библиотека NumPy используется настолько часто и настолько многими, что сложилась традиция импортировать <strong>NumPy</strong> с псевдонимом <strong>np</strong>. </div>\n",
    "\n",
    "<h2 style=\"text-align:center; margin-top:20px\">Понятие массива Numpy</h2>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Основным объектом NumPy является однородный многомерный <strong>массив</strong> данных (тип <strong>ndarray</strong>, сокращение от <i>n-dimensional array</i>, n-мерный массив). </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify\">Это многомерный массив элементов (обычно чисел), одного типа (либо только целые числа, либо числа с плавающей точкой, либо строки, либо булевы (логические) значения). </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Чем же удобны массивы? </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify\">Во-первых, они используют меньше памяти и работают быстрее, чем обычные списки Python. </div>\n",
    " \n",
    "<div style=\"text-indent:30px; text-align:justify\">Во-вторых, с ними очень удобно работать. Все операции над массивами будут производиться поэлементно, то есть, для выполнения действий над каждым элементом массива, нам не придется использовать какие-то специальные конструкции вроде циклов, мы сможем обращаться сразу ко всему массиву. </div>\n",
    "\n",
    "<div style=\"text-indent:200px; text-align:justify; margin-top:20px\"><u>Примеры одномерного, двухмерного и трехмерного массивов:</u></div>\n",
    "\n",
    "<div style=\"text-align:center\"><img src=\"ndarray.jpg\" width=\"800\"></div>\n",
    "\n",
    "<h2 style=\"text-align:center; margin-top:40px\">Характеристики (атрибуты) массива</h2>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Теперь познакомимся с характеристиками самого массива. Создадим массив <strong>M</strong> и будем с ним работать: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[5.2, 3. , 4.5],\n",
       "       [9.1, 0.1, 0.3]])"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M = np.array([[5.2, 3.0, 4.5], \n",
    "              [9.1, 0.1, 0.3]])\n",
    "M"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Массивы бывают многомерными, значит, у массива есть число измерений. Давайте его найдем: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.ndim    # Сокращение от dimensions (размеры)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Действительно, всего два измерения - чтобы указать на число <strong>4.5</strong> из этого массива, нам понадобятся всего две координаты – номер строки (<strong>0</strong>) и номер столбца (<strong>2</strong>). </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.5"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M[0,2]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Теперь посмотрим на форму или вид массива (shape): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(2, 3)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.shape  # shape (форма) = 2 строки и 3 столбца"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Кроме того, можем найти общее число элементов в массиве, то есть, размер (size): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.size  # Всего 6 элементов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2 style=\"text-align:center; margin-top:20px\">Создание массивов</h2>\n",
    "\n",
    "<h3 style=\"text-align:center; margin-top:40px\">Функция <strong>np.array()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Во-первых, массив можно создать с помощью функции <strong>np.array()</strong>, которой мы передаем, например, список или кортеж элементов: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Создадим одномерный массив из списка\n",
    "arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Создадим одномерный массив из кортежа\n",
    "arr = np.array((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([3.14, 4.  , 2.  , 3.  ])"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.array([3.14, 4, 2, 3]) # Если типы элементов не совпадают, NumPy попытается выполнить повышающее приведение типов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:20px\">Функция <strong>np.arange()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Кроме того, можно воспользоваться функцией <strong>np.arange()</strong>. Эта функция, как и функция <strong>range()</strong>, с которой мы уже знакомы, создает линейную последовательность элементов. Обязательным параметром является верхняя граница, которая не входит в последовательность. </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Создадим одномерный массив из 10 элементов\n",
    "arr = np.arange(10)\n",
    "arr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Нижняя граница и шаг обязательными не являются: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 3  5  7  9 11 13 15 17 19]\n"
     ]
    }
   ],
   "source": [
    "# Создаем массив, заполненный линейной последовательностью, \n",
    "# начинающейся с 3 и заканчивающейся 20, с шагом 2 (аналогично встроенной функции range())\n",
    "arr = np.arange(3, 20, 2)\n",
    "print(arr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "numpy.ndarray"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Отличие <strong>range()</strong> от функции <strong>np.arange()</strong> заключается в том, что первая не допускает использования типа <strong>float</strong>. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Попробуем создать список с помощью функций <strong>range()</strong> и <strong>list()</strong> и получим ошибку: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "'float' object cannot be interpreted as an integer",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[13], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28mlist\u001b[39m(\u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m2\u001b[39m, \u001b[38;5;241m5.5\u001b[39m, \u001b[38;5;241m0.5\u001b[39m))\n",
      "\u001b[1;31mTypeError\u001b[0m: 'float' object cannot be interpreted as an integer"
     ]
    }
   ],
   "source": [
    "list(range(2, 5.5, 0.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">При этом в массиве NumPy тип <strong>float</strong> вполне может использоваться: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2. , 2.5, 3. , 3.5, 4. , 4.5, 5. ])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.arange(2, 5.5, 0.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:20px\">Функция <strong>np.linspace()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Эта функция создает линейную последовательность элементов, равномерно располагающихся в заданном интервале. Она работает как функция <strong>np.arange()</strong>. Но вместо указания размера шага она определяет количество элементов в интервале между начальным и конечным значениями: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-5.5       , -4.94285714, -4.38571429, -3.82857143, -3.27142857,\n",
       "       -2.71428571, -2.15714286, -1.6       , -1.04285714, -0.48571429,\n",
       "        0.07142857,  0.62857143,  1.18571429,  1.74285714,  2.3       ])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Создаем массив из 15 значений, равномерно располагающихся между -5.5 и 2.3\n",
    "np.linspace(-5.5, 2.3, 15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Также библиотека numpy позволяет создать массивы, состоящие из одних нулей или единиц, одинаковых значений, а также  «пустые» массивы (на практике используются редко). Удобство заключается в том, что сначала можно создать массив, инициализировать его (например, заполнить нулями), а затем заменить нули на другие значения в соответствии с требуемыми условиями. </div>\n",
    "\n",
    "<h3 style=\"text-align:center; margin-top:20px\">Функция <strong>np.zeros()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Создадим двумерный массив из одних нулей в виде значений с плавающей точкой (по умолчанию): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0.],\n",
       "       [0., 0., 0., 0., 0.]])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Z = np.zeros((3, 5))   # Размеры задаются в виде кортежа - не теряйте еще одни круглые скобки\n",
    "Z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Теперь создадим трехмерный массив из одних нулей в виде целых значений: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[[0, 0],\n",
       "        [0, 0],\n",
       "        [0, 0],\n",
       "        [0, 0]],\n",
       "\n",
       "       [[0, 0],\n",
       "        [0, 0],\n",
       "        [0, 0],\n",
       "        [0, 0]],\n",
       "\n",
       "       [[0, 0],\n",
       "        [0, 0],\n",
       "        [0, 0],\n",
       "        [0, 0]]])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Z = np.zeros((3, 4, 2), dtype=int)   # Размеры задаются в виде кортежа\n",
    "Z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:20px\">Функция <strong>np.ones()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Пример создания двумерного массива из единиц в виде целых значений: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 1, 1, 1, 1],\n",
       "       [1, 1, 1, 1, 1],\n",
       "       [1, 1, 1, 1, 1]])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m = np.ones((3, 5), dtype=int)\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Пример создания двумерного массива из логического типа данных (все элементы массива равны True): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ True,  True,  True,  True,  True],\n",
       "       [ True,  True,  True,  True,  True],\n",
       "       [ True,  True,  True,  True,  True]])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m = np.ones((3, 5), dtype=bool)\n",
    "m"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:20px\">Функция <strong>np.eye()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">А вот так выглядит единичная матрица – таблица из 0 и 1, в которой число строк и столбцов одинаково, и где на главной диагонали стоят 1: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 0., 0., 0., 0.],\n",
       "       [0., 1., 0., 0., 0.],\n",
       "       [0., 0., 1., 0., 0.],\n",
       "       [0., 0., 0., 1., 0.],\n",
       "       [0., 0., 0., 0., 1.]])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "E = np.eye((5))\n",
    "E"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Несмотря на то, что для создания единичной матрицы есть специальный метод, давайте посмотрим, как бы мы создавали ее «вручную» (мы мало работали со вложенными циклами, поэтому стоит повторить). </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Для этого нам потребовались бы вложенные циклы: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1., 0., 0., 0.],\n",
       "       [0., 1., 0., 0.],\n",
       "       [0., 0., 1., 0.],\n",
       "       [0., 0., 0., 1.]])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "I = np.zeros((4, 4))\n",
    "\n",
    "for i in range(0, I.shape[0]):\n",
    "    for j in range(0, I.shape[1]):\n",
    "        if i == j:\n",
    "            I[i][j] = 1\n",
    "I"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Данный пример заодно показывает одну важную особенность — массивы, как и списки, являются изменяемыми объектами в Python. </div>\n",
    "\n",
    "<h3 style=\"text-align:center; margin-top:20px\">Функция <strong>np.full()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Пример создания двумерного массива, заполненного значением <strong>3.14</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[3.14, 3.14, 3.14, 3.14, 3.14],\n",
       "       [3.14, 3.14, 3.14, 3.14, 3.14],\n",
       "       [3.14, 3.14, 3.14, 3.14, 3.14]])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.full((3, 5), 3.14)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2 style=\"text-align:center; margin-top:40px\">Создание массивов со случайными элементами</h2>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:40px\">Для создания массивов со случайными элементами служит модуль <strong>numpy.random</strong>. </div>\n",
    "\n",
    "<h3 style=\"text-align:center; margin-top:40px\">Функция <strong>np.random.sample()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Самый простой способ задать массив со случайными элементами - использовать функцию <strong>sample</strong> (или <strong>random</strong>, или <strong>random_sample</strong>, или <strong>ranf</strong> - это все одна и та же функция): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[0.97271746, 0.47340792, 0.486786  , 0.05483375, 0.62849926],\n",
       "       [0.6283741 , 0.66525056, 0.0136125 , 0.26279421, 0.92801512],\n",
       "       [0.93906615, 0.42229848, 0.46289972, 0.62001445, 0.36350522]])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Создаем массив размером 3 x 5 равномерно распределенных случайных значений в диапазоне [0, 1]\n",
    "# np.random.random((3, 5))\n",
    "# np.random.random_sample((3, 5))\n",
    "# np.random.ranf((3, 5))\n",
    "\n",
    "np.random.sample((3, 5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Функция без аргументов возвращает просто одно число, с одним целым числом - одномерный массив, с кортежем - массив с размерами, указанными в кортеже (все числа - из промежутка [0, 1]). </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">С помощью функции <strong>randint()</strong> можно создать массив из целых чисел. </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify\">Для вызова функции удобно использовать именованные аргументы, вместо позиционных: </div>\n",
    "    \n",
    "<div style=\" margin-left:50px\">\n",
    "<li style=\"text-align:justify\"><strong>low</strong> - начальное значение;</li>\n",
    "<li style=\"text-align:justify\"><strong>high</strong> - конечное значение (функция <strong>randint()</strong> не включает в себя это число);</li>\n",
    "<li style=\"text-align:justify\"><strong>size</strong> - размеры возвращаемого массива (кортеж).</li>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-2,  1,  4,  2,  2],\n",
       "       [ 1,  4, -2,  3,  3],\n",
       "       [ 1,  0, -1, -2,  2],\n",
       "       [ 4, -2, -1,  4,  1]])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# np.random.randint(-2, 5, (4,5))\n",
    "np.random.randint(low=-2, high=5, size=(4,5))  # Удобнее использовать именованные аргументы, вместо позиционных"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">В предыдущих примерах мы генерировали числа из непрерывного <a href=\"https://ru.wikipedia.org/wiki/%D0%94%D0%B8%D1%81%D0%BA%D1%80%D0%B5%D1%82%D0%BD%D0%BE%D0%B5_%D1%80%D0%B0%D0%B2%D0%BD%D0%BE%D0%BC%D0%B5%D1%80%D0%BD%D0%BE%D0%B5_%D1%80%D0%B0%D1%81%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5\"><strong>равномерного распределения</strong></a>. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">NumPy также включает генераторы для других распределений, таких как: Бета, биномиальное, хи-квадрат, Дирихле, экспоненциальное, Фишера, Гамма, геометрическое, Гамбала, гипергеометрическое, Лапласа, логистическое, логнормальное, логарифмическое, мультиномиальное, многомерное нормальное, отрицательное биномиальное, нецентральное хи-квадрат, нецентральное Фишера, нормальное (Гаусса), Парето, Пуассона, степенное, Рэлея, Коши, Стьюдента, треугольное, Фон-Миса, Вальда, Вейбулла и Ципфа. </div>\n",
    "\n",
    "<h3 style=\"text-align:center; margin-top:40px\">Функция <strong>np.random.normal()</strong></h3>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\"><a href=\"https://ru.wikipedia.org/wiki/%D0%9D%D0%BE%D1%80%D0%BC%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%80%D0%B0%D1%81%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5\"><strong>Нормальное распределение</strong></a> (также называемое распределением Гаусса или Гаусса — Лапласа), занимает одно из центральных мест в статистике и машинном обучении. Оно характеризуется симметричной «колоколообразной» кривой, которая имеет огромное значение в теории вероятностей и анализа данных. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Функция <strong>np.random.normal()</strong> создает массив с нормально распределенными элементами. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Для вызова функции удобно использовать именованные аргументы, вместо позиционных: </div>\n",
    "    \n",
    "<div style=\" margin-left:50px\">\n",
    "<li style=\"text-align:justify\"><strong>loc</strong> - математическое ожидание (среднее значение) распределения (<strong>μ</strong>);</li>\n",
    "<li style=\"text-align:justify\"><strong>scale</strong> - стандартное отклонение (<strong>σ</strong>)</li>\n",
    "<li style=\"text-align:justify\"><strong>size</strong> - размеры возвращаемого массива (кортеж).</li>\n",
    "</div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Пример генерации чисел из нормального распределения (Гаусса) при среднем значении <strong>μ = 1.5</strong> и стандартном отклонении <strong>σ = 4.0</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([11.96981497,  4.80944659,  6.56125972,  1.48002467,  3.0198823 ,\n",
       "       -1.63332516, -0.2082319 , -0.11560365,  1.61295695, -0.23749819,\n",
       "        3.44470336,  4.57409383, -0.98457806,  2.12112966,  8.68653596,\n",
       "        6.31103815, 10.12247785,  2.20683484,  2.82335713,  4.57881697,\n",
       "       -1.99001152,  1.22293726,  3.88875255,  5.30633798, -2.48067463])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.normal(loc=1.5, scale=4, size=25)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Стандартным нормальным распределением называется нормальное распределение с математическим ожиданием <strong>μ = 0</strong> и стандартным отклонением <strong>σ = 1</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-0.28751133, -0.31481052, -0.54276728,  0.66250318, -1.23594968,\n",
       "       -1.03862532,  2.29670889, -2.0448765 ,  0.54758251,  1.32044122,\n",
       "       -1.40199375, -0.8796905 ,  0.4968837 ,  0.13589905,  0.03376497])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.normal(size=15)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:40px\">Выбор и перемешивание</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Перемешать массив можно с помощью функции <strong>shuffle()</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-15,  33,   5,   7, -25],\n",
       "       [ 39, -10,  -4,  37,  49],\n",
       "       [ -9, -16,  19,  49,  43],\n",
       "       [-23,  12,  41, -19,   3]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = np.random.randint(low=-25, high=50, size=(4,5))  # Создаем двумерный массив из целых чисел [-25,50]\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-15,  33,   5,   7, -25],\n",
       "       [-23,  12,  41, -19,   3],\n",
       "       [ 39, -10,  -4,  37,  49],\n",
       "       [ -9, -16,  19,  49,  43]])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.random.shuffle(a)  # Перемешиваем массив\n",
    "a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:40px\">Функция <strong>np.random.seed()</strong></h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">В Python, как и в любом другом языке, используется так называемый <a href=\"https://ru.wikipedia.org/wiki/%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%82%D0%BE%D1%80_%D0%BF%D1%81%D0%B5%D0%B2%D0%B4%D0%BE%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D1%8B%D1%85_%D1%87%D0%B8%D1%81%D0%B5%D0%BB\"><strong>генератор псевдослучайных чисел</strong></a>. Таким образом любая функция для генерации случайного числа выдает не случайное число, а число которое вычисляется алгоритмом на основе другого числа (по умолчанию это текущее время). </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Функция <strong>np.random.seed()</strong> позволяет изменить начальное число, которое используется для генерации случайного числа, а так как \"случайные\" числа выдаются одним и тем же алгоритмом, то при одинаковом параметре в <strong>np.random.seed()</strong>, будут и одинаковые \"случайные\" числа (инициализация генератора случайных чисел). </div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2 style=\"text-align:center; margin-top:40px\">Индексация массивов</h2>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Одномерный массив: \n",
      " [5 0 3 3 7 9]\n",
      "\n",
      " Двумерный массив: \n",
      " [[19 18  4  6]\n",
      " [12  1  6  7]\n",
      " [14 17  5 13]]\n"
     ]
    }
   ],
   "source": [
    "np.random.seed(0) # Начальное значение для целей воспроизводимости\n",
    "x1 = np.random.randint(10, size=6)         # Одномерный массив\n",
    "x2 = np.random.randint(20, size=(3, 4))    # Двумерный массив\n",
    "\n",
    "print(\"Одномерный массив: \\n\", x1)\n",
    "print(\"\\n Двумерный массив: \\n\", x2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 0, 3, 3, 7, 9])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1   # Весь одномерный массив x1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1[0]  # Элемент одномерного массива x1 с индексом (номером) 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1[2]  # Элемент одномерного массива x1 с индексом (номером) 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1[-1]  # ПОСЛЕДНИЙ элемент одномерного массива x1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[19, 18,  4,  6],\n",
       "       [12,  1,  6,  7],\n",
       "       [14, 17,  5, 13]])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2  # Весь двумерный массив x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2[1][-1]  # Элемент двумерного массива x2, находящийся на пересечении 1-ой строки и ПОСЛЕДНЕГО столбца"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Обращаться к элементам в многомерном массиве можно с помощью разделенных запятыми кортежей индексов: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2[1,-1]  # Элемент двумерного массива x2, находящийся на пересечении 1-ой строки и ПОСЛЕДНЕГО столбца"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Мы можем изменить значение любого элемента массива, используя любую из перечисленных выше индексных нотаций: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[19, 18,  4,  6],\n",
       "       [12,  1,  6,  7],\n",
       "       [14, 17,  5, 13]])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 19,  18,   4,   6],\n",
       "       [ 12,   1, 666,   7],\n",
       "       [ 14,  17,   5,  13]])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2[1, 2] = 666\n",
    "x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [],
   "source": [
    "x2[-1][-1] = 97.91"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 19,  18,   4,   6],\n",
       "       [ 12,   1, 666,   7],\n",
       "       [ 14,  17,   5,  97]])"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2 # Числа после точки были отброшены!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:40px\">Срезы массивов (доступ к подмассивам)</h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Аналогично доступу к отдельным элементам массива можно использовать квадратные скобки для доступа к подмассивам с помощью срезов (slicing), обозначаемых знаком двоеточия (<strong>:</strong>). </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify\">Синтаксис срезов библиотеки NumPy соответствует аналогичному синтаксису для стандартных списков языка Python. Для доступа к срезу массива <strong>x</strong> используйте следующий синтаксис: &nbsp;&nbsp; <strong>x[начало:конец:шаг]</strong></div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:10px\">Если какие-либо из этих значений не указаны, значения применяются по умолчанию (начало = 0, конец = размер соответствующего измерения, шаг = 1). </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:10px\">Примеры: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 0, 3, 3, 7, 9])"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1  # Весь одномерный массив x1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[5 0 3 3]\n",
      "[7 9]\n",
      "[3 7]\n",
      "[5 3 7]\n"
     ]
    }
   ],
   "source": [
    "print(x1[:4])\n",
    "print(x1[4:])\n",
    "print(x1[-3:-1])\n",
    "print(x1[::2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 19,  18,   4,   6],\n",
       "       [ 12,   1, 666,   7],\n",
       "       [ 14,  17,   5,  97]])"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2  # Весь двумерный массив x2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 19  18   4   6]\n",
      " [ 12   1 666   7]]\n"
     ]
    }
   ],
   "source": [
    "print(x2[:2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Многомерные срезы задаются схожим образом, с разделением срезов запятыми: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 19,  18,   4],\n",
       "       [ 12,   1, 666]])"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2[:2, :3] # две строки, три столбца"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 18,   4,   6],\n",
       "       [  1, 666,   7],\n",
       "       [ 17,   5,  97]])"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x2[:, 1:] # Все строки и три последних столбца"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 97,   5,  17,  14],\n",
       "       [  7, 666,   1,  12],\n",
       "       [  6,   4,  18,  19]])"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Измерения подмассивов также можно «переворачивать»:\n",
    "x2[::-1, ::-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:40px\">Изменение формы массивов</h3>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Еще одна удобная операция — изменение формы массивов методом <strong>reshape()</strong>. </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Например, если вам требуется поместить числа от <strong>1</strong> до <strong>9</strong> в таблицу <strong>3×3</strong>, сделать это можно следующим образом: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3, 4, 5, 6, 7, 8, 9])"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "grid = np.arange(1, 10)  # Создаем одномерный массив из 9 чисел\n",
    "grid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6],\n",
       "       [7, 8, 9]])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "grid = np.arange(1, 10).reshape((3, 3))  # Преобразуем его в матрицу 3x3\n",
    "grid"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Обратите внимание, что размер исходного массива должен соответствовать размеру измененного. </div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:40px\">\"Прихотливая\" индексация</h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">«Прихотливая» индексация (fancy indexing) похожа на уже рассмотренную нами простую индексацию, но вместо скалярных значений передаются массивы индексов. Это дает возможность очень быстрого доступа и модификации сложных подмножеств значений массива. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Суть «прихотливой» индексации  проста - она заключается в передаче массива индексов с целью одновременного доступа к нескольким элементам массива. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Рассмотрим пример: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([72,  9, 20, 80, 69, 79, 47, 64, 82, 99])"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.randint(100, size=10)  # Создадим массив из 10 чисел\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Допустим, нам требуется обратиться к трем различным элементам. </div>\n",
    "    \n",
    "<div style=\"text-indent:30px; text-align:justify\">Можно сделать это следующим образом: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(80, 64, 20)"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[3], x[7], x[2]   # Пусть нам требуются три элемента массива в указанном порядке"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">С другой стороны, можно передать единый список индексов и получить тот же результат: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([80, 64, 20])"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ind = [3, 7, 2]   # Создадим список с нужными номерами\n",
    "x[ind]            # Передадим его в качестве"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">В случае «прихотливой» индексации форма результата отражает форму массивов индексов (index arrays), а не форму индексируемого массива. </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[3, 7],\n",
       "       [4, 2]])"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ind = np.array([[3, 7],[4, 2]])  # Создадим двумерный массив индексов\n",
    "ind"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[80, 64],\n",
       "       [69, 20]])"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[np.array(ind)]  # Извлечем из одномерного исходного массива элементы в соответствии с формой массива ind"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h3 style=\"text-align:center; margin-top:40px\">Слияние и разбиение массивов</h3>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Слияние, или объединение, двух массивов в библиотеке NumPy выполняется в основном с помощью методов <strong>np.concatenate</strong>, <strong>np.vstack</strong> и <strong>np.hstack</strong>. Метод <strong>np.concatenate</strong> принимает на входе кортеж или список массивов в качестве первого аргумента: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1,  3,  5,  7,  9, 11])"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.array([1, 3, 5])  # Одномерный массив x\n",
    "y = np.array([7, 9, 11]) # Одномерный массив y\n",
    "np.concatenate([x, y])   # Слияние двух массивов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Мы можем использовать знак плюс, для случае слияния списков: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[1, 3, 5, 7, 9, 11]"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = [1, 3, 5]   # Список a\n",
    "b = [7, 9, 11]  # Список b\n",
    "a + b           # Слияние двух списков"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">А вот при использовании знака плюс для массивов, мы получим поэлементное сложение элементов массивов (и только в случае одинаковой длины массивов): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 8, 12, 16])"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x + y  # Поэлементное сложение двух массивов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify\">Можно объединять более двух массивов одновременно: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 1  3  5  7  9 11 99 88 77]\n"
     ]
    }
   ],
   "source": [
    "z = [99, 88, 77]\n",
    "print(np.concatenate([x, y, z])) # Слияние трех массивов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Для объединения двумерных массивов можно также использовать <strong>np.concatenate</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [],
   "source": [
    "grid = np.array([[1, 2, 3], \n",
    "                 [4, 5, 6]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [4, 5, 6],\n",
       "       [1, 2, 3],\n",
       "       [4, 5, 6]])"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Слияние по первой оси координат (с индексом 0)\n",
    "# np.concatenate([grid, grid], axis=0)\n",
    "np.concatenate([grid, grid])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3, 1, 2, 3],\n",
       "       [4, 5, 6, 4, 5, 6]])"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Слияние по второй оси координат (с индексом 1)\n",
    "np.concatenate([grid, grid], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Для работы с массивами с различающимися измерениями удобнее и понятнее использовать функции <strong>np.vstack</strong> (вертикальное объединение) и <strong>np.hstack</strong> (горизонтальное объединение): </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 2, 3],\n",
       "       [9, 8, 7],\n",
       "       [6, 5, 4]])"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.array([1, 2, 3])\n",
    "grid = np.array([[9, 8, 7], [6, 5, 4]])\n",
    "\n",
    "# Объединяет массивы по вертикали\n",
    "np.vstack([x, grid])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 9,  8,  7, 99],\n",
       "       [ 6,  5,  4, 55]])"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = np.array([[99],[55]])\n",
    "\n",
    "# Объединяет массивы по горизонтали\n",
    "np.hstack([grid, y])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Противоположностью слияния является разбиение, выполняемое с помощью функций <strong>np.split</strong>, <strong>np.hsplit</strong> и <strong>np.vsplit</strong>. </div>\n",
    "\n",
    "<div style=\"text-indent:30px; text-align:justify\">Каждой из них необходимо передавать список индексов, задающих точки в которых происходит разбиение: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 2 3] [99 77 55 34] [3 2 1] [ 0 -2]\n"
     ]
    }
   ],
   "source": [
    "x = [1, 2, 3, 99, 77, 55, 34, 3, 2, 1, 0, -2]  # Одномерный массив x\n",
    "x1, x2, x3, x4 = np.split(x, [3, 7, 10])       # Разрезаем массив в трех точках на четыре подмассива\n",
    "print(x1, x2, x3, x4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Соответствующие функции <strong>np.hsplit</strong> и <strong>np.vsplit</strong> действуют аналогично: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ 0,  1,  2,  3],\n",
       "       [ 4,  5,  6,  7],\n",
       "       [ 8,  9, 10, 11],\n",
       "       [12, 13, 14, 15]])"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "grid = np.arange(16).reshape((4, 4))  # Массив grid 4x4\n",
    "grid"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0 1 2 3]\n",
      " [4 5 6 7]]\n",
      "[[ 8  9 10 11]\n",
      " [12 13 14 15]]\n"
     ]
    }
   ],
   "source": [
    "upper, lower = np.vsplit(grid, [2])  # Второй массив начнется со строки, имеющей индекс 2 в исходном массиве\n",
    "print(upper)\n",
    "print(lower)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 0  1]\n",
      " [ 4  5]\n",
      " [ 8  9]\n",
      " [12 13]]\n",
      "[[ 2  3]\n",
      " [ 6  7]\n",
      " [10 11]\n",
      " [14 15]]\n"
     ]
    }
   ],
   "source": [
    "left, right = np.hsplit(grid, [2])\n",
    "print(left)\n",
    "print(right)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2 style=\"text-align:center\">Арифметические функции</h2>\n",
    "<div style=\"text-align:center\"><img src=\"arithmetic.png\" width=\"600\"></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x = [0 1 2 3]\n",
      "x + 5 = [5 6 7 8]\n",
      "x - 5 = [-5 -4 -3 -2]\n",
      "x * 2 = [0 2 4 6]\n",
      "x / 2 = [0.  0.5 1.  1.5]\n",
      "x // 2 = [0 0 1 1]\n"
     ]
    }
   ],
   "source": [
    "x = np.arange(4)\n",
    "print(\"x =\", x)\n",
    "print(\"x + 5 =\", x + 5)   # Прибавляем к каждому элементу массива число 5, это то же самое, что = np.add(x,5)\n",
    "print(\"x - 5 =\", x - 5)   # Вычитаем  из каждого элемента массива число 5, это то же самое, что = np.subtract(x,5)\n",
    "print(\"x * 2 =\", x * 2)   # Умножаем каждый элемент массива на число 2, это то же самое, что = np.multiply(x,2)\n",
    "print(\"x / 2 =\", x / 2)   # Делим каждый элемент массива на число 2, это то же самое, что = np.divide(x,2)\n",
    "print(\"x // 2 =\", x // 2) # Делим каждый элемент массива на число 2 с округлением в меньшую сторону, это то же самое, что = np.floor_divide(x,2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-x =  [ 0 -1 -2 -3]\n",
      "x ** 2 =  [0 1 4 9]\n",
      "x % 2 =  [0 1 0 1]\n"
     ]
    }
   ],
   "source": [
    "print(\"-x = \", -x)         # Меняем знак каждого элемента массива на противоположный\n",
    "print(\"x ** 2 = \", x ** 2) # Возводим каждый элемент массива в степень\n",
    "print(\"x % 2 = \", x % 2)   # Вычисляем остаток от деления каждого элемента массива"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([-1.  , -2.25, -4.  , -6.25])"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "-(0.5*x + 1) ** 2  # Вычисляем выражение для каждого элемента массива"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 1, 0, 1, 2])"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.array([-2, -1, 0, 1, 2])\n",
    "abs(x)     # Встроенная функция для вычисления абсолютного значения каждого элемента массива"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 1, 0, 1, 2])"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.abs(x)  # Функция ИЗ NUMPY для вычисления абсолютного значения каждого элемента массива"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2 style=\"text-align:center; margin-top:40px; margin-bottom:30px\">Агрегирование (минимум, максимум и все, что посередине)</h2>\n",
    "\n",
    "<div style=\"text-align:center\"><img src=\"aggregation.png\" width=\"600\"></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "big_array = np.random.rand(1000000)   # Создадим большой одномерный массив"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "500385.13121223805"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.sum(big_array)   # Сумма всех элементов массива, это то же самое, что = big_array.sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "500385.13121223805"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "big_array.sum()     # Сумма всех элементов массива с использованием метода sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "500385.1312122504"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(big_array)      # Встроенная функция sum()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9999997207656334"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.max(big_array)   # Максимальное значение всех элементов массива"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9999997207656334"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "big_array.max()     # Максимальное значение всех элементов массива с использованием метода max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9999997207656334"
      ]
     },
     "execution_count": 79,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "max(big_array)      # Встроенная функция max()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.500385131212238"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(big_array)  # Среднее значение всех элементов массива"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.500385131212238"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "big_array.mean()    # Среднее значение всех элементов массива с использованием метода mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'mean' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[82], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m mean(big_array)\n",
      "\u001b[1;31mNameError\u001b[0m: name 'mean' is not defined"
     ]
    }
   ],
   "source": [
    "mean(big_array)     # А вот такой встроенной функции нет"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1.,  2., nan,  1.])"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr = np.array([1, 2, np.nan, 1])\n",
    "arr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.3333333333333333"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.nanmean(arr)   # Среднее значение всех элементов массива с NaN-безопасной функцией mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "nan"
      ]
     },
     "execution_count": 85,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.mean(arr)   # Среднее значение всех элементов массива с обычной функцией mean()\n",
    "# Функция не работает, потому что есть элементы массива не содержащие число."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[0.20020551 0.71990961 0.04386778 0.24253241]\n",
      " [0.58362154 0.19365921 0.73131058 0.54673692]\n",
      " [0.34738314 0.4634587  0.36476761 0.48515853]]\n"
     ]
    }
   ],
   "source": [
    "M = np.random.random((3, 4))  # Двумерный массив\n",
    "print(M)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.13121019, 1.37702752, 1.13994597, 1.27442786])"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.sum(axis=0)   # Сумма элементов двумерного массива по столбцам"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1.20651531, 2.05532825, 1.66076798])"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.sum(axis=1)   # Сумма элементов двумерного массива по строкам"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.922611543412104"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "M.sum()         # Сумма всех элементов двумерного массива"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<h2 style=\"text-align:center; margin-top:40px; margin-bottom:30px\">Сравнения, маски и булева логика</h2>\n",
    "\n",
    "<div style=\"text-align:center\"><img src=\"comparison.png\" width=\"600\"></div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 90,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = np.array([1, 2, 3, 4, 5])   # Создадим одномерный массив"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False, False, False,  True,  True])"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x > 3   # Проверим наличие элементов массива, удовлетворяющих условию"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[3, 8, 8, 4],\n",
       "       [6, 8, 4, 0],\n",
       "       [5, 4, 8, 8]])"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = np.random.randint(10, size=(3, 4))  # Создадим случайный двумерный массив\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 93,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[False,  True,  True, False],\n",
       "       [ True,  True, False, False],\n",
       "       [False, False,  True,  True]])"
      ]
     },
     "execution_count": 93,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x >= 6   # Проверим наличие элементов массива, удовлетворяющих условию"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Для подсчета количества элементов <strong>True</strong> в булевом массиве удобно использовать функцию <strong>np.count_nonzero()</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 94,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 94,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.count_nonzero(x >= 6)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 2, 2])"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Сколько значений, больших или равных 6, содержится в каждой строке?\n",
    "np.sum(x >= 6, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Если вам необходимо быстро проверить, существует ли хоть одно истинное значение, или все ли значения истинны, можно воспользоваться функциями <strong>np.any()</strong> и <strong>np.all()</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 96,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 96,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Имеются ли в массиве какие-либо значения, превышающие 8?\n",
    "np.any(x > 8)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 97,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([False, False, False])"
      ]
     },
     "execution_count": 97,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Все ли значения в каждой строке меньше 8?\n",
    "np.all(x < 8, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-indent:30px; text-align:justify; margin-top:20px\">Чтобы выбрать нужные значения из массива, достаточно просто проиндексировать исходный массив <strong>x</strong> по этому булеву массиву. Такое действие носит название операции наложения маски или <strong>маскирования</strong>: </div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[3, 8, 8, 4],\n",
       "       [6, 8, 4, 0],\n",
       "       [5, 4, 8, 8]])"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[ True, False, False,  True],\n",
       "       [False, False,  True,  True],\n",
       "       [False,  True, False, False]])"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x < 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([3, 4, 4, 0, 4])"
      ]
     },
     "execution_count": 100,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[x < 5]  # Наложение маски"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([5, 6, 7])"
      ]
     },
     "execution_count": 101,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# При создании булева выражения с заданным массивом следует использовать операторы & и |, а не операции and или or:\n",
    "x = np.arange(10)\n",
    "x[(x > 4) & (x < 8)]"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
