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

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

Некоторые задачи заимствованы из домашнего задания по курсу «Введение в программирование» (Магистерская программа «Журналистика данных», НИУ ВШЭ, 2017-18, И.В.Щуров).

Для того чтобы получить за это домашнее задание оценку 10, необходимо набрать не менее 13 баллов.

Формат сдачи

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

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

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

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

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

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

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 балл)

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

[Имя]... какое хорошее имя!

Пример

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

Анна

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

Анна... какое хорошее имя!
In [ ]:
def good_name():
    
    "# YOUR CODE HERE"

good_name()
In [ ]:
test_data = [
    ("Алла", "Алла... какое хорошее имя!"),
    ("Петя", "Петя... какое хорошее имя!"),
    ("Мэри", "Мэри... какое хорошее имя!"),
    ("Саша", "Саша... какое хорошее имя!")
]

for inp, out in test_data:
    with Tester([inp]) as t:
        good_name()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0] == out+"\n", "Неверный ответ, была введена строка " + inp

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

В среднем за неделю Питон получает пять сообщений от Анаконды ($\lambda = 5$). Пользователь с клавиатуры вводит число сообщений, которые Анаконда может прислать Питону ($k$). Напишите программу, которая выводит на экран вероятность, с которой Питон получит сообщений от Анаконды за неделю, с точностью до трёх знаков после запятой. Сообщение, выводимое на экран, должно быть такого вида:

Число сообщений от Анаконды за неделю равно [k], вероятность равна [p].

Вероятность того, что Питон получит ровно сообщений, определяется следующим образом (распределение Пуассона):

$$ \text{P}(X = k) = e^{-\lambda} \frac{\lambda^k}{k!}, $$

В задаче можно использовать функцию factorial() из модуля math.

Пример

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

2

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

Число сообщений от Анаконды за неделю равно 2, вероятность равна 0.084.
In [ ]:
def pois():
    
    "# YOUR CODE HERE"
pois()    
In [ ]:
test_data = [
    ("0", ["Число сообщений от Анаконды за неделю равно 0, вероятность равна 0.007.\n"]),
    ("1", ["Число сообщений от Анаконды за неделю равно 1, вероятность равна 0.034.\n"]),
    ("2", ["Число сообщений от Анаконды за неделю равно 2, вероятность равна 0.084.\n"]),
    ("5", ["Число сообщений от Анаконды за неделю равно 5, вероятность равна 0.175.\n"]),
    ("7", ["Число сообщений от Анаконды за неделю равно 7, вероятность равна 0.104.\n"])
]

for inp, out in test_data:
    with Tester([inp]) as t:
        pois()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t == out,"Неверный ответ, было введено число " + inp

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

Дан список станций желтой ветки московского метро (упорядочены как на карте):

line = ["Третьяковская", "Марксистская", "Площадь Ильича", "Авиамоторная", "Шоссе Энтузиастов", "Перово", "Новогиреево", "Новокосино"]

Давайте считать, что движение происходит от Третьяковской до Новокосино.

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

Следующая станция: [станция].

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

Пример

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

Площадь Ильича

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

Следующая станция: Авиамоторная.
In [ ]:
def metro():
    line = ["Третьяковская", "Марксистская", "Площадь Ильича", "Авиамоторная", "Шоссе Энтузиастов",
    "Перово", "Новогиреево", "Новокосино"]
    
    "# YOUR CODE HERE"

metro()
In [ ]:
test_data = [
    ("Третьяковская", "Следующая станция: Марксистская."),
    ("Марксистская", "Следующая станция: Площадь Ильича."),
    ("Площадь Ильича", "Следующая станция: Авиамоторная."),
    ("Авиамоторная", "Следующая станция: Шоссе Энтузиастов."),
    ("Шоссе Энтузиастов","Следующая станция: Перово."),
    ("Перово", "Следующая станция: Новогиреево."),
    ("Новогиреево", "Следующая станция: Новокосино.")
]

for inp, out in test_data:
    with Tester([inp]) as t:
        metro()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0] == out+"\n", "Неверный ответ, была введена строка " + inp

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

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

Пример

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

3
9
2
5

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

90
In [ ]:
def prod_n():
    
    "# YOUR CODE HERE"
    
prod_n()
In [ ]:
test_data = [
    ("3 9 2 5", 90),
    ("1 1", 1),
    ("2 0 10", 0),
    ("2 10 0", 0),
    ("10 1 2 3 4 5 6 7 8 9 10", 3628800),
    ("5 -1 -1 -1 -1 -1", -1)
]

for inp, out in test_data:
    with Tester(inp.split()) as t:
        prod_n()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == str(out), "Неверный ответ, были введены числа " + inp
print("Ура, всё верно!")

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

Факториалом $n!$ натурального числа $n$ называется произведение всех натуральных чисел от 1 до $n$ включительно. Напишите программу, которая запрашивает натуральное число $n$ с клавиатуры, вычисляет и выводит на экран $n!$. Использовать какие-либо библиотечные функции нельзя (то есть запрещена конструкция import).

Пример

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

5

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

120
In [ ]:
def my_fact():
    
    "# YOUR CODE HERE"

my_fact()
In [ ]:
test_data = [
    ("1", 1),
    ("2", 2),
    ("3", 6),
    ("4", 24),
    ("5", 120),
    ("10", 3628800),
    ("20", 2432902008176640000)
]

for inp, out in test_data:
    with Tester([inp]) as t:
        my_fact()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == str(out), "Неверный ответ, были введены числа " + inp
print("Хорошо, давайте теперь что-нибудь посложнее решим!")

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

Числа Фибоначчи — это последовательность чисел, заданная следующим образом:

$$ a_1 = 1,\text{ }a_2 = 1,\text{ }a_{k+1} = a_{k} + a_{k-1} $$

Напишите программу, которая запрашивает число $k$ с клавиатуры и выводит на экран $a_k$. Считайте, что число $k$ всегда больше одного, то есть на вход никогда не будет подано число, меньшее или равное 1.

Пример

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

3

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

2

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

5

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

5

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

6

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

8
In [ ]:
def fib_k():
    
    "# YOUR CODE HERE"
    
fib_k()
In [ ]:
test_data = [(33, 3524578),
 (87, 679891637638612258),
 (66, 27777890035288),
 (43, 433494437),
 (44, 701408733),
 (19, 4181),
 (98, 135301852344706746049),
 (65, 17167680177565),
 (32, 2178309),
 (11, 89)]
for inp, out in test_data:
    with Tester([str(inp)]) as t:
        fib_k()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == str(out), ("Неверный ответ, было "
                                          "введено число " + str(inp) + 
                                         " ожидалось " + str(out))
print("Ура, всё верно!")

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

$X$ – дискретная случайная величина, которая может принимать как целые, так и дробные значения.

Напишите программу, которая запрашивает с клавиатуры значения дискретной случайной величины $X$ через пробел (количество значений может быть любым), затем – вероятности, соответствующие этим значениям, тоже через пробел, вычисляет математическое ожидание и дисперсию $X$ и выводит их на экран с точностью до второго знака после запятой в виде сообщений такого вида:

E(X) = [математическое ожидание]; D(X) = [дисперсия]

Считайте, что пользователь не ошибается при вводе, то есть количество вероятностей всегда совпадает с количеством значений, при этом вероятности всегда лежат в пределах от 0 до 1 и записываются в виде десятичных дробей.

Напоминание:

$$ E(X) = \sum_{i=1}^nx_i p_i $$$$ D(X) = E(X^2) - [E(X)]^2 $$

Пример

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

2 5 8
0.1 0.5 0.4

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

E(X) = 5.90; D(X) = 3.69
In [ ]:
def discrete():
    
    "# YOUR CODE HERE"
    
discrete()
In [ ]:
test_data = [
    ("2 5 8;0.1 0.5 0.4", "E(X) = 5.90; D(X) = 3.69"),
    ("-1 0 1;0.2 0.3 0.5", "E(X) = 0.30; D(X) = 0.61"),
    ("9 10 20;0.15 0.25 0.6", "E(X) = 15.85; D(X) = 25.93"),
    ("-8 -5 -1;0.9 0.05 0.05", "E(X) = -7.50; D(X) = 2.65")
]

for inp, out in test_data:
    with Tester(inp.split(";")) as t:
        discrete()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == str(out), "Неверный ответ, были введены числа " + inp
print("Отлично!")

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

Напишите программу, которая запрашивает с клавиатуры целое положительное четырёхзначное число, сохраняет его в числовом формате и выводит на экран его представление в следующем виде:

тысячи * 1000 + сотни * 100 + десятки * 10 + единицы

Пример

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

1234

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

1 * 1000 + 2 * 100 + 3 * 10 + 4

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

9000

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

9 * 1000 + 0 * 100 + 0 * 10 + 0

Важно: «сохраняет в числовом виде» – обязательное условие, нельзя просто извлечь первый/второй/третий/четвёртый символ строки и подставить в текстовый шаблон, нужно работать с числом! Иначе всё слишком просто!

In [ ]:
def split_number():
    
    "# YOUR CODE HERE"
    
split_number()
In [ ]:
test_data = [
    ("1234", "1 * 1000 + 2 * 100 + 3 * 10 + 4"),
    ("9000", "9 * 1000 + 0 * 100 + 0 * 10 + 0"),
    ("3999", "3 * 1000 + 9 * 100 + 9 * 10 + 9"),
    ("4005", "4 * 1000 + 0 * 100 + 0 * 10 + 5"),
    ("5555", "5 * 1000 + 5 * 100 + 5 * 10 + 5")
]

for inp, out in test_data:
    with Tester([inp]) as t:
        split_number()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == str(out), "Неверный ответ, были введены числа " + inp

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

Напишите программу, которая угадывает, какая цифра была скрыта за «звёздочкой», если известно, что пользователь вводит с клавиатуры сначала одно двузначное число со «звёздочкой» на конце, затем – второе двузначное число со «звёздочкой» в начале, а затем их сумму. Считайте, что сумма чисел тоже является двузначным числом.

Примеры

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

2*
*1
54

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

* = 3

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

1*
*3
90

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

* = 7

Для решения этой задачи нельзя использовать конструкцию if-else.

In [ ]:
def guess():
    
    "# YOUR CODE HERE"
guess()
In [ ]:
test_data = [
    ("1* *2 45", "* = 3"),
    ("1* *3 90", "* = 7"),
    ("2* *1 54", "* = 3"),
    ("8* *2 93", "* = 1"),
    ("4* *4 88", "* = 4"),
    ("1* *8 40", "* = 2")
]

for inp, out in test_data:
    with Tester(inp.split()) as t:
        guess()
        assert len(t) == 1, "Вам нужно вывести ровно одну строку с ответом"
        assert t[0].strip() == str(out), "Неверный ответ, были введены числа " + inp

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

Введите число $n$ с клавиатуры. Выведите таблицу умножения от 1 до $n$ включительно, как показано в примере.

Пример

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

5

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

1 2 3 4 5 
2 4 6 8 10 
3 6 9 12 15 
4 8 12 16 20 
5 10 15 20 25 
In [ ]:
def mult_table():
    
    "# YOUR CODE HERE"
    
mult_table()
In [ ]:
test_data = [(1, ['1 \n']),
 (4, ['1 2 3 4 \n', '2 4 6 8 \n', '3 6 9 12 \n', '4 8 12 16 \n']),
 (7,
  ['1 2 3 4 5 6 7 \n',
   '2 4 6 8 10 12 14 \n',
   '3 6 9 12 15 18 21 \n',
   '4 8 12 16 20 24 28 \n',
   '5 10 15 20 25 30 35 \n',
   '6 12 18 24 30 36 42 \n',
   '7 14 21 28 35 42 49 \n']),
 (10,
  ['1 2 3 4 5 6 7 8 9 10 \n',
   '2 4 6 8 10 12 14 16 18 20 \n',
   '3 6 9 12 15 18 21 24 27 30 \n',
   '4 8 12 16 20 24 28 32 36 40 \n',
   '5 10 15 20 25 30 35 40 45 50 \n',
   '6 12 18 24 30 36 42 48 54 60 \n',
   '7 14 21 28 35 42 49 56 63 70 \n',
   '8 16 24 32 40 48 56 64 72 80 \n',
   '9 18 27 36 45 54 63 72 81 90 \n',
   '10 20 30 40 50 60 70 80 90 100 \n']),
 (13,
  ['1 2 3 4 5 6 7 8 9 10 11 12 13 \n',
   '2 4 6 8 10 12 14 16 18 20 22 24 26 \n',
   '3 6 9 12 15 18 21 24 27 30 33 36 39 \n',
   '4 8 12 16 20 24 28 32 36 40 44 48 52 \n',
   '5 10 15 20 25 30 35 40 45 50 55 60 65 \n',
   '6 12 18 24 30 36 42 48 54 60 66 72 78 \n',
   '7 14 21 28 35 42 49 56 63 70 77 84 91 \n',
   '8 16 24 32 40 48 56 64 72 80 88 96 104 \n',
   '9 18 27 36 45 54 63 72 81 90 99 108 117 \n',
   '10 20 30 40 50 60 70 80 90 100 110 120 130 \n',
   '11 22 33 44 55 66 77 88 99 110 121 132 143 \n',
   '12 24 36 48 60 72 84 96 108 120 132 144 156 \n',
   '13 26 39 52 65 78 91 104 117 130 143 156 169 \n'])]

for inp, out in test_data:
    with Tester([str(inp)]) as t:
        mult_table()
        line_t = "".join(t)
        t = line_t.splitlines()
        assert len(t) == len(out), "Неверное количество строк в выводе"
        for l_test, l_out in zip(t, out):
            assert len(l_test.split()) == len(l_out.split()), \
                "Неверное количество элементов в строке " + l_out
            for el_test, el_out in zip(l_test.split(), l_out.split()):
                assert el_test == el_out, "Ошибка {} != {}".format(l_test, 
                                                                   l_out)
print("Теперь вы можете печатать тетрадки для школьников!")