{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "9c7c1cc4",
   "metadata": {},
   "source": [
    "# Словари. Пример программы #"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e4b389fd",
   "metadata": {},
   "source": [
    "Итак, мы с вами разобрали словари и то, как они работают, и давайте попробуем решить задачку на их применение.\n",
    "\n",
    "В качестве примера попробуем найти три самых часто встречающихся слова в Zen of Python. Как вы уже знаете, Zen of Python можно получить, импортировав `this`, и в нём содержится какой-то набор утверждений, которым должен следовать программист, который пишет на Python.\n",
    "\n",
    "Например, красивое лучше чем некрасивое."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e74604f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The Zen of Python, by Tim Peters\n",
      "\n",
      "Beautiful is better than ugly.\n",
      "Explicit is better than implicit.\n",
      "Simple is better than complex.\n",
      "Complex is better than complicated.\n",
      "Flat is better than nested.\n",
      "Sparse is better than dense.\n",
      "Readability counts.\n",
      "Special cases aren't special enough to break the rules.\n",
      "Although practicality beats purity.\n",
      "Errors should never pass silently.\n",
      "Unless explicitly silenced.\n",
      "In the face of ambiguity, refuse the temptation to guess.\n",
      "There should be one-- and preferably only one --obvious way to do it.\n",
      "Although that way may not be obvious at first unless you're Dutch.\n",
      "Now is better than never.\n",
      "Although never is often better than *right* now.\n",
      "If the implementation is hard to explain, it's a bad idea.\n",
      "If the implementation is easy to explain, it may be a good idea.\n",
      "Namespaces are one honking great idea -- let's do more of those!\n"
     ]
    }
   ],
   "source": [
    "import this"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77beacdd",
   "metadata": {},
   "source": [
    "Давайте создадим нашу переменную `zen`, в которую положим эти замечательные утверждения. Мы должны использовать тройные кавычки, потому что здесь есть переносы строк. Скопируем все строки и поместим в `zen`. Отлично."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "f7b07b1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "zen = \"\"\"Beautiful is better than ugly.\n",
    "Explicit is better than implicit.\n",
    "Simple is better than complex.\n",
    "Complex is better than complicated.\n",
    "Flat is better than nested.\n",
    "Sparse is better than dense.\n",
    "Readability counts.\n",
    "Special cases aren't special enough to break the rules.\n",
    "Although practicality beats purity.\n",
    "Errors should never pass silently.\n",
    "Unless explicitly silenced.\n",
    "In the face of ambiguity, refuse the temptation to guess.\n",
    "There should be one-- and preferably only one --obvious way to do it.\n",
    "Although that way may not be obvious at first unless you're Dutch.\n",
    "Now is better than never.\n",
    "Although never is often better than *right* now.\n",
    "If the implementation is hard to explain, it's a bad idea.\n",
    "If the implementation is easy to explain, it may be a good idea.\n",
    "Namespaces are one honking great idea -- let's do more of those!\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34a73654",
   "metadata": {},
   "source": [
    "Теперь нам нужно каким-то образом выяснить, какие слова используются чаще всего. Логично нам нужно для начала разбить нашу большую длинную строчку на, собственно, эти самые слова. \n",
    "\n",
    "Давайте используем для этого переменную `zen_map` какой-то `dict`, где мы будем хранить собственно количество слов, которое мы уже нашли, и будем итерироваться с помощью метода `split`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "23b04337",
   "metadata": {},
   "outputs": [],
   "source": [
    "zen_map = dict()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "36889d1e",
   "metadata": {},
   "source": [
    "Метод `split` разобьёт нашу большую строку по пробельным символам. У нас получится какое-то слово при итерации. Теперь, что нам нужно сделать с этим словом? Нам нужно, во-первых, его очистить от каких-то точек, от каких-то восклицательных знаков и прочих спецсимволов. Давайте назовём переменную `cleaned_word` и будем очищать наше слово, используя метод строки `strip`.\n",
    "\n",
    "Очистим всё, что мы можем найти. Отлично. Теперь давайте приведём строку к нижнему регистру с помощью метода `lower`. И теперь нам нужно добавить нашу строку в наш `zen_map`.\n",
    "\n",
    "Если строка уже есть в `zen_map`'е, мы просто хотим инкрементировать `counter`, который говорит о том, сколько раз мы находили уже это слово. Если строки там нет, то мы туда должны её добавить, и давайте именно это и сделаем.\n",
    "\n",
    "Если `cleaned_word`'а ещё нету в `zen_map`'е, мы добавим наш `zen_map` в `cleaned_word`. В качестве значения у нас будет 0, потому что мы это слово ещё не встречали. Если мы его встречали или только что добавили, мы инкрементируем это значение.\n",
    "\n",
    "Таким образом, в нашем `zen_map`'e будет хранится отображение из слова в количество раз, в котором мы его встретили."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "4c71d106",
   "metadata": {},
   "outputs": [],
   "source": [
    "for word in zen.split():\n",
    "    cleaned_word = word.strip(\".,!-*\").lower()\n",
    "    \n",
    "    if cleaned_word not in zen_map:\n",
    "        zen_map[cleaned_word] = 0\n",
    "        \n",
    "    zen_map[cleaned_word] += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16f875bd",
   "metadata": {},
   "source": [
    "Давайте попробуем вывести наш `zen_map`. Отлично."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "41ca869f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'beautiful': 1, 'is': 10, 'better': 8, 'than': 8, 'ugly': 1, 'explicit': 1, 'implicit': 1, 'simple': 1, 'complex': 2, 'complicated': 1, 'flat': 1, 'nested': 1, 'sparse': 1, 'dense': 1, 'readability': 1, 'counts': 1, 'special': 2, 'cases': 1, \"aren't\": 1, 'enough': 1, 'to': 5, 'break': 1, 'the': 5, 'rules': 1, 'although': 3, 'practicality': 1, 'beats': 1, 'purity': 1, 'errors': 1, 'should': 2, 'never': 3, 'pass': 1, 'silently': 1, 'unless': 2, 'explicitly': 1, 'silenced': 1, 'in': 1, 'face': 1, 'of': 2, 'ambiguity': 1, 'refuse': 1, 'temptation': 1, 'guess': 1, 'there': 1, 'be': 3, 'one': 3, 'and': 1, 'preferably': 1, 'only': 1, 'obvious': 2, 'way': 2, 'do': 2, 'it': 2, 'that': 1, 'may': 2, 'not': 1, 'at': 1, 'first': 1, \"you're\": 1, 'dutch': 1, 'now': 2, 'often': 1, 'right': 1, 'if': 2, 'implementation': 2, 'hard': 1, 'explain': 2, \"it's\": 1, 'a': 2, 'bad': 1, 'idea': 3, 'easy': 1, 'good': 1, 'namespaces': 1, 'are': 1, 'honking': 1, 'great': 1, '': 1, \"let's\": 1, 'more': 1, 'those': 1}\n"
     ]
    }
   ],
   "source": [
    "print(zen_map)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2717da3c",
   "metadata": {},
   "source": [
    "У нас хранится отображение из строки в количество раз, в котором мы её встретили. Например, `beautiful` у нас встретилось всего один раз, а `is` встретилось десять раз.\n",
    "    \n",
    "Теперь нам нужно каким-то образом выяснить, какие слова встречаются чаще всего. Обратите внимание, как ключи, так и значения в словаре не упорядочены, поэтому нам нужно сделать так, чтобы они были упорядочены. Для этого можно воспользоваться, например, методом `items` и, например, в `zen_items` мы можем положить `zen_map.items`. Это будет уже список таплов. Обратите внимание, у нас в нашем `zen_items` содержится как ключ, так и значение, но уже в виде тапла, и эту вещь мы уже можем сортировать."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "981664ee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict_items([('beautiful', 1), ('is', 10), ('better', 8), ('than', 8), ('ugly', 1), ('explicit', 1), ('implicit', 1), ('simple', 1), ('complex', 2), ('complicated', 1), ('flat', 1), ('nested', 1), ('sparse', 1), ('dense', 1), ('readability', 1), ('counts', 1), ('special', 2), ('cases', 1), (\"aren't\", 1), ('enough', 1), ('to', 5), ('break', 1), ('the', 5), ('rules', 1), ('although', 3), ('practicality', 1), ('beats', 1), ('purity', 1), ('errors', 1), ('should', 2), ('never', 3), ('pass', 1), ('silently', 1), ('unless', 2), ('explicitly', 1), ('silenced', 1), ('in', 1), ('face', 1), ('of', 2), ('ambiguity', 1), ('refuse', 1), ('temptation', 1), ('guess', 1), ('there', 1), ('be', 3), ('one', 3), ('and', 1), ('preferably', 1), ('only', 1), ('obvious', 2), ('way', 2), ('do', 2), ('it', 2), ('that', 1), ('may', 2), ('not', 1), ('at', 1), ('first', 1), (\"you're\", 1), ('dutch', 1), ('now', 2), ('often', 1), ('right', 1), ('if', 2), ('implementation', 2), ('hard', 1), ('explain', 2), (\"it's\", 1), ('a', 2), ('bad', 1), ('idea', 3), ('easy', 1), ('good', 1), ('namespaces', 1), ('are', 1), ('honking', 1), ('great', 1), ('', 1), (\"let's\", 1), ('more', 1), ('those', 1)])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "zen_items = zen_map.items()\n",
    "zen_items"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "367d5fc2",
   "metadata": {},
   "source": [
    "Как мы будем это делать? Если мы попробуем отсортировать `zen_items` прямо сейчас, то у нас сортировка произойдёт по первому значению, по `beautiful`.\n",
    "\n",
    "Однако, нам нужно сортировать по второму значению наших таплов, по количеству раз, в котором мы встречали это слово. Для этого нам поможет замечательный модуль `operator` и функция `sorted`. Воспользуемся функцией `sorted` и будем сортировать наши `zen_items`.\n",
    "\n",
    "Чтобы сортировать по второму значению в нашем тапле, мы можем в качестве аргумента `key` передать туда `operator.itemgetter` и написать единичку, потому что нас интересует именно первый индекс. `beautiful` — это нулевой индекс, единичка — это первый индекс.\n",
    "\n",
    "Так как нам нужны самые популярные слова, мы используем `reverse=True`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "d3b22620",
   "metadata": {},
   "outputs": [],
   "source": [
    "import operator\n",
    "\n",
    "word_count_items = sorted(\n",
    "    zen_items, key=operator.itemgetter(1), reverse=True\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "831b3bed",
   "metadata": {},
   "source": [
    "Давайте попробуем вывести `word_count_items` и возьмём первые три элемента. Отлично, у нас получилось, что самые популярные слова, это `is`, `better`, `than`, что мы собственно и ожидали."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "80cdc92d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('is', 10), ('better', 8), ('than', 8)]\n"
     ]
    }
   ],
   "source": [
    "print(word_count_items[:3])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dabaebe1",
   "metadata": {},
   "source": [
    "Однако, как это часто бывает в Python'е, есть встроенный модуль, который вам поможет решить эту задачу намного быстрее. Мы можем импортировать из модуля `collections` `Counter`. В случае, если мы используем `Counter`, всё, что нам нужно — это очистить наши слова и передать эти слова в `Counter`.\n",
    "\n",
    "Давайте именно это и сделаем.\n",
    "\n",
    "Создадим `cleaned_list`, в который в цикле будем добавлять очищенные слова уже знакомым образом, используя метод `split`, который разбивает строку по пробельным символам, и будем добавлять в `cleaned_lis`t с помощью метода `append` очищенное слово.\n",
    "\n",
    "Очищенное слово будет получено с помощью метода `strip` и приведения к нижнему регистру с помощью метода `lower`.\n",
    "\n",
    "Итак, всё, что нам осталось сделать, это вызвать `counter` с `cleaned_list`'ом и обратиться к методу `most_common`, перадать ему троечку.\n",
    "\n",
    "Давайте попробуем, что у нас получится. Отлично, именно то, что мы ожидали, однако, это достигается с помощью трёх строк кода."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "0c407d54",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('is', 10), ('better', 8), ('than', 8)]\n"
     ]
    }
   ],
   "source": [
    "from collections import Counter\n",
    "\n",
    "cleaned_list = []\n",
    "\n",
    "for word in zen.split():\n",
    "    cleaned_list.append(word.strip(\".,-!\").lower())\n",
    "    \n",
    "print(Counter(cleaned_list).most_common(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89af8ccd",
   "metadata": {},
   "source": [
    "Часто в Python'е есть встроенные методы и модули, которые вам позволяют делать какие-то вещи легко и удобно. Отлично, мы с вами познакомились со словарями и решили задачку на их применение."
   ]
  }
 ],
 "metadata": {
  "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.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
