{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Настройка pandas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# импортируем библиотеки numpy и pandas\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "# импортируем библиотеку datetime для работы с датами\n",
    "import datetime\n",
    "from datetime import datetime, date\n",
    "\n",
    "# Задаем некоторые опции библиотеки pandas, которые \n",
    "# настраивают вывод\n",
    "pd.set_option('display.notebook_repr_html', False)\n",
    "pd.set_option('display.max_columns', 8)\n",
    "pd.set_option('display.max_rows', 10)\n",
    "pd.set_option('display.width', 60)\n",
    "\n",
    "# импортируем библиотеку matplotlib для построения графиков\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Конкатенация данных"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    0\n",
       "1    1\n",
       "2    2\n",
       "dtype: int32"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем два объекта Series для конкатенации\n",
    "s1 = pd.Series(np.arange(0, 3))\n",
    "s2 = pd.Series(np.arange(5, 8))\n",
    "s1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    5\n",
       "1    6\n",
       "2    7\n",
       "dtype: int32"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    0\n",
       "1    1\n",
       "2    2\n",
       "0    5\n",
       "1    6\n",
       "2    7\n",
       "dtype: int32"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# конкатенируем их\n",
    "pd.concat([s1, s2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   a  b  c\n",
       "0  0  1  2\n",
       "1  3  4  5\n",
       "2  6  7  8"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем два объекта DataFrame для конкатенации,\n",
    "# используя те же самые индексные метки и имена столбцов, \n",
    "# но другие значения\n",
    "df1 = pd.DataFrame(np.arange(9).reshape(3, 3), \n",
    "                   columns=['a', 'b', 'c'])\n",
    "#df2 имеет значения 9 .. 18\n",
    "df2 = pd.DataFrame(np.arange(9, 18).reshape(3, 3), \n",
    "                   columns=['a', 'b', 'c'])\n",
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a   b   c\n",
       "0   9  10  11\n",
       "1  12  13  14\n",
       "2  15  16  17"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a   b   c\n",
       "0   0   1   2\n",
       "1   3   4   5\n",
       "2   6   7   8\n",
       "0   9  10  11\n",
       "1  12  13  14\n",
       "2  15  16  17"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем конкатенацию\n",
    "pd.concat([df1, df2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   a  b  c\n",
       "0  0  1  2\n",
       "1  3  4  5\n",
       "2  6  7  8"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# демонстрируем конкатенацию двух объектов DataFrame\n",
    "# с разными столбцами\n",
    "df1 = pd.DataFrame(np.arange(9).reshape(3, 3), \n",
    "                   columns=['a', 'b', 'c'])\n",
    "df2 = pd.DataFrame(np.arange(9, 18).reshape(3, 3), \n",
    "                   columns=['a', 'c', 'd'])\n",
    "df1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a   c   d\n",
       "0   9  10  11\n",
       "1  12  13  14\n",
       "2  15  16  17"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a    b   c     d\n",
       "0   0  1.0   2   NaN\n",
       "1   3  4.0   5   NaN\n",
       "2   6  7.0   8   NaN\n",
       "0   9  NaN  10  11.0\n",
       "1  12  NaN  13  14.0\n",
       "2  15  NaN  16  17.0"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем конкатенацию, пропусками будут заполнены\n",
    "# значения столбца d в датафрейме df1 и\n",
    "# значения столбца b в датафрейме df2\n",
    "pd.concat([df1, df2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "        a    b   c     d\n",
       "df1 0   0  1.0   2   NaN\n",
       "    1   3  4.0   5   NaN\n",
       "    2   6  7.0   8   NaN\n",
       "df2 0   9  NaN  10  11.0\n",
       "    1  12  NaN  13  14.0\n",
       "    2  15  NaN  16  17.0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем конкатенацию двух объектов, \n",
    "# но при этом создаем индекс с помощью\n",
    "# заданных ключей \n",
    "c = pd.concat([df1, df2], keys=['df1', 'df2'])\n",
    "# обратите внимание на метки строк в выводе\n",
    "c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a   b   c     d\n",
       "0   9 NaN  10  11.0\n",
       "1  12 NaN  13  14.0\n",
       "2  15 NaN  16  17.0"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# мы можем извлечь данные, относящиеся к первому \n",
    "# или второму исходному датафрейму\n",
    "c.loc['df2']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   a  b  c   a   c   d\n",
       "0  0  1  2   9  10  11\n",
       "1  3  4  5  12  13  14\n",
       "2  6  7  8  15  16  17"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# конкатенируем датафреймы df1 и df2 по оси столбцов\n",
    "# выравниваем по меткам строк, \n",
    "# получаем дублирующиеся столбцы\n",
    "pd.concat([df1, df2], axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a   d\n",
       "2  20  21\n",
       "3  22  23\n",
       "4  24  25"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем новый датафрейм d3, чтобы конкатенировать его \n",
    "# с датафреймом df1\n",
    "# датафрейм df3 имеет общую с датафреймом df1 \n",
    "# метку 2 и общий столбец (a)\n",
    "df3 = pd.DataFrame(np.arange(20, 26).reshape(3, 2), \n",
    "                   columns=['a', 'd'], \n",
    "                   index=[2, 3, 4])\n",
    "df3"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "     a    b    c     a     d\n",
       "0  0.0  1.0  2.0   NaN   NaN\n",
       "1  3.0  4.0  5.0   NaN   NaN\n",
       "2  6.0  7.0  8.0  20.0  21.0\n",
       "3  NaN  NaN  NaN  22.0  23.0\n",
       "4  NaN  NaN  NaN  24.0  25.0"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# конкатерируем их по оси столбцов. Происходит выравнивание по меткам строк,\n",
    "# осуществляется заполнение значений столбцов df1, а затем \n",
    "# столбцов df3, получаем дублирующиеся столбцы\n",
    "pd.concat([df1, df3], axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   a  b  c   a   d\n",
       "2  6  7  8  20  21"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем внутреннее соединение вместо внешнего\n",
    "# результат представлен в виде одной строки\n",
    "pd.concat([df1, df3], axis=1, join='inner')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  df1       df2        \n",
       "    a  b  c   a   c   d\n",
       "0   0  1  2   9  10  11\n",
       "1   3  4  5  12  13  14\n",
       "2   6  7  8  15  16  17"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# добавляем ключи к столбцам\n",
    "df = pd.concat([df1, df2], \n",
    "               axis=1,\n",
    "               keys=['df1', 'df2'])\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a   c   d\n",
       "0   9  10  11\n",
       "1  12  13  14\n",
       "2  15  16  17"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# извлекаем данные из датафрейма \n",
    "# с помощью ключа 'df2'\n",
    "df.loc[:, 'df2']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a    b   c     d\n",
       "0   0  1.0   2   NaN\n",
       "1   3  4.0   5   NaN\n",
       "2   6  7.0   8   NaN\n",
       "0   9  NaN  10  11.0\n",
       "1  12  NaN  13  14.0\n",
       "2  15  NaN  16  17.0"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# метод .append() выполняет конкатенацию по оси острок (axis=0), \n",
    "# в результате получаем дублирующиеся индексные метки строк\n",
    "df1.append(df2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    a    b   c     d\n",
       "0   0  1.0   2   NaN\n",
       "1   3  4.0   5   NaN\n",
       "2   6  7.0   8   NaN\n",
       "3   9  NaN  10  11.0\n",
       "4  12  NaN  13  14.0\n",
       "5  15  NaN  16  17.0"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# избавляемся от дублирования меток в итоговом индексе,  \n",
    "# игнорируя индексные метки в датафреймах-источниках\n",
    "df1.append(df2, ignore_index=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Слияние данных"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "              Address  CustomerID    Name\n",
       "0    Address for Mike          10    Mike\n",
       "1  Address for Marcia          11  Marcia"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# это наши клиенты\n",
    "customers = {'CustomerID': [10, 11],\n",
    "             'Name': ['Mike', 'Marcia'],\n",
    "             'Address': ['Address for Mike',\n",
    "                         'Address for Marcia']}\n",
    "customers = pd.DataFrame(customers)\n",
    "customers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   CustomerID   OrderDate\n",
       "0          10  2014-12-01\n",
       "1          11  2014-12-01\n",
       "2          10  2014-12-01"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# это наши заказы, сделанные клиентами,\n",
    "# они связаны с клиентами с помощью столбца CustomerID\n",
    "orders = {'CustomerID': [10, 11, 10],\n",
    "          'OrderDate': [date(2014, 12, 1),\n",
    "                        date(2014, 12, 1),\n",
    "                        date(2014, 12, 1)]}\n",
    "orders = pd.DataFrame(orders)\n",
    "orders"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "              Address  CustomerID    Name   OrderDate\n",
       "0    Address for Mike          10    Mike  2014-12-01\n",
       "1    Address for Mike          10    Mike  2014-12-01\n",
       "2  Address for Marcia          11  Marcia  2014-12-01"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем слияние датафреймов customers и orders так, чтобы\n",
    "# мы могли отправить товары\n",
    "customers.merge(orders)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  lval1\n",
       "0    a    x      0\n",
       "1    b    y      1\n",
       "2    c    z      2"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем данные, которые будем использовать в качестве примеров\n",
    "# в оставшейся части этого раздела\n",
    "left_data = {'key1': ['a', 'b', 'c'], \n",
    "            'key2': ['x', 'y', 'z'],\n",
    "            'lval1': [ 0, 1, 2]}\n",
    "right_data = {'key1': ['a', 'b', 'c'],\n",
    "              'key2': ['x', 'a', 'z'], \n",
    "              'rval1': [ 6, 7, 8 ]}\n",
    "left = pd.DataFrame(left_data, index=[0, 1, 2])\n",
    "right = pd.DataFrame(right_data, index=[1, 2, 3])\n",
    "left"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  rval1\n",
       "1    a    x      6\n",
       "2    b    a      7\n",
       "3    c    z      8"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "right"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  lval1  rval1\n",
       "0    a    x      0      6\n",
       "1    c    z      2      8"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# демонстрируем слияние, не указывая столбцы, по которым\n",
    "# нужно выполнить слияние\n",
    "# этот программный код неявно выполняет слияние \n",
    "# по всем общим столбцам\n",
    "left.merge(right)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2_x  lval1 key2_y  rval1\n",
       "0    a      x      0      x      6\n",
       "1    b      y      1      a      7\n",
       "2    c      z      2      z      8"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# демонстрируем слияние, явно задав столбец,\n",
    "# по значениям которого нужно связать \n",
    "# объекты DataFrame \n",
    "left.merge(right, on='key1')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  lval1  rval1\n",
       "0    a    x      0      6\n",
       "1    c    z      2      8"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# явно выполняем слияние с помощью двух столбцов\n",
    "left.merge(right, on=['key1', 'key2'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1_x key2_x  lval1 key1_y key2_y  rval1\n",
       "1      b      y      1      a      x      6\n",
       "2      c      z      2      b      a      7"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# соединяем индексы строк обеих матриц\n",
    "pd.merge(left, right, left_index=True, right_index=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Настройка семантики соединения при выполнении слияния"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  lval1  rval1\n",
       "0    a    x    0.0    6.0\n",
       "1    b    y    1.0    NaN\n",
       "2    c    z    2.0    8.0\n",
       "3    b    a    NaN    7.0"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# внешнее соединение возращает все строки из внутреннего соединения, \n",
    "# а также строки датафреймов left и right,\n",
    "# не попавшие во внутреннее соединение \n",
    "# отсутствующие элементы заполняются значениями NaN\n",
    "left.merge(right, how='outer')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  lval1  rval1\n",
       "0    a    x      0    6.0\n",
       "1    b    y      1    NaN\n",
       "2    c    z      2    8.0"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# левое соединение возращает все строки из внутреннего соединения, \n",
    "# а также строки датафрейма left,\n",
    "# не попавшие во внутреннее соединение \n",
    "# отсутствующие элементы заполняются значениями NaN\n",
    "# итоговый датафрейм содержит общие строки датафреймов \n",
    "# left и right с метками 0 и 2 (строки датафреймов left и right, \n",
    "# у которых совпали значения в общих столбцах key1 и key2)\n",
    "# а также уникальную строку датафрейма left с меткой 1 \n",
    "# уникальная строка датафрейма left в итоговом датафрейме\n",
    "# в столбце rval1 заполняется значением NaN, потому что\n",
    "# в датафрейме left этот столбец отсутствовал\n",
    "left.merge(right, how='left')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1 key2  lval1  rval1\n",
       "0    a    x    0.0      6\n",
       "1    c    z    2.0      8\n",
       "2    b    a    NaN      7"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# правое соединение возращает все строки из внутреннего соединения, \n",
    "# а также строки датафрейма right,\n",
    "# не попавшие во внутреннее соединение \n",
    "# отсутствующие элементы заполняются значениями NaN\n",
    "# итоговый датафрейм содержит общие строки датафреймов \n",
    "# left и right с метками 0 и 1 (строки датафреймов left и right, \n",
    "# у которых совпали значения в общих столбцах key1 и key2)\n",
    "# а также уникальную строку датафрейма right с меткой 2\n",
    "# уникальная строка датафрейма right в итоговом датафрейме\n",
    "# в столбце lval1 заполняется значением NaN, потому что\n",
    "# в датафрейме right этот столбец отсутствовал\n",
    "left.merge(right, how='right')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1_left key2_left  lval1 key1_right key2_right  rval1\n",
       "0         a         x      0        NaN        NaN    NaN\n",
       "1         b         y      1          a          x    6.0\n",
       "2         c         z      2          b          a    7.0"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# соединяем left с right (метод по умолчанию - outer)\n",
    "# и поскольку эти объекты имеют дублирующиеся имена столбцов\n",
    "# мы задаем параметы lsuffix и rsuffix\n",
    "left.join(right, lsuffix='_left', rsuffix='_right')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  key1_left key2_left  lval1 key1_right key2_right  rval1\n",
       "1         b         y      1          a          x      6\n",
       "2         c         z      2          b          a      7"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# соединяем left с right с помощью внутреннего соединения\n",
    "left.join(right, lsuffix='_left', rsuffix='_right', how='inner')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Поворот"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "    interval axis  reading\n",
       "0          0    X      0.0\n",
       "1          0    Y      0.5\n",
       "2          0    Z      1.0\n",
       "3          1    X      0.1\n",
       "4          1    Y      0.4\n",
       "..       ...  ...      ...\n",
       "7          2    Y      0.3\n",
       "8          2    Z      0.8\n",
       "9          3    X      0.3\n",
       "10         3    Y      0.2\n",
       "11         3    Z      0.7\n",
       "\n",
       "[12 rows x 3 columns]"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# считываем данные акселерометра\n",
    "sensor_readings = pd.read_csv(\"C:/data/accel.csv\")\n",
    "sensor_readings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   interval axis  reading\n",
       "0         0    X      0.0\n",
       "3         1    X      0.1\n",
       "6         2    X      0.2\n",
       "9         3    X      0.3"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# извлекаем показания по оси X\n",
    "sensor_readings[sensor_readings['axis'] == 'X']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "axis        X    Y    Z\n",
       "interval               \n",
       "0         0.0  0.5  1.0\n",
       "1         0.1  0.4  0.9\n",
       "2         0.2  0.3  0.8\n",
       "3         0.3  0.2  0.7"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# поворачиваем данные. Интервалы становятся индексом, столбцы -\n",
    "# это оси, а показания - значения столбцов\n",
    "sensor_readings.pivot(index='interval', \n",
    "                     columns='axis', \n",
    "                     values='reading')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Состыковка с помощью неиерархических индексов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "     a\n",
       "one  1\n",
       "two  2"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем простой датафрейм с одним столбцом\n",
    "df = pd.DataFrame({'a': [1, 2]}, index={'one', 'two'})\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "one  a    1\n",
       "two  a    2\n",
       "dtype: int64"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# помещаем столбец в еще один уровень индекса строк \n",
    "# результатом становится объект Series, в которой\n",
    "# значения можно просмотреть с помощью мультииндекса\n",
    "stacked1 = df.stack()\n",
    "stacked1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# ищем значение для 'one'/'a', передав кортеж в индексатор\n",
    "stacked1[('one', 'a')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "     a  b\n",
       "one  1  3\n",
       "two  2  4"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем датафрейм с двумя столбцами\n",
    "df = pd.DataFrame({'a': [1, 2],\n",
    "                   'b': [3, 4]}, \n",
    "                  index={'one', 'two'})\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "one  a    1\n",
       "     b    3\n",
       "two  a    2\n",
       "     b    4\n",
       "dtype: int64"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# помещаем оба столбца в отдельный уровень индекса\n",
    "stacked2 = df.stack()\n",
    "stacked2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# ищем значение с помощью индекса 'one' / 'b'\n",
    "stacked2[('one', 'b')]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Расстыковка с помощью иерархических индексов"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "                      reading\n",
       "who    interval axis         \n",
       "Mike   0        X         0.0\n",
       "                Y         0.5\n",
       "                Z         1.0\n",
       "       1        X         0.1\n",
       "                Y         0.4\n",
       "...                       ...\n",
       "Mikael 2        Y        30.0\n",
       "                Z        80.0\n",
       "       3        X        30.0\n",
       "                Y        20.0\n",
       "                Z        70.0\n",
       "\n",
       "[24 rows x 1 columns]"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# создаем две копии данных акселерометра, \n",
    "# по одной для каждого пользователя\n",
    "user1 = sensor_readings.copy()\n",
    "user2 = sensor_readings.copy()\n",
    "# добавляем столбец who в каждую копию\n",
    "user1['who'] = 'Mike'\n",
    "user2['who'] = 'Mikael'\n",
    "# давайте отмасштабируем данные user2\n",
    "user2['reading'] *= 100\n",
    "# и организуем данные так, чтобы получить иерархический\n",
    "# индекс строк\n",
    "multi_user_sensor_data = pd.concat([user1, user2]) \\\n",
    "            .set_index(['who', 'interval', 'axis'])\n",
    "multi_user_sensor_data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "               reading\n",
       "interval axis         \n",
       "0        X         0.0\n",
       "         Y         0.5\n",
       "         Z         1.0\n",
       "1        X         0.1\n",
       "         Y         0.4\n",
       "...                ...\n",
       "2        Y         0.3\n",
       "         Z         0.8\n",
       "3        X         0.3\n",
       "         Y         0.2\n",
       "         Z         0.7\n",
       "\n",
       "[12 rows x 1 columns]"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# извлекаем показания, относящиеся к пользователю Mike,\n",
    "# с помощью индекса\n",
    "multi_user_sensor_data.loc['Mike']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "             reading\n",
       "who    axis         \n",
       "Mike   X         0.1\n",
       "       Y         0.4\n",
       "       Z         0.9\n",
       "Mikael X        10.0\n",
       "       Y        40.0\n",
       "       Z        90.0"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# извлекаем все показания по всем осям\n",
    "# и по всем пользователям в интервале 1\n",
    "multi_user_sensor_data.xs(1, level='interval')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "                reading             \n",
       "axis                  X     Y      Z\n",
       "who    interval                     \n",
       "Mikael 0            0.0  50.0  100.0\n",
       "       1           10.0  40.0   90.0\n",
       "       2           20.0  30.0   80.0\n",
       "       3           30.0  20.0   70.0\n",
       "Mike   0            0.0   0.5    1.0\n",
       "       1            0.1   0.4    0.9\n",
       "       2            0.2   0.3    0.8\n",
       "       3            0.3   0.2    0.7"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем расстыковку, в результате самый внутренний \n",
    "# уровень индекса строк (уровень axis) \n",
    "# стал уровнем индекса столбцов\n",
    "multi_user_sensor_data.unstack()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "              reading     \n",
       "who            Mikael Mike\n",
       "interval axis             \n",
       "0        X        0.0  0.0\n",
       "         Y       50.0  0.5\n",
       "         Z      100.0  1.0\n",
       "1        X       10.0  0.1\n",
       "         Y       40.0  0.4\n",
       "...               ...  ...\n",
       "2        Y       30.0  0.3\n",
       "         Z       80.0  0.8\n",
       "3        X       30.0  0.3\n",
       "         Y       20.0  0.2\n",
       "         Z       70.0  0.7\n",
       "\n",
       "[12 rows x 2 columns]"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем расстыковку по уровню 0\n",
    "multi_user_sensor_data.unstack(level=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "         reading                              \n",
       "who         Mike           Mikael             \n",
       "axis           X    Y    Z      X     Y      Z\n",
       "interval                                      \n",
       "0            0.0  0.5  1.0    0.0  50.0  100.0\n",
       "1            0.1  0.4  0.9   10.0  40.0   90.0\n",
       "2            0.2  0.3  0.8   20.0  30.0   80.0\n",
       "3            0.3  0.2  0.7   30.0  20.0   70.0"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# выполняем расстыковку уровней\n",
    "# who и axis\n",
    "unstacked = multi_user_sensor_data.unstack(['who', 'axis'])\n",
    "unstacked"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "                reading             \n",
       "axis                  X     Y      Z\n",
       "interval who                        \n",
       "0        Mikael     0.0  50.0  100.0\n",
       "         Mike       0.0   0.5    1.0\n",
       "1        Mikael    10.0  40.0   90.0\n",
       "         Mike       0.1   0.4    0.9\n",
       "2        Mikael    20.0  30.0   80.0\n",
       "         Mike       0.2   0.3    0.8\n",
       "3        Mikael    30.0  20.0   70.0\n",
       "         Mike       0.3   0.2    0.7"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# и, конечно, мы можем выполнить состыковку уровней, \n",
    "# которые расстыковали\n",
    "# выполняем состыковку уровня who\n",
    "unstacked.stack(level='who')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Расплавление"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "   Height    Name  Weight\n",
       "0     6.1    Mike     220\n",
       "1     6.0  Mikael     185"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# продемонстрируем расплавление \n",
    "# с помощью этого датафрейма\n",
    "data = pd.DataFrame({'Name' : ['Mike', 'Mikael'],\n",
    "                     'Height' : [6.1, 6.0],\n",
    "                     'Weight' : [220, 185]})\n",
    "data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "     Name variable  value\n",
       "0    Mike   Height    6.1\n",
       "1  Mikael   Height    6.0\n",
       "2    Mike   Weight  220.0\n",
       "3  Mikael   Weight  185.0"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# расплавляем датафрейм, используем Name \n",
    "# в качестве идентификатора, а столбцы \n",
    "# Height and Weight в качестве переменных\n",
    "pd.melt(data, \n",
    "        id_vars=['Name'],\n",
    "        value_vars=['Height', 'Weight'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Преимущества использования состыкованных данных"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(24.243854494566037, 1.3032710876761477, 0.11965651392785048)"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# поиск значений в состыкованных данных может быть\n",
    "# намного быстрее поиска в обычных данных \n",
    "\n",
    "# время выполнения различных методов\n",
    "import timeit\n",
    "t = timeit.Timer(\"stacked1[('one', 'a')]\", \n",
    "                 \"from __main__ import stacked1, df\")\n",
    "r1 = timeit.timeit(lambda: stacked1.loc[('one', 'a')], \n",
    "                   number=10000)\n",
    "r2 = timeit.timeit(lambda: df.loc['one']['a'], \n",
    "                   number=10000)\n",
    "r3 = timeit.timeit(lambda: df.iloc[1, 0], \n",
    "                   number=10000)\n",
    "\n",
    "# и вот наши результаты...  Да, это самый быстрый метод из всех трех\n",
    "r1, r2, r3"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "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.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
