Python для сбора и анализа данных

Домашнее задание №2

Домашнее задание состоит из двух частей. Для того, чтобы получить оценку 10, необходимо решить все задачи первой части (задачи с тестами) и хотя бы одну задачу из второй части (задачи без тестов). Вы можете решить больше задач, чем требуется, чтобы потренироваться.

Для предварительной проверки задания нужно сделать следующее:

  1. Скачать данный ipynb-файл на свой компьютер, открыть его в IPython Notebook/Jupyter.
  2. Активировать тесты (см. ниже).
  3. Вставить решение каждой задачи в ячейку для кода, следующую за его условием, там, где написан комментарий # YOUR CODE HERE. Отступ вашего кода должен составлять 4 пробела. Ничего не менять вокруг!
  4. Запустить ячейку, в которую вы вставили код с решением. Ввести какие-то входные данные, проверить визуально правильность вывода.
  5. Запустить следующую ячейку (в ней содержится тест). Если запуск ячейки с тестом не приводит к появлению ошибки (assertion), значит, всё в порядке, задача решена. Если приводит к появлению ошибки, значит, тест не пройден и нужно искать ошибку.

Внимание! Если в какой-то момент забыть ввести входные данные и перейти на следующую ячейку, есть риск, что Notebook перестанет откликаться. В этом случае надо перезапустить ядро: Kernel → Restart. При этом потеряются все значения переменных, но сам код останется.

Чтобы сдать ДЗ, его надо загрузить в nbgr-x в виде ipynb-файла. Получить ipynb-файл можно, выбрав в Jupyter пункт меню File → Download as... → IPython Notebook (.ipynb).

Активировать тесты

Запустите следующие ячейку, чтобы иметь возможность запускать тесты. Эту операцию нужно проделывать каждый раз, когда вы перезапускаете ядро. Если какой-то из тестов говорит NameError: name 'Tester' is not defined, нужно запустить эту ячейку ещё раз.

In [ ]:
# Фабрика тестов для проверки программ, принимающих данные через input()

from collections import deque

class Tester(object):
    def __init__(self, inp):
        self.outputs = []
        self.inputs = deque(inp)
    def print(self, *args, sep = " ", end = "\n"):
        text = sep.join(map(str, args)) + end
        newlines = text.splitlines(keepends=True)
        if self.outputs and self.outputs[-1] and self.outputs[-1][-1] != "\n" and newlines:
            self.outputs[-1] += newlines[0]
            self.outputs.extend(newlines[1:])
        else:
            self.outputs.extend(newlines)
            
    def input(self, *args):
        assert self.inputs, "Вы пытаетесь считать больше элементов, чем предусмотрено условием" 
        return self.inputs.popleft()
    def __enter__(self):
        global print
        global input
        print = self.print
        input = self.input
        return self.outputs
    def __exit__(self, *args):
        global print
        global input
        del print
        del input

Часть 1

Задача 1 (1 балл)

Создайте пустой словарь lens и добавьте в него две записи: одну с ключом "Liasis papuana" и значением 4, вторую – с ключом "Python reticulatus" и значением 6. Итоговый словарь должен выглядеть так:

{'Liasis papuana': 4, 'Python reticulatus' : 6}
In [ ]:
def py_dict():
    
    "# YOUR CODE HERE"
    
    return lens
py_dict()       
In [ ]:
assert py_dict() == {'Liasis papuana': 4, 'Python reticulatus': 6}, "Неверный ответ."

Задача 2 (1 балл)

У питона Пети в записной книжке хранятся полные имена друзей и знакомых из разных стран. Записная книжка представляет собой словарь:

pythons = {"королевский питон Рональд" : "Уганда", 
      "карликовый питон Антониа" : "Ангола", 
      "сетчатый питон Асиф" : "Бангладеш", 
      "зеленый питон Оливер" : "Австралия", 
      "тиморский питон Николау" : "Восточный Тимор", 
      "аметистовый питон Харрисон" : "Австралия", 
      "тигровый питон Мэйли" : "Китай",
      "оливковый питон Руби" : "Австралия", 
      "белогубый питон Арис" : "Индонезия", 
      "эфиопский питон Оника" : "Мозамбик"}

Питон Петя хочет съездить в Австралию, и поэтому хочет вывести на экран имена тех друзей и знакомых, которые живут в Австралии (чтобы потом выбрать, у кого остановиться). Напишите программу, которая выводит на экран полные имена питонов из Австралии.

Ожидаемый вывод на экран:

зеленый питон Оливер
аметистовый питон Харрисон
оливковый питон Руби
In [ ]:
def get_names():
    
    pythons = {"королевский питон Рональд" : "Уганда", 
          "карликовый питон Антониа" : "Ангола", 
          "сетчатый питон Асиф" : "Бангладеш", 
          "зеленый питон Оливер" : "Австралия", 
          "тиморский питон Николау" : "Восточный Тимор", 
          "аметистовый питон Харрисон" : "Австралия", 
          "тигровый питон Мэйли" : "Китай",
          "оливковый питон Руби" : "Австралия", 
          "белогубый питон Арис" : "Индонезия", 
          "эфиопский питон Оника" : "Мозамбик"}
    
    "# YOUR CODE HERE"

get_names()
In [ ]:
test_data = [("", 
          ["зеленый питон Оливер\n","аметистовый питон Харрисон\n","оливковый питон Руби\n"])]

for inp, out in test_data:
    with Tester([inp]) as t:
        get_names()
        assert sorted(out) == sorted(t), "Неверный ответ, проверьте соответствие Вашего вывода и ожидаемого вывода."

Задача 3 (1 балл)

Дан словарь с записями, ключами которых являются числовые id, а значениями – города.

cities = {100: 'Москва', 101: 'Санкт-Петербург', 
          103: 'Екатеринбург', 104: 'Пермь', 
          107: 'Красноярск', 111 : 'Киров'}

Напишите программу, которая просит пользователя ввести с клавиатуры сначала ключ, потом значение, а затем:

  • если в словаре cities есть запись с таким ключом, на экран выводится сообщение "Запись с таким ключом существует. Введите другой ключ.";

  • если в словаре cities нет записи с таким ключом, в этот словарь добавляется запись с введенным ключом и значением + обновленный словарь cities выводится на экран.

Примеры

Входные данные:

Введите ключ: 101
Введите значение: Мурманск

Выходные данные:

Запись с таким ключом существует. Введите другой ключ.

Входные данные:

Введите ключ: 117
Введите значение: Омск

Выходные данные (порядок записей может быть другим):

{100: 'Москва', 101: 'Санкт-Петербург', 
103: 'Екатеринбург', 104: 'Пермь', 
107: 'Красноярск', 111 : 'Киров', 117 : 'Омск'}

Обратите внимание, id должны быть целочисленными!

In [ ]:
def add_city():
    
    cities = {100: 'Москва', 101: 'Санкт-Петербург', 103: 'Екатеринбург', 104: 'Пермь', 
              107: 'Красноярск', 111 : 'Киров'}

    "# YOUR CODE HERE"
add_city()
In [ ]:
from ast import literal_eval
test_data = [("101 Мурманск", 
          "Запись с таким ключом существует. Введите другой ключ."),
            ("117 Омск", {100: 'Москва', 101: 'Санкт-Петербург', 103: 'Екатеринбург', 104: 'Пермь', 
            107: 'Красноярск', 117: 'Омск', 111 : 'Киров'})]

for inp, out in test_data:
    with Tester(inp.split()) as t:
        add_city()
        if type(out) is dict:
            assert literal_eval(t[0]) == out, "Неверный ответ, был введен ключ %i, значение %s." % (int(inp.split()[0]), inp.split()[1])
        else:
            assert t[0] == out+'\n', "Неверный ответ, был введен ключ %i, значение %s." % (int(inp.split()[0]), inp.split()[1])

Задача 4 (1 балл)

Напишите программу, которая сначала запрашивает у пользователя имена студентов через проблел, затем их оценки через пробел и создает и выводит на экран словарь, где ключами являются имена, а значениями – оценки.

Пример

Входные данные:

Введите имена студентов: Анна Николай
Введите оценки студентов: 8 7

Выходные данные (порядок записей может отличаться):

{'Анна' : 8, 'Николай' : 7}
In [ ]:
def gradebook():
    
    "# YOUR CODE HERE"
    
gradebook()
In [ ]:
from ast import literal_eval
test_data = [("Анна Дмитрий,7 5", {'Дмитрий': 5, 'Анна': 7}),
            ("Николай Иван,10 8", {'Иван': 8, 'Николай': 10}),
            ("Мария Степан Виктор,6 5 7", {'Мария': 6, 'Степан': 5, 'Виктор': 7})]

for inp, out in test_data:
    with Tester([inp.split(",")][0] + [int(i) for i in inp.split(",")[1].split()]) as t:
        gradebook()
        assert literal_eval(t[0].strip()) == out, "Неверный ответ, были введены ключи " + inp.split(",")[0] 

Задача 5 (1 балл)

Дан словарь results:

results = {"Best score": 25, "Champion" : "Alex"}

Напишите программу, которая запрашивает у пользователя его имя и число очков (в одну строчку через пробел), и

  • если число очков больше, чем текущее число очков в словаре, то она меняет значение "Best score" в results на то, которое указал пользователь, и значение "Champion" в results на имя пользователя;

  • если число очков не больше, чем текущее число очков в словаре, то программа ничего со словарем results не делает.

После чего, вне зависимости от того, был ли словарь изменен или нет, на экран выводится results. Считайте, что число очков может быть только целым.

Пример

Входные данные:

Enter your name and score: Alice 27

Выходные данные (порядок записей может отличаться):

{"Best score": 27, "Champion" : "Alice"}

Входные данные:

Enter your name and score: Anton 23

Выходные данные (порядок записей может отличаться):

{"Best score": 25, "Champion" : "Alex"}
In [ ]:
def champ():
    results = {"Best score": 25, "Champion" : "Alex"}
    
    "# YOUR CODE HERE"
    
champ()
In [ ]:
from ast import literal_eval
test_data = [("Alice 27", {'Champion': 'Alice', 'Best score': 27}), 
             ("Peter 21", {'Champion': 'Alex', 'Best score': 25}), 
             ("Mark 25", {'Champion': 'Alex', 'Best score': 25}), 
             ("Ann 18", {'Champion': 'Alex', 'Best score': 25}),
             ("Sam 32", {'Champion': 'Sam', 'Best score': 32})]

for inp, out in test_data:
    with Tester([inp]) as t:
        champ()
        assert literal_eval(t[0].strip()) == out, "Неверный ответ, было введено имя %s и число очков %i" %(inp.split()[0], inp.split()[1])

Задача 6 (2 балла)

Дан словарь, содержащий информацию по станциям метро в Новосибирске (станции в списке указаны последовательно, с севера на юг в случае Ленинской ветки, и с запада на восток в случае Дзержинской ветки).

nmetro = {"Ленинская" : ["Заельцовская", "Гагаринская", "Красный проспект",                               "Площадь Ленина", "Октябрьская","Речной Вокзал",                               "Студенческая", "Площадь Маркса"], 
"Дзержинская" : ["Площадь Гарина-Михайловского",
                        "Сибирская","Маршала Покрышкина",
                        "Березовая Роща", "Золотая Нива"]}

Напишите программу, которая запрашивает у пользователя сначала название текущей станции без пробела на конце, затем направление движения ('1' – с севера на юг или с запада на восток, '2' – с юга на север или с востока на запад) и выводит на экран название следующей станции.

Считайте, что пользоватль знает, какие станции являются конечными, и не будет запрашивать станцию, следующую после конечной.

Пример

Входные данные:

Введите название текущей станции: Гагаринская
Выберите направление движения (1 - с севера на юг или с запада на восток, 2 - с юга на север или с востока на запад): 2

Выходные данные:

Следующая станция: Заельцовская


Входные данные:

Введите название текущей станции: Сибирская
Выберите направление движения (1 - с севера на юг или с запада на восток, 2 - с юга на север или с востока на запад): 1

Выходные данные:

Следующая станция: Маршала Покрышкина
In [ ]:
def metro():
    nmetro = {"Ленинская" : ["Заельцовская", "Гагаринская", "Красный проспект", "Площадь Ленина", "Октябрьская",
                   "Речной Вокзал", "Студенческая", "Площадь Маркса"], 
              "Дзержинская" : ["Площадь Гарина-Михайловского", "Сибирская", "Маршала Покрышкина", "Березовая Роща",
              "Золотая Нива"]}

    "# YOUR CODE HERE"
    
metro()
In [ ]:
test_data = [("Гагаринская,2", "Следующая станция: Заельцовская"), 
             ("Сибирская,1", "Следующая станция: Маршала Покрышкина"),
             ("Студенческая,1", "Следующая станция: Площадь Маркса"),
             ("Березовая Роща,2", "Следующая станция: Маршала Покрышкина"),
             ("Золотая Нива,2", "Следующая станция: Березовая Роща"),
             ("Площадь Гарина-Михайловского,1", "Следующая станция: Сибирская")]

for inp, out in test_data:
    
    with Tester(inp.split(",")) as t:
        metro()
        assert t[0] == out+'\n', "Неверный ответ. Было введено название %s, направление %s"%(inp.split(",")[0], inp.split(",")[1])

Часть 2

Решения этих задач можно добавить в этот ipynb-файл и загрузить в систему, а можно отправить отдельным файлом (.ipynb или .py) на почту [email protected], это наш грейдер Анастасия.

Задача 1

Бизнес-центр представляет собой N-этажное здание, этажи пронумерованы от 1 до N снизу вверх. На каждом этаже работает ровно один сотрудник. Все сотрудники утром приезжают на парковку, которая расположена в подвальном помещении на 1 этаж ниже первого. Бизнес-центр оборудован лифтом, который вмещает неограниченное число людей, но лифтёр готов отвезти всех сотрудников только на один какой-то этаж.

У каждого сотрудника есть выбор: он может пойти вверх пешком по лестнице, на подъём на один этаж при этом будет уходить A секунд. Либо он может сесть в лифт, который отвезёт всех сотрудников на какой-то выбранный ими вместе этаж. Выйдя из лифта, сотрудник может подняться до своего этажа (также тратя A секунд на подъём на один этаж), либо спуститься до нужного этажа вниз, тратя B секунд на спуск на один этаж. Лифт тратит C секунд на подъём на один этаж.

Определите минимальное время, за которое все сотрудники разойдутся по своим этажам, если они наилучшим образом выберут этаж, на который едет лифт, и свою стратегию поведения (подниматься по лестнице, или ехать на лифте, а затем идти по лестнице).

Формат ввода. Первая строка входных данных содержит число 𝑁 — количество этажей в бизнесцентре. Следующие три строки содержат числа 𝐴,𝐵,𝐶 — время, необходимое сотруднику на подъем на один этаж, на спуск на один этаж и время, необходимое лифту на подъём на один этаж. Все числа положительные, при этом 𝐴 ≥ 𝐵, 𝐴 ≥ 𝐶.

Формат вывода. Программа должна вывести единственное целое число — минимальное время, за которое все сотрудники могут добраться до своего этажа.

Задача 2

Вдоль прямой выложены три спички. Необходимо переложить одну из них так, чтобы при поджигании любой спички сгорали все три. Для того чтобы огонь переходил с одной спички на другую, необходимо, чтобы эти спички соприкасались (хотя бы концами). Требуется написать программу, определяющую, какую из трех спичек необходимо переместить.

Формат ввода. Вводятся шесть целых чисел через пробел: $𝑙_1$,$𝑟_1$,$𝑙_2$,$𝑟_2$,$𝑙_3$,$𝑟_3$ –– координаты первой, второй и третьей спичек соответственно, $0 \leqslant 𝑙_𝑖 < 𝑟_𝑖 \leqslant 100$. Каждая спичка описывается координатами левого и правого концов по горизонтальной оси $Ox$.

Формат вывода. Выведите номер искомой спички. Если возможных ответов несколько, то выведите наименьший из них. В случае, когда нет необходимости перемещать какую-либо спичку, выведите $0$. Если же требуемого результата достигнуть невозможно, то выведите $-1$.

Задача 3

На склад, который имеет форму прямоугольного параллелепипеда, привезли ноутбуки, упакованные в коробки. Каждая коробка также имеет форму прямоугольного параллелепипеда. По правилам хранения, коробки с ноутбуками должны быть размещены на складе с выполнением следующих двух условий:

  1. Стороны коробок должны быть параллельны сторонам склада.
  2. Коробку при помещении на склад разрешается расположить где угодно (с учётом выполнением предыдущего условия), в том числе на другой коробке, но все коробки должны быть ориентированы одинаково (т.е. нельзя одну коробку расположить «стоя», а другую —«лёжа»).

Напишите программу, которая по размерам склада и размерам коробки с ноутбуком определит максимальное количество ноутбуков, которое может быть размещено на складе.

Формат ввода. Программа получает на вход шесть натуральных чисел. Первые три задают длину, высоту и ширину склада. Следующие три задают соответственно длину, высоту и ширину коробки с ноутбуком.

Формат вывода. Программа должна вывести одно число — максимальное количество ноутбуков, которое может быть размещено на складе.