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

Курс повышения квалификации

И. В. Щуров, НИУ ВШЭ

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

Максимум баллов: 8.

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

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

С помощью API World Bank, напишите функцию get_capital(country_code), принимающую на вход ISO 3166-1 код государства и возвращающую название его столицы (в том виде, в котором оно возвращается этим API). Если вы хотите, чтобы API использовало формат JSON, укажите словарь {'format':'json'} в качестве второго аргумента requests.get. (Вы можете использовать как JSON, так и XML-интерфейс, на ваш выбор.) Обратите внимание: код страны здесь надо передавать как часть URL.

In [ ]:
# YOUR CODE HERE

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

Написать функцию diff_lat(place1, place2), которая бы с помощью геокодера Яндекса находила координаты двух объектов, заданных строками place1 и place2, и возвращала бы число с плавающей точкой, являющееся ответом на вопрос: на сколько градусов place2 севернее, по сравнению с place1?

In [ ]:
# YOUR CODE HERE
In [ ]:
assert abs(diff_lat("Москва", "Апатиты") - 11.81) < 0.1
assert abs(diff_lat("Шаболовка, 26", "Кочновский, 3")-0.086) < 0.001
assert abs(diff_lat("Краснодар", "Петропавловск-Камчатский") - 8) < 0.1
assert abs(diff_lat("Геленджик", "Саратов") - 7) < 0.1
assert abs(diff_lat("Саратов", "Геленджик") + 7) < 0.1

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

На сайте http://dronestre.am собираются данные об ударах дронов США. У него есть простое API, позволяющее получить информацию о каждом ударе в виде JSON-файла. Адрес для API: http://api.dronestre.am/data Написать функцию strikes_per_country(year), принимающую на вход год в виде целого числа (например, 2015) и возвращающую словарь, ключами которого являются страны, а значениями — число ударов в этой стране.

Подсказка 1. API не принимает параметр year (и вообще никаких параметров не принимает). Вам придётся скачать все данные и вытащить из них только те записи, которые вам нужны. Дата в данных указана в виде строки в стандартном формате. Можно проверить, что строка начинается с некоторого префикса с помощью .startswith. Например, "Hello".startswith("He") возвращает True.

Подсказка 2. Попробуйте использовать collections.Counter (from collections import Counter). Почитайте в документации, что он делает.

Подсказка 3. Для ускорения работы функции вы можете записать код, который будет запрашивать информацию с сайта, до описания функции:

# (make request)
# json_data = (some lines to get data)
def strikes_per_country(year):
    # query json_data for data you need
In [ ]:
# YOUR CODE HERE
In [ ]:
assert strikes_per_country(2002) == {'Yemen': 1}
assert strikes_per_country(2009) == {'Pakistan': 56}
assert strikes_per_country(2016) == {'Pakistan': 3, 'Somalia': 5, 'Yemen': 23}

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

С помощью API Google Books можно получать информацию о различных книгах. Например, вот так можно получить данные по книге по её ISBN: https://www.googleapis.com/books/v1/volumes?q=isbn:9785699648146. Напишите функцию book_table(isbns), принимающую на вход список ISBN'ов и возвращающую таблицу pandas, содержащую заглавие, авторов, язык и число страниц. Названия колонок должны соответствовать названиям полей в ответе API. Если авторов несколько, они должны быть разделены запятой и пробелом. Пример см. в тесте.

In [ ]:
# YOUR CODE HERE
In [ ]:
obtained = book_table(['9781292153964', '9780262035613', '9785457499850'])
expected = pd.DataFrame({'authors': {0: 'Stuart Russell, Peter Norvig',
  1: 'Ian Goodfellow, Yoshua Bengio, Aaron Courville',
  2: 'Рэй Брэдбери'},
 'language': {0: 'en', 1: 'en', 2: 'ru'},
 'pageCount': {0: 1152, 1: 800, 2: 499},
 'title': {0: 'Artificial Intelligence',
  1: 'Deep Learning',
  2: 'Вино из одуванчиков'}})
assert obtained.to_dict() == expected.to_dict()

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

Написать функцию float_or_nan, которая преобразует вещественное число, записанное в строке, в формат float. При этом если в строке записано что-то, что не может быть преобразовано во float, должно быть возвращено специальное значение float("nan").

Подсказка: вам предстоит использовать конструкцию try/except для решения этой задачи.

In [ ]:
# YOUR CODE HERE
In [ ]:
import numpy as np
assert np.isnan(float_or_nan("12.34.5"))
assert np.isnan(float_or_nan("+-12.34"))
assert np.isnan(float_or_nan("q"))
assert float_or_nan("+12.34") == 12.34
assert float_or_nan("-12.34") == -12.34
assert float_or_nan("12.34e1") == 123.4
assert float_or_nan("-12.34e+1") == -123.4
assert float_or_nan("-12.e+1") == -120
assert float_or_nan("-0.e+1") == 0
assert float_or_nan("-.0e+1") == 0

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

Написать функцию find(some_list, element), которая возвращает индекс первого элемента в списке some_list, равного element. Если такого элемента нет, должно быть возвращено -1. Использовать циклы запрещено.

Подсказка. У списков есть метод index, который делает ровно то, что нужно, за одним исключением: если элемент не найден, происходит исключение (exception) ValueError. Здесь и в следующих двух задачах вам необходимо использовать конструкцию try/except, чтобы обработать исключение. Подробнее про исключения можно прочитать здесь.

In [ ]:
# YOUR CODE HERE
In [ ]:
assert find([1, 2, 3], 4) == -1
assert find([1, 2, 3, 4], 3) == 2
assert find(['Hello', 1, 2, 'World'], 'World') == 3
assert find([True, False], 0) == 1
assert find([['a'], ['b', 'c'], 'd'], 'd') == 2
assert find([['a'], ['b', 'c'], 'd'], []) == -1
assert find([['a'], ['b', 'c'], 'd'], ['b', 'c']) == 1

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

Напишите функцию power_or_nan(a, b), которая возводит вещественное положительное число a в степень, заданную вещественным положителным числом b, и возвращает результат. Если числа оказались слишком большими и в процессе вычисления произошло переполнение, функция должна вернуть специальное значение float("NaN").

Примечание. Python 3 может работать со сколь угодно большими целыми числами (в принципе помещающимися в память), но для чисел с плавающей точкой существует ограничение: слишком большие числа просто невозможно записать. Если вы работаете с целыми числами и в какой-то момент эти числа становятся очень-очень большими, то компьютер будет очень долго думать: например, попробуйте вычислить 9 ** (9 ** 9) (прекратить вычисление можно с помощью Kernel → Interrupt). Если аналогичное вычисление выполнить с числами с плавающей точкой, возникнет переполнение (попробуйте). В данной задаче требуется работать только с числами с плавающей точкой. Если вы хотите записать целое число как число с плавающей точкой, это можно сделать, поставив точку: например, type(9) — это int, а type(9.) — это float. Чтобы преобразовать переменную к типа float нужно использовать одноимённую функцию (например: y = float(x)).

In [ ]:
# YOUR CODE HERE
In [ ]:
from math import pi, e, isnan
from sys import float_info
from numpy import isclose

max_float = float_info.max

assert power_or_nan(2., 3.) == 8.
assert isclose(power_or_nan(pi, e), 22.45915771836104)
assert power_or_nan(max_float, 0.99) == max_float ** 0.99
assert isnan(power_or_nan(max_float, 1.01))

assert(isnan(power_or_nan(9., 9. ** 9)))

if isclose(max_float, 1.7976931348623157e+308):
    assert isclose(power_or_nan(1.00000001, (9. ** 9)), 48.144400906189524)
    assert isclose(power_or_nan(1.0000001, (9. ** 9)), 6.690479178194533e+16)
    assert isnan(power_or_nan(1.00001, (9. ** 9)))
In [ ]:
ok = False
try:
    power_or_nan('a', 'b')
    ok = False
except (ValueError, TypeError):
    ok = True
assert ok, ("Вам нужно обрабатывать только исключение, "
               "связанное с переполнением, другие исключения"
               "обрабатывать не нужно")