{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Python для сбора и анализа данных\n", "\n", "*Разработчик формата сдачи заданий и фабрики автоматических тестов: Щуров И.В.
\n", "Автор задач: Тамбовцева А.А.*\n", "\n", "## Практикум 1: типы данных, ввод и вывод, форматирование строк" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Тесты для проверки кода\n", "\n", "На первом этапе проверки ваши решения в домашних заданиях и лабораторных работах будут проверяться с помощью автоматических тестов – ячеек с кодом, которые проверяют соответствие результатов исполнения ваших программ ожидаемым результатам в условиях задач. Эти тесты можно использовать для проверки кода перед сдачей задания. \n", "\n", "Итак, для предварительной проверки задания нужно сделать следующее:\n", "\n", "1. Скачать данный ipynb-файл на свой компьютер, открыть его в Jupyter Notebook.\n", "2. Активировать тесты (см. ниже).\n", "3. Вставить решение каждой задачи в ячейку для кода, следующую за его условием, там, где написан комментарий `# YOUR CODE HERE`. Отступ вашего кода должен составлять 4 пробела. Ничего не менять вокруг!\n", "4. Запустить ячейку, в которую вы вставили код с решением. Ввести какие-то входные данные, проверить визуально правильность вывода.\n", "5. Запустить следующую ячейку (в ней содержится тест). Если запуск ячейки с тестом не приводит к появлению ошибки (`assertion`), значит, всё в порядке, задача решена. Если приводит к появлению ошибки, значит, тест не пройден и нужно искать ошибку.\n", "\n", "Внимание! Если в какой-то момент забыть ввести входные данные и перейти на следующую ячейку, есть риск, что Notebook перестанет откликаться. В этом случае надо перезапустить ядро: *Kernel → Restart*. При этом потеряются все значения переменных, но сам код останется." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Активировать тесты\n", "\n", "Запустите следующую ячейку, чтобы иметь возможность запускать тесты." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Фабрика тестов для проверки программ, принимающих данные через input()\n", "\n", "from collections import deque\n", "\n", "class Tester(object):\n", " def __init__(self, inp):\n", " self.outputs = []\n", " self.inputs = deque(inp)\n", " def print(self, *args, sep = \" \", end = \"\\n\"):\n", " text = sep.join(map(str, args)) + end\n", " newlines = text.splitlines(keepends=True)\n", " if self.outputs and self.outputs[-1] and self.outputs[-1][-1] != \"\\n\" and newlines:\n", " self.outputs[-1] += newlines[0]\n", " self.outputs.extend(newlines[1:])\n", " else:\n", " self.outputs.extend(newlines)\n", " \n", " def input(self, *args):\n", " assert self.inputs, \"Вы пытаетесь считать больше элементов, чем предусмотрено условием\"\n", " return self.inputs.popleft()\n", " def __enter__(self):\n", " global print\n", " global input\n", " print = self.print\n", " input = self.input\n", " return self.outputs\n", " def __exit__(self, *args):\n", " global print\n", " global input\n", " del print\n", " del input" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Задача 1\n", "\n", "Напишите программу, которая запрашивает у пользователя имя и фамилию (отдельный ввод для имени и фамилии) и выводит на экран сообщение:\n", "\n", " Имя Фамилия, добро пожаловать!\n", " \n", "**Пример работы программы:**\n", "\n", "Ввод:\n", "\n", " Введите имя: Алла\n", " Введите фамилию: Тамбовцева\n", " \n", "Вывод:\n", "\n", " Алла Тамбовцева, добро пожаловать!" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Добби\n", "Свободный\n", "Добби Свободный, добро пожаловать!\n" ] } ], "source": [ "def hello():\n", " \n", " name = input()\n", " surname = input()\n", " print(f\"{name} {surname}, добро пожаловать!\")\n", " \n", "hello()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "test_data = [\n", " (\"Алла Тамбовцева\", \"Алла Тамбовцева, добро пожаловать!\"),\n", " (\"Добби Свободный\", \"Добби Свободный, добро пожаловать!\"),\n", " (\"0 1\", \"0 1, добро пожаловать!\")\n", "]\n", "\n", "for inp, out in test_data:\n", " with Tester(inp.split()) as t:\n", " hello()\n", " assert len(t) == 1, \"Вам нужно вывести ровно одну строку с ответом\"\n", " assert t[0] == out+\"\\n\", \"Неверный ответ, была введена строка \" + inp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Проверка кода:**\n", "\n", "Запускаем ячейку с тестами ниже и проверяем код, который вписали выше в `hello()`.\n", "\n", "* Если после запуска ничего не выводится на экран – всё хорошо, тесты пройдены.\n", "\n", "* Если ошибка вида `AssertionError`: код не прошел тест, проверьте, соответствует ли выводимый на экран текст условию, в частности, проверьте, на месте ли восклицательный знак и всё ли хорошо с пробелами. Запустите ячейку с `test_data` ещё раз.\n", "\n", "* Если ошибка вида `NameError` про `hello is not defined`: забыли запустить ячейку выше с `hello()` или перезапустили ядро в *Kernel* (Python все сбросил и забыл), запустите ячейку выше и проверьте, что в ней есть код с решением. Запустите ячейку с `test_data` ещё раз.\n", "\n", "* Если ошибка вида `NameError` про `Tester is not defined`: забыли запустить ячейку в начале файла с `Tester` (под заголовком *Активировать тесты*) или перезапустили ядро в *Kernel* (Python все сбросил и забыл), запустите эту ячейку. Запустите ячейку с `test_data` ещё раз." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Задача 2\n", "\n", "Напишите программу, которая запрашивает у пользователя с клавиатуры его рост в сантиметрах, его вес в килограммах (каждый показатель – с новой строки, в новом запросе) и выводит на экран сообщение вида:\n", "\n", " Индекс массы тела: [значение].\n", "\n", "где вместо `[значение]` подставляется посчитанное значение индекса массы тела, указанное с точностью до второго знака после запятой.\n", "\n", "Индекс массы тела считается так:\n", "\n", "$$\n", "\\text{BMI}=\\frac{m}{h^2},\n", "$$\n", "\n", "где $m$ – масса тела в килограммах, $h$ – рост *в метрах*.\n", "\n", "**Пример работы программы:**\n", "\n", "Ввод:\n", " \n", " Укажите рост: 168\n", " Укажите вес: 55\n", " \n", "Вывод:\n", " \n", " Индекс массы тела: 19.49." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Укажите рост: 168\n", "Укажите вес: 55\n", "Индекс массы тела: 19.49.\n" ] } ], "source": [ "# внимание: в print() нужно .2f для фиксации числа знаков после точки\n", "# просто {bmi} выдаст ответ в виде длинного числа без округлений\n", "\n", "def get_bmi():\n", " \n", " height = int(input(\"Укажите рост: \"))\n", " weight = int(input(\"Укажите вес: \"))\n", " bmi = weight / (height / 100) ** 2\n", " print(f\"Индекс массы тела: {bmi:.2f}.\")\n", " \n", "get_bmi()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "test_data = [\n", " (\"168 55\", \"Индекс массы тела: 19.49.\"),\n", " (\"180 70\", \"Индекс массы тела: 21.60.\"),\n", " (\"165 48\", \"Индекс массы тела: 17.63.\"),\n", " (\"167 89\", \"Индекс массы тела: 31.91.\")\n", "]\n", "\n", "for inp, out in test_data:\n", " with Tester(inp.split()) as t:\n", " get_bmi()\n", " assert len(t) == 1, \"Вам нужно вывести ровно одну строку с ответом\"\n", " assert t[0] == out+\"\\n\", \"Неверный ответ, была введена строка \" + inp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Задача 3\n", "\n", "Напишите программу, которая запрашивает у пользователя год рождения, информацию о том, был ли у того день рождения (`True`, если был, или пустая строка, если не был), и выводит на экран число полных лет этого пользователя.\n", "\n", "**Пример работы программы:**\n", "\n", "Ввод:\n", " \n", " 1994\n", " \n", " \n", "Вывод:\n", "\n", " 29\n", " \n", "Ввод:\n", " \n", " 1957\n", " True\n", " \n", "Вывод:\n", "\n", " 67" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Важный момент 1.** Python умеет переводить логическое `True` в число 1, а логическое `False` в 0, логика и двоичная система связаны." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1\n", "0\n" ] } ], "source": [ "print(int(True))\n", "print(int(False))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Важный момент 2.** Python умеет переводить строку `\"True\"` в логическое `True`, но не умеет переводить строку `\"False\"` в логическое `False`. Функция `bool()` от любого текста, включая `\"False\"`, вернет `True`. Функция `bool()` вернёт `False` только в одном случае – если применить её к пустой строке." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n", "True\n", "False\n" ] } ], "source": [ "print(bool(\"True\"))\n", "print(bool(\"False\"))\n", "print(bool(\"\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Идея решения:** считаем число полных лет на прошлый год (2023), а затем либо добавляем ещё один год (день рождения был), либо нет (дня рождения не было). Для этого пользуемся тем, что введённое слово `\"True\"` можно переделать в логическое `True`, а оно эквивалентно числу 1, а введённую пустую строку – в логическое `False` (что эквивалентно 0)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1994\n", "True\n", "30\n" ] } ], "source": [ "# решение 1: сами указываем год\n", "\n", "def counter():\n", " \n", " byear = int(input())\n", " binary = bool(input())\n", " print(2023 - byear + binary)\n", "\n", "counter()" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1994\n", "True\n", "30\n" ] } ], "source": [ "# решение 2: пользуемся знаниями об извлечении текущей даты с помощью datetime\n", "# используем функцию now() и забираем оттуда текущий год через атрибут .year\n", "\n", "from datetime import datetime\n", "\n", "def counter():\n", " \n", " byear = int(input())\n", " binary = bool(input())\n", " year = datetime.now().year\n", " print((year - 1) - byear + binary)\n", "\n", "counter()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "test_data = [\n", " (\"1994-\", \"29\"),\n", " (\"1963-True\", \"61\"),\n", " (\"1957-\", \"66\"),\n", " (\"2000-True\", \"24\")\n", "]\n", "\n", "for inp, out in test_data:\n", " with Tester(inp.split(\"-\")) as t:\n", " counter()\n", " assert len(t) == 1, \"Вам нужно вывести ровно одну строку с ответом\"\n", " assert t[0] == out+\"\\n\", \"Неверный ответ, была введена строка \" + inp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Задача 4\n", "\n", "В среднем за неделю Питон получает пять сообщений от Анаконды ($\\lambda$=5). Пользователь с клавиатуры вводит число сообщений, которые Анаконда может прислать Питону ($k$). Напишите программу, которая выводит на экран вероятность, с которой Питон получит $k$\n", "сообщений от Анаконды за неделю, с точностью до трёх знаков после запятой. Сообщение, выводимое на экран, должно быть такого вида:\n", "\n", " Число сообщений от Анаконды за неделю равно [k], вероятность равна [значение].\n", "\n", "Вероятность того, что Питон получит ровно $k$ сообщений, определяется следующим образом (распределение Пуассона):\n", "\n", "$$\n", "\\text{P}(X=k)=e^{−\\lambda}⋅\\frac{\\lambda^k}{k!}.\n", "$$\n", "\n", "Функцию `factorial()` для вычисления факториала можно вызвать из модуля `math`.\n", "\n", "**Пример работы программы:**\n", "\n", "Ввод:\n", "\n", " Число сообщений: 2\n", "\n", "Вывод:\n", "\n", " Число сообщений от Анаконды за неделю равно 2, вероятность равна 0.084." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Число сообщений:8\n", "Число сообщений от Анаконды за неделю равно 8, вероятность равна 0.065.\n" ] } ], "source": [ "from math import factorial, exp\n", "\n", "# почему lamda = 5, а не lambda = 5?\n", "# lambda в Python зарезервировано для специальных функций,\n", "# (подсвечивается зеленым), не стоит использовать\n", "\n", "def message():\n", " \n", " lamda = 5\n", " k = int(input(\"Число сообщений:\"))\n", " p = exp(-lamda) * lamda ** k / factorial(k)\n", " print(f\"Число сообщений от Анаконды за неделю равно {k}, вероятность равна {p:.3f}.\")\n", " \n", "message()" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "test_data = [\n", " (\"2\", \"Число сообщений от Анаконды за неделю равно 2, вероятность равна 0.084.\"),\n", " (\"0\", \"Число сообщений от Анаконды за неделю равно 0, вероятность равна 0.007.\"),\n", " (\"3\", \"Число сообщений от Анаконды за неделю равно 3, вероятность равна 0.140.\"),\n", " (\"5\", \"Число сообщений от Анаконды за неделю равно 5, вероятность равна 0.175.\"),\n", " (\"8\", \"Число сообщений от Анаконды за неделю равно 8, вероятность равна 0.065.\")\n", "]\n", "\n", "for inp, out in test_data:\n", " with Tester([inp]) as t:\n", " message()\n", " assert len(t) == 1, \"Вам нужно вывести ровно одну строку с ответом\"\n", " assert t[0] == out+\"\\n\", \"Неверный ответ, была введена строка \" + inp" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Дополнение.** Можем импортировать модуль `ipywidgets` и воспользоваться возможностями Jupyter Notebook для более интересного запроса ввода данных со стороны пользователя. " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "import ipywidgets as widgets" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Создадим слайдер для целых чисел (`IntSlider`) для выбора значений от 0 до 10 (по умолчанию шаг в слайдере 1, если пользователь ничего не выбрал, будет сохраняться значение из аргумента `value`):" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "26728c53cec34d8fa5208b847244f7c5", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntSlider(value=5, max=10)" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "slider_k = widgets.IntSlider(value = 5, min = 0, max = 10)\n", "display(slider_k)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Число сообщений от Анаконды за неделю равно 8, вероятность равна 0.065.\n" ] } ], "source": [ "# из объекта ipywidgets извлекаем значение – атрибут .value\n", "\n", "k = slider_k.value\n", "lamda = 5\n", "p = exp(-lamda)* lamda**k / factorial(k)\n", "print(f\"Число сообщений от Анаконды за неделю равно {k}, вероятность равна {p:.3f}.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Можем доработать слайдер – импортировать функцию `Layout()` для стилизации и добавить словарь `style`:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "from ipywidgets import Layout" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "02488808813844f7be1d0e5e12711799", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntSlider(value=5, description='Число сообщений: ', layout=Layout(width='50%'), max=10, style=SliderStyle(desc…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# description: текст описания слева от слайдера\n", "# style: стиль слайдера, регулируем длину текста\n", "# из-за длинного описания «съелось» место для самого слайдера\n", "# в Layout корректируем длину (в % от ширины страницы)\n", "\n", "slider_k = widgets.IntSlider(value = 5, \n", " min = 0, \n", " max = 10,\n", " description = 'Число сообщений: ',\n", " style = {'description_width': '100pt'}, \n", " layout = Layout(width='50%'))\n", "display(slider_k)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Кому в жизни не хватает красок:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "fd1bbcd782f6420984d99ac834d3545c", "version_major": 2, "version_minor": 0 }, "text/plain": [ "IntSlider(value=5, description='Число сообщений: ', layout=Layout(width='50%'), max=10, style=SliderStyle(desc…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# handle_color\n", "\n", "slider_k = widgets.IntSlider(value = 5, \n", " min = 0, \n", " max = 10,\n", " description = 'Число сообщений: ',\n", " style = {'description_width': '100pt', \n", " 'handle_color' : 'limegreen'}, \n", " layout = Layout(width='50%'))\n", "display(slider_k)" ] } ], "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.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }