Статистика и открытые данные

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

Авторы задач в подборке: И. Щуров, Ф. Тюрин

На странице курса находятся другие материалы.

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

За разные задачи можно получить разное число баллов. Если не указано обратное, задача весит 1 балл. Максимум за ДЗ можно набрать 10 баллов. Вы можете решить больше задач, чем требуется, чтобы дополнительно потренироваться.

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

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

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

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

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

Написать функцию any_news_about_harry(url), принимающую на вход адрес веб-страницы url, загружающую эту веб-страницу и проверяющую, встречается ли в ней слово Harry (с большой буквы). Функция должна возвращать True, если встречается, и False в противном случае. Также функция должна возвращать False, если страницу не удалось открыть (например, была получена ошибка 404 Not Found.)

Подсказка. Чтобы загрузить страницу, нужно использовать библиотеку requests:

import requests
r = requests.get(url)

Содержимое страницы затем окажется в r.text. Проверить, что запрос увенчался успехом, можно так:

if r:
    # увенчался успехом

Если вы пытаетесь обратиться к несуществующему сайту, произойдёт исключение requests.RequestException. Его нужно обработать с помощью try/except. Подробнее про исключения можно прочитать здесь.

В этой задаче не нужно использовать BeautifulSoup.

In [ ]:
# YOUR CODE HERE
In [ ]:
assert any_news_about_harry("https://en.wikipedia.org/w/index.php?title=J._K._Rowling&oldid=694008857")
assert any_news_about_harry("https://en.wikipedia.org/w/index.php?title=Star_Wars&oldid=694701430")
assert not any_news_about_harry("https://en.wikipedia.org/w/index.php?title=Darth_Vader&oldid=694617684")
assert not any_news_about_harry("http://math-info.hse.ru/there_is_no_Harry_here")
assert not any_news_about_harry("http://nonexistent.domain.ji8bohVe/")

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

Написать функцию get_strong(html), принимающую на вход html-страницу в виде длинной строки, записанной в переменную html, и возвращающую строчку, содержащуюся в первом теге strong.

Примеры см. в тестах.

Подсказка. Вы можете создать объект BeautifulSoup, передав ему строку с html в качестве параметра. Например:

from bs4 import BeautifulSoup
page = BeautifulSoup("<html><body><p>Hello</p></body></html>", "html.parser")
print(page.p)
In [ ]:
# YOUR CODE HERE
In [ ]:
assert get_strong("<html><body><p>Hello, <strong>World</strong>!") == "World"
html = """<html>
    <body>
        <p>
            Hello,
            <strong>
                World
            </strong>
        </p>
    </body>
</html>"""
assert get_strong(html).strip() == "World"
assert get_strong("<html><body><p>tag &lt;strong&gt; is used in HTML\n to make letters <strong>stronger</strong>") == "stronger"
assert get_strong("<html><body><strong>One\nTwo</strong></body></html>") == "One\nTwo"

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

Для вставки картинок в HTML используется тег <img>, содержащий атрибут src — адрес файла с картинкой. Например, <img src="https://example.com/figure.jpg"/>. Здесь значением атрибута src является строка "https://example.com/figure.jpg". Написать функцию all_images_src(html), принимающую на вход длинную строчку с HTML-документом, а возвращающую список адресов всех картинок, встречающихся в этом документе (в том порядке, в котором они встречаются в документе). Некоторые теги <img> могут не содержать атрибута src — их надо пропустить.

Подсказка. Для обращения к атрибутам тега нужно использовать квадратные скобки, как если бы тег был словарём. Например, для тега <a href="http://example.com">example site</a>

a['href']

вернёт

'http://example.com'.

Однако, у тега может не оказаться запрашиваемого атрибута, и тогда код вернет ошибку KeyError. У тега, так же, как у словаря, есть метод get(), который возвращает None, если запрашиваемого атриюута нет (или возвращает значение по умолчанию, определенное пользователем и переданное как второй аргумент):

a.get('QQQ')

вернет None.

In [ ]:
# YOUR CODE HERE
In [ ]:
assert all_images_src('<html><body><img src="https://upload.wikimedia.org/wikipedia/commons/b/bd/Struthio_camelus_portrait_Whipsnade_Zoo.jpg"/>') == ["https://upload.wikimedia.org/wikipedia/commons/b/bd/Struthio_camelus_portrait_Whipsnade_Zoo.jpg"]
assert all_images_src( ('<html><body><IMG src="test.jpg">\n'
                        '<p>Some text\n'
                        '<img SRC=\'well.png\'><img>\n'
                        '</p></body></html>') ) == ["test.jpg", "well.png"]
assert all_images_src('<html><body><p><a href="link.html">'
                      '<img alt="Just a test image" src="this is a test.jpg"><ul>' + "\n"
                      .join("<li><img src='img%04i.png'></li>" % i for i in range(1000)) + 
                      "</ul></p></body></html>"
                     ) == ['this is a test.jpg'] + ['img%04i.png' % i for i in range(1000)]

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

Написать функцию get_all_headings(url), принимающую на вход адрес страницы в Википедии и возвращающую список, состоящий из названий разделов статьи (в порядке появления в статье). Если такой страницы не существует, функция должна вернуть список, состоящей из строки "Not found".

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

Примечание. Не на всех страницах Википедии есть оглавление. В частности, ваш код должен коорректно обрабатывать эту страницу.

In [ ]:
# YOUR CODE HERE
In [ ]:
from urllib.parse import urlencode
entrypoint = "https://ru.wikipedia.org/w/index.php?"
def mkurl(title, oldid):
    return entrypoint+urlencode(dict(title=title, oldid=oldid))
assert get_all_headings(mkurl("Северовирджинская кампания",75043192)) == ['Предыстория',
                                                                          'Силы сторон',
                                                                          'Сражения',
                                                                          'Последствия',
                                                                          'Примечания',
                                                                          'Литература',
                                                                          'Ссылки']

assert get_all_headings(mkurl('User:Ilya_Voyager/sandbox/h2test',"75055744")) == ['Заголовок', 'Ещё один заголовок', 'Третий заголовок']
assert get_all_headings(mkurl('User:Ilya_Voyager/This Page Will Never Exist', "")) == ["Not found"]
del urlencode, mkurl

Задача 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. Вам нужно перехватить его.

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)))


ok = True

try:
    power_or_nan('a', 'b')
    ok = False
except:
    pass

assert ok, ("Вам нужно обрабатывать только исключение, "
                   "связанное с переполнением, другие исключения"
                   "обрабатывать не нужно")

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

Госдепартамент США имеет широкую представленность в различных соцсетях, а именно - аккаунты посольств, консульств и прочих дипломатических миссий для стран, где они присутсвуют. Напишите функцию matching_socials(snetwork1, snetwork2), которая принимает названия двух социальных сетей и возвращает список стран (список строк), для которых у Госдепартамента США есть аккаунты в обоих соцсетях. Названия соцсетей на вход берите из этого списка: facebook, twitter, youtube, instagram, google. Например, если в Фейсбуке у Госдепартамента есть аккаунты для Англии, Бразилии и России, а на Ютубе — только для Бразилии, то функция должна вернуть список, состоящий из единственного элемента: Бразилии.

Пример.

matching_socials('facebook', 'youtube') == ['Brazil']

Примечание 1. Названия стран в возвращаемом списке должны совпадать с названиями стран на сайте Госдепартамента, порядок элементов в списке может быть любым.

Примечание 2. Тип представительства (посольство, консульство, миссия, и прочее) стоит игнорировать.

Примечение 3. Вместо стран иногда могут попадаться объекты типа Department of State - их тоже включайте в итоговый список.

Подсказка. Вам пригодятся множества (set).

In [ ]:
# YOUR CODE HERE
In [ ]:
assert set(matching_socials('facebook', 'instagram')) == {'Algeria', 'Brazil', 'Canada', 'Department of State',
 'Ghana', 'Israel', 'New Zealand', 'Philippines', 'Russia', 'Thailand'}

a = set(matching_socials('facebook', 'twitter')) - {"Global Women's Issues", 
                                                    'Afghanistan',
                                                    'Cameroon',
                                                    'Kazakhstan',
                                                    'Kenya',
                                                    'Kuwait',
                                                    'Malaysia',
                                                    'Mali'}
# - {"Afghanistan", ...} is backward compatibility hack, 
# remove in future versions
# (Keep Global Women's Issue)

assert a == {'ART in Embassies', 'Africa Regional Services',
 'Amreekani (Arabic)', 'Argentina', 'Armenia', 'Arms Control, Verification and Compliance',
 'Australia', 'Australia - Melbourne', 'Australia - Perth', 'Australia - Sydney', 'Austria',
 'Bahrain', 'Belgium', 'Bolivia', 'Brazil', 'Cambodia', 'Canada', 'Careers', 'Chile', 'China',
 'China - Guangzhou', 'Civilian Response Corps', 'Colombia', 'Congo, D.R.', 'Consular Notification',
 'Costa Rica', 'Cuba', 'Cyprus', 'Czech Republic', 'Denmark', 'Dominican Republic', 'Economic and Business Affairs',
 'Ecuador', 'Egypt', 'Estonia', 'Exchange Programs', 'Finland', 'Foreign Press Center', 'France', 'Fulbright Program',
 'Gabon', 'Georgia', 'Germany', 'Germany - Duesseldorf', 'Germany - Frankfurt', 'Germany - Hamburg',
 'Germany - Leipzig', 'Germany - Munich', 'Ghana', 'Greece', 'Guatemala',
 'Guinea', 'Guyana', 'Honduras', 'Hong Kong and Macau', 'Hungary', 'India', 'India - Chennai',
 'India - Hyderabad', 'India - Kolkata', 'India - Mumbai', 'Indonesia', 'Innovation Generation',
 'Innovation Generation', 'International Exchange Alumni', 'International Security and Nonproliferation',
 'Ireland', 'Ireland - Belfast', 'Israel', 'Italy', 'Italy - Milan', 'Japan', 'Japan - Fukuoka',
 'Jerusalem', 'Jordan', 'Korea, South', 'Kyrgyzstan', 'Latvia', 'Liberia', 'Libya', 'Madagascar and the Comoros',
 'Media Hub - East Asia and Pacific', 'Media Hub - European', 'Mexico', 'Mexico - Ciudad Juarez',
 'Mexico - Guadalajara', 'Mexico - Matamoros', 'Mexico - Monterrey', 'Micronesia, Federated States of',
 'Moldova', 'Mozambique', 'Nepal', 'Netherlands', 'New Zealand', 'Nigeria', 'Norway',
 'Oceans and International Environmental and Scientific Affairs', 'Oman', 'Our Planet',
 'Pakistan', 'Pakistan - Lahore', 'Panama', 'Paraguay', 'Peru', 'Philippines', 'Poland',
 'Qatar', 'Samoa', 'Saudi Arabia', 'Senegal', 'Serbia', 'Singapore', 'Slovenia', 'South Africa',
 'South and Central Asian Affairs', 'Spain', 'Spain - Barcelona', 'Sri Lanka', 'State Magazine',
 'Sweden', 'Syria', 'Tanzania', 'Thailand', 'Thailand - Chiang Mai', 'Timor-Leste', 'Togo',
 'Trafficking in Persons', 'Turkey', 'U.S. Mission to ASEAN', 'U.S. Mission to UN in Vienna',
 'USAdarFarsi', 'Uganda', 'United Kingdom', 'Uruguay', 'Venezuela', 'Virtual Student Foreign Service',
 'Zambia', 'Zimbabwe'}

assert set(matching_socials('google', 'instagram')) == {'Department of State', 'Philippines'}
assert set(matching_socials('instagram', 'google')) == {'Department of State', 'Philippines'}

print('Nailed it!')