{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Основы программирования в Python\n", "\n", "## Семинар 8: решения" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Импортируем необходимые библиотеки:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from bs4 import BeautifulSoup\n", "import requests" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Загрузим страницу с проходными баллами с сайта Вышки:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "page = requests.get('https://ma.hse.ru/passingrade')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создадим объект `soup` и сохраним в него html-код страницы:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "soup = BeautifulSoup(page.text, 'lxml')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Проходные баллы и названия программ хранятся в таблице, причём не просто в таблице, а в таблице с атрибутом `class`, равным `data table bordered` (см. исходный код страницы):" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "table = soup.findAll('table', {'class' : 'bordered'})[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Баллы и названия курсов нужно искать в пределах этой таблицы. Обратим внимание, что все баллы заключены в тэги ``:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[ 35,\n", "  21,\n", "  33,\n", "  21,\n", "  30]" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "table.findAll('strong')[0:5] # для примера первые 5 элементов" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сохраним все баллы в список, вытащив из тэгов текст. Воспользуемся списковым включением:" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['\\xa035', '\\xa021', '\\xa033', '\\xa021', '\\xa030']" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sc = table.findAll('strong')\n", "scores = [score.text for score in sc]\n", "scores[0:5] # первые несколько примеров" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Значения сохранились с пробелами вначале, которые в текущей кодировке страницы записываются как `\\xa0`. Пока их трогать не будем, лучше преобразуем, когда созраним все значения в датафрейм. Лучше обратим внимание на такую проблему: в списке не хватает одного значения: проходной балл для коммерческих мест на программе «Физика». Соответствующая ячейка не заполнена (таких нет), но это сбивает последующую нумерацию элементов, а нам она пригодится. Добавим пропущенное значение (дефис) сами на нужное место:" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "22\n" ] } ], "source": [ "print(scores.index('110'))" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [], "source": [ "scores.insert(23, '-')" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['110', '-', '\\xa0-']" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "scores[22:25]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Похожая история с магистратурой «Церковь, общество, государство». Добавим недостающее «пустое» значение:" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [], "source": [ "scores.insert(153, '-')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как отделить баллы, соответствующие бюджетным местам, от баллов на коммерческие места? Можно заметить, что все баллы на бюджетные места имеют чётные индексы в списке `scores`, а на коммерческие – нечётные. Воспользуемся этим и рассортируем значения: " ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [], "source": [ "budget = []\n", "comm = []\n", "for i in range(0, len(scores)):\n", " if i % 2 == 0:\n", " budget.append(scores[i])\n", " else:\n", " comm.append(scores[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Списки с баллами сформированы, осталось выгрузить названия образовательных программ. Заметим, что названия сохранены в тех же тэгах, что и ссылки на них (тэг `a`). Воспользуемся этим:" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Математика',\n", " 'Mathematics',\n", " 'Математика и математическая физика',\n", " 'Совместная магистратура НИУ ВШЭ и Центра педагогического мастерства',\n", " 'Анализ данных в биологии и медицине']" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "links = table.findAll('a')\n", "masters = [link.text for link in links]\n", "masters[0:5] # несколько примеров из списка " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь попробуем объединить полученные списки в датафрейм, предварительно создав словарь, где ключами являются названия столбцов, а значениями – списки значений:" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "arrays must all be same length", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m df = pd.DataFrame({'programme' : masters, \n\u001b[1;32m 2\u001b[0m \u001b[0;34m'budget'\u001b[0m \u001b[0;34m:\u001b[0m \u001b[0mbudget\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m 'commerce' : comm})\n\u001b[0m", "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, data, index, columns, dtype, copy)\u001b[0m\n\u001b[1;32m 346\u001b[0m dtype=dtype, copy=copy)\n\u001b[1;32m 347\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 348\u001b[0;31m \u001b[0mmgr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_init_dict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumns\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 349\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mMaskedArray\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 350\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mma\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmrecords\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mmrecords\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py\u001b[0m in \u001b[0;36m_init_dict\u001b[0;34m(self, data, index, columns, dtype)\u001b[0m\n\u001b[1;32m 457\u001b[0m \u001b[0marrays\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mk\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mkeys\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 458\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 459\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0m_arrays_to_mgr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marrays\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata_names\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumns\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 460\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 461\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_init_ndarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mvalues\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mindex\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcolumns\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcopy\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py\u001b[0m in \u001b[0;36m_arrays_to_mgr\u001b[0;34m(arrays, arr_names, index, columns, dtype)\u001b[0m\n\u001b[1;32m 7313\u001b[0m \u001b[0;31m# figure out the index, if necessary\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7314\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mindex\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 7315\u001b[0;31m \u001b[0mindex\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mextract_index\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marrays\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7316\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7317\u001b[0m \u001b[0;31m# don't force copy because getting jammed in an ndarray anyway\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;32m/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py\u001b[0m in \u001b[0;36mextract_index\u001b[0;34m(data)\u001b[0m\n\u001b[1;32m 7359\u001b[0m \u001b[0mlengths\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mraw_lengths\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7360\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlengths\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 7361\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'arrays must all be same length'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7362\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7363\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhave_dicts\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", "\u001b[0;31mValueError\u001b[0m: arrays must all be same length" ] } ], "source": [ "df = pd.DataFrame({'programme' : masters, \n", " 'budget' : budget,\n", " 'commerce' : comm})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Не получилось! Почему? Потому что списки имеют разную длину. Проверим!" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "110 110 144\n" ] } ], "source": [ "print(len(budget), len(comm), len(masters))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Действительно, длины разные. Посмотрим, в чём проблема:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Математика',\n", " 'Mathematics',\n", " 'Математика и математическая физика',\n", " 'Совместная магистратура НИУ ВШЭ и Центра педагогического мастерства',\n", " 'Анализ данных в биологии и медицине']" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "masters[0:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Англоязычные названия программ, реализуемых на английском языке, тоже сохраняются в списке! Уберём дубликаты с помощью регулярных выражений: отфильтруем только те названия, которые на кириллице." ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Математика']" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import re # импортируем модуль для регулярных выражений\n", "re.findall('[А-Я|а-я]+', masters[0]) # для первого элемента" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Математика',\n", " 'Математика и математическая физика',\n", " 'Совместная магистратура НИУ ВШЭ и Центра педагогического мастерства',\n", " 'Анализ данных в биологии и медицине',\n", " 'Математические методы моделирования и компьютерные технологии',\n", " 'Науки о данных',\n", " 'Прикладная статистика с методами сетевого анализа',\n", " 'Статистическая теория обучения',\n", " 'Финансовые технологии и анализ данных',\n", " 'Системы управления и обработки информации в инженерии']" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# поместим всё в цикл\n", "progs = []\n", "for p in masters:\n", " res = re.findall('([А-Я]|[а-я])', p)\n", " if len(res) != 0:\n", " progs.append(p)\n", "progs[0:10] # готово!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Проверим длины списков теперь:" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "110 110 118\n" ] } ], "source": [ "print(len(budget), len(comm), len(progs))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Опять много! Уберём баллы для очно-заочной формы обучения (в списках баллов их нет, если что, сгрузим отдельно):" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [], "source": [ "progs = progs[0:-7]" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "110 110 111\n" ] } ], "source": [ "print(len(budget), len(comm), len(progs)) # и снова неудача!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Из-за того, что в случае совместного конкурса в ячейке с программами по экономике стоят сразу две программы («Прикладная экономика» и «Экономика: исследовательская программа»), у нас образовалось лишнее значение. Давайте одно удалим, а второе честно поправим на более общее:" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "progs.index(\"Экономика: исследовательская программа\")" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [], "source": [ "progs.remove(\"Экономика: исследовательская программа\")\n", "progs[31] = 'Единый конкурс на образовательные программы \"Прикладная экономика\" и \"Экономика: исследовательская программа\"'" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "110 110 110\n" ] } ], "source": [ "print(len(budget), len(comm), len(progs)) # ура!" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
programmebudgetcommerce
0Математика3521
1Математика и математическая физика3321
2Совместная магистратура НИУ ВШЭ и Центра педаг...3021
3Анализ данных в биологии и медицине5332
4Математические методы моделирования и компьюте...7169
\n", "
" ], "text/plain": [ " programme budget commerce\n", "0 Математика  35  21\n", "1 Математика и математическая физика  33  21\n", "2 Совместная магистратура НИУ ВШЭ и Центра педаг...  30  21\n", "3 Анализ данных в биологии и медицине  53  32\n", "4 Математические методы моделирования и компьюте...  71  69" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df = pd.DataFrame({'programme' : progs, \n", " 'budget' : budget,\n", " 'commerce' : comm})\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Осталось привести в порядок значения в столбцах с баллами – сделать их числовыми (пока они текстовые). Напишем функцию, которая будет принимать на вход строку, убирать пробелы и конвертировать её в целое число. Если это сделать невозможно (например, в графе стоит прочерк, то функция будет возвращать `None`)." ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [], "source": [ "def to_number(x):\n", " n = x.strip()\n", " try:\n", " res = int(n)\n", " except:\n", " res = None\n", " return res" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [], "source": [ "df['commerce'] = df.commerce.apply(to_number)" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "RangeIndex: 110 entries, 0 to 109\n", "Data columns (total 3 columns):\n", "programme 110 non-null object\n", "budget 91 non-null float64\n", "commerce 105 non-null float64\n", "dtypes: float64(2), object(1)\n", "memory usage: 2.7+ KB\n" ] } ], "source": [ "df.info() # готово!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сохраним таблицу в Excel-файл:" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [], "source": [ "df.to_excel('master-hse.xlsx')" ] } ], "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.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }