Мэтиз. Изучаем Python. Crash course2 n d e d i t i o na h a n d s o n, p r o j e c t b a s e d i n t r o d u c t i o n t o p r o g r a m m i n g
Скачать 6.2 Mb.
|
Рис. 15.11. Случайное блуждание c 50 000 точек Случайное блуждание 337 Поэкспериментируйте с этим кодом и посмотрите, насколько вам удастся увели- чить количество точек в случайном блуждании, прежде чем система начнет заметно тормозить или диаграмма потеряет свою визуальную привлекательность. Изменение размера диаграммы для заполнения экрана Визуализация гораздо эффективнее передает закономерности в данных, если она адаптирована под размер экрана. Чтобы диаграмма лучше смотрелась на экране, измените размер области просмотра Matplotlib: rw_visual.py while True: # Построение случайного блуждания. rw = RandomWalk() rw.fill_walk() # Назначение размера области просмотра. plt.style.use('classic') fig, ax = plt.subplots(figsize=(15, 9)) Функция figure() управляет шириной, высотой, разрешением и цветом фона диа- граммы. Параметр figsize получает кортеж с размерами окна диаграммы в дюймах. Matplotlib предполагает, что разрешение экрана составляет 100 пикселов на дюйм; если этот код не дает точного размера, внесите необходимые изменения в чис- ла. Или, если вы знаете разрешение экрана в вашей системе, передайте его plt. subplots() в параметре dpi для выбора размера, эффективно использующего до- ступное пространство: fig, ax = plt.subplots(figsize=(10, 6), dpi=128) УПРАЖНЕНИЯ 15.3. Молекулярное движение: измените программу rw_visual .py и замените plt.scatter() вызовом plt.plot() . Чтобы смоделировать путь пыльцевого зерна на поверхности водя- ной капли, передайте значения rw.x_values и rw.y_values и включите аргумент linewidth Используйте 5000 точек вместо 50 000. 15.4. Измененные случайные блуждания: в классе RandomWalk значения x_step и y_step ге- нерируются по единому набору условий. Направление выбирается случайно из списка [1, -1] , а расстояние — из списка [0, 1, 2, 3, 4] . Измените значения в этих списках и посмо- трите, что произойдет с общей формой диаграммы. Попробуйте применить расширенный список вариантов расстояния (например, от 0 до 8) или удалите –1 из списка направлений по оси x или y. 15.5. Рефакторинг: метод fill_walk() получился слишком длинным. Создайте новый ме- тод с именем get_step() , который определяет расстояние и направление для каждого шага, после чего вычисляет этот шаг. В результате метод fill_walk() должен содержать два вы- зова get_step() : 338 Глава 15 • Генерирование данных x_step = self.get_step() y_step = self.get_step() Рефакторинг сокращает размер fill_walk() , а метод становится более простым и понятным. Моделирование бросков кубиков в Plotly В этом разделе мы воспользуемся пакетом визуализации Plotly для построения ин- терактивных визуализаций. Пакет Plotly особенно хорошо подходит для визуализа- ций, которые будут отображаться в браузере, потому что изображение автоматически масштабируется по размерам экрана зрителя. Кроме того, Plotly генерирует интерак- тивные визуализации; когда пользователь наводит указатель мыши на некоторые элементы, на экране появляется расширенная информация об этом элементе. В этом проекте мы займемся анализом результатов бросков кубиков. При броске одного шестигранного кубика существует равная вероятность выпадения любого числа от 1 до 6. С другой стороны, при броске двух кубиков некоторые суммы выпадают с большей вероятностью, чем другие. Чтобы определить, какие числа наиболее вероятны, мы сгенерируем набор данных, представляющих брошенные кубики. Затем на базе данных большого количества бросков будет построена диа- грамма, по которой можно определить, какие результаты более вероятны. Броски кубиков часто используются в математике для пояснения различных типов анализа данных. Впрочем, они также находят применение в реальных приложе- ниях — скажем, в казино и в обычных играх («Монополия», ролевые игры и т. д.). Установка Plotly Установите Plotly при помощи pip по аналогии с тем, как это делалось с Matplotlib: $ python -m pip install --user plotly (Если при установке Matplotlib использовалась команда python3 или другая, убе- дитесь в том, что в данном случае используется то же самое.) Примеры визуализаций, которые могут быть построены с использованием Plotly, представлены в галерее диаграмм: зайдите на сайт https://plot .ly/python/ . Каждый пример сопровождается исходным кодом, так что вы сможете увидеть, как была построена каждая из визуализаций. Создание класса кубика Для моделирования броска одного кубика будет использоваться класс Die : die.py from random import randint Моделирование бросков кубиков в Plotly 339 class Die(): """Класс, представляющий один кубик.""" ❶ def __init__(self, num_sides=6): """По умолчанию используется шестигранный кубик.""" self.num_sides = num_sides def roll(self): """"Возвращает случайное число от 1 до числа граней.""" ❷ return randint(1, self.num_sides) Метод __init__() получает один необязательный аргумент. Если при создании экземпляра кубика аргумент с количеством сторон не передается, по умолчанию создается шестигранный кубик. Если же аргумент присутствует, то переданное значение используется для определения количества граней . (Кубики принято обозначать по количеству граней: шестигранный кубик — D6, восьмигранный — D8 и т. д.) Метод roll() использует функцию randint() для получения случайного числа в диапазоне от 1 до количества граней . Функция может вернуть начальное зна- чение (1), конечное значение ( num_sides ) или любое целое число в этом диапазоне. Бросок кубика Прежде чем строить визуализацию на основе этого класса, бросим кубик D6, вы- ведем результаты и убедимся в том, что они выглядят разумно: die_visual.py from die import Die # Создание кубика D6. ❶ die = Die() # Моделирование серии бросков с сохранением результатов в списке. results = [] ❷ for roll_num in range(100): result = die.roll() results.append(result) print(results) В точке создается экземпляр Die с шестью гранями по умолчанию. В точке моделируются 100 бросков кубика, а результат каждого броска сохраняется в спи- ске results . Выборка выглядит примерно так: [4, 6, 5, 6, 1, 5, 6, 3, 5, 3, 5, 3, 2, 2, 1, 3, 1, 5, 3, 6, 3, 6, 5, 4, 1, 1, 4, 2, 3, 6, 4, 2, 6, 4, 1, 3, 2, 5, 6, 3, 6, 2, 1, 1, 3, 4, 1, 4, 3, 5, 1, 4, 5, 5, 2, 3, 3, 1, 2, 3, 5, 6, 2, 5, 6, 1, 3, 2, 1, 1, 1, 6, 5, 5, 2, 2, 6, 4, 1, 4, 5, 1, 1, 1, 4, 5, 3, 3, 1, 3, 5, 4, 5, 6, 5, 4, 1, 5, 1, 2] 340 Глава 15 • Генерирование данных Беглое знакомство с результатами показывает, что класс Die работает. В результа- тах встречаются граничные значения 1 и 6, то есть модель возвращает наименьшее и наибольшее возможное значение; значения 0 и 7 не встречаются, а значит, все результаты лежат в диапазоне допустимых значений. Также в выборке встречаются все числа от 1 до 6, то есть представлены все возможные результаты. Анализ результатов Чтобы проанализировать результаты бросков одного кубика D6, мы подсчитаем, сколько раз выпадало каждое число: die_visual.py # Моделирование серии бросков с сохранением результатов в списке. results = [] ❶ for roll_num in range(1000): result = die.roll() results.append(result) # Анализ результатов. frequencies = [] ❷ for value in range(1, die.num_sides+1): ❸ frequency = results.count(value) ❹ frequencies.append(frequency) print(frequencies) Так как Plotly используется для анализа, а не для вывода результатов, количество моделируемых бросков можно увеличить до 1000 . Для анализа создается пустой список frequencies , в котором хранится количество выпадений каждого значения. Программа перебирает возможные значения (от 1 до 6 в данном случае) в цикле , подсчитывает количество вхождений каждого числа в результатах , после чего присоединяет полученное значение к списку frequencies . Содержимое списка выводится перед построением визуализации: [155, 167, 168, 170, 159, 181] Результаты выглядят разумно: мы видим все шесть частот, по одной для каждого возможного результата при броске D6, и ни одна из частот не выделяется на общем фоне. А теперь займемся наглядным представлением результатов. Построение гистограммы Имея список частот, можно построить гистограмму результатов. Гистограмма пред- ставляет собой столбцовую диаграмму, наглядно отображающую относительные частоты результатов. Код построения гистограммы выглядит так: Моделирование бросков кубиков в Plotly 341 die_visual.py from plotly.graph_objs import Bar, Layout from plotly import offline from die import Die # Анализ результатов. frequencies = [] for value in range(1, die.num_sides+1): frequency = results.count(value) frequencies.append(frequency) # Визуализация результатов. ❶ x_values = list(range(1, die.num_sides+1)) ❷ data = [Bar(x=x_values, y=frequencies)] ❸ x_axis_config = {'title': 'Result'} y_axis_config = {'title': 'Frequency of Result'} ❹ my_layout = Layout(title='Results of rolling one D6 1000 times', xaxis=x_axis_config, yaxis=y_axis_config) ❺ offline.plot({'data': data, 'layout': my_layout}, filename='d6.html') Чтобы построить столбцовую диаграмму, необходимо создать столбец для каж- дого из возможных результатов. Эти результаты сохраняются в списке x_values , который начинается с 1 и заканчивается количеством граней кубика . Plotly не может получить результат функции range() напрямую, поэтому необходимо явно преобразовать диапазон в список при помощи функции list . Класс Bar из Plotly представляет набор данных, который будет форматироваться в виде столбцовой диаграммы . Класс должен быть заключен в квадратные скобки, поскольку набор данных может состоять из нескольких элементов. Для осей предусмотрены различные возможности настройки, и каждый параметр конфигурации сохраняется в виде элемента в словаре. На данный момент мы толь- ко задаем заголовок каждой оси . Класс Layout() возвращает объект, который за- дает макет и конфигурацию диаграммы в целом . Здесь также задается заголовок диаграммы и передаются словари конфигурации осей x и y Диаграмма строится вызовом функции offline.plot() . Этой функции пере- дается словарь с объектами данных и макета; она также получает имя файла для сохранения результата. В нашем примере результат сохраняется в файле с именем d6 .html При запуске программы die_visual .py должен открыться браузер с файлом d6 .html Если это не происходит автоматически, откройте новую вкладку в любом браузере, а затем откройте файл d6 .html (из папки, в которой был сохранен файл die_visual .py ). Диаграмма должна выглядеть примерно так, как на рис. 15.12. (Изображение было слегка изменено для печати; по умолчанию Plotly генерирует диаграммы с более темным фоном, чем на иллюстрации.) 342 Глава 15 • Генерирование данных Рис. 15.12. Простая гистограмма, созданная с использованием Plotly Обратите внимание на интерактивность диаграмм, построенных с использованием Plotly: если навести указатель мыши на столбец диаграммы, вы увидите данные, связанные с этим столбцом. Данная возможность особенно полезна при нанесе- нии нескольких наборов данных на одну диаграмму. Также обратите внимание на кнопки в правом верхнем углу: они позволяют изменить масштаб визуализации и сохранить ее в графическом файле. Бросок двух кубиков При броске двух кубиков вы получаете большие значения с другим распределением результатов. Изменим наш код и создадим два кубика D6, моделирующих бросок пары кубиков. При броске каждой пары программа складывает два числа (по одно- му с каждого кубика) и сохраняет сумму в results . Сохраните копию die_visual .py под именем dice_visual .py и внесите следующие изменения: dice_visual.py from plotly.graph_objs import Bar, Layout from plotly import offline from die import Die # Создание двух кубиков D6. die_1 = Die() die_2 = Die() # Моделирование серии бросков с сохранением результатов в списке. results = [] for roll_num in range(1000): Моделирование бросков кубиков в Plotly 343 ❶ result = die_1.roll() + die_2.roll() results.append(result) # Анализ результатов. frequencies = [] ❷ max_result = die_1.num_sides + die_2.num_sides ❸ for value in range(2, max_result+1): frequency = results.count(value) frequencies.append(frequency) # Визуализация результатов. x_values = list(range(2, max_result+1)) data = [Bar(x=x_values, y=frequencies)] ❹ x_axis_config = {'title': 'Result', 'dtick': 1} y_axis_config = {'title': 'Frequency of Result'} my_layout = Layout(title='Results of rolling two D6 dice 1000 times', xaxis=x_axis_config, yaxis=y_axis_config) offline.plot({'data': data, 'layout': my_layout}, filename='d6_d6.html') Создав два экземпляра Die , мы бросаем кубики и вычисляем сумму для каждого броска . Самый большой возможный результат (12) вычисляется суммировани- ем наибольших результатов на обоих кубиках; мы сохраняем его в max_result . Наименьший возможный результат (2) равен сумме наименьших результатов на обоих кубиках. В процессе анализа подсчитывается количество результатов для каждого значения от 2 до max_result . (Также можно было использовать диапазон range(2, 13) , но он работал бы только для двух кубиков D6. При моделировании реальных ситуаций лучше писать код, который легко адаптируется для разных ситуаций. В частности, этот код позволяет смоделировать бросок пары кубиков с любым количеством граней.) При создании диаграммы мы включаем ключ dtick в словарь x_axis_config . Этот параметр управляет расстоянием между делениями на оси x. С появлением дополнительных столбцов на гистограмме в конфигурации по умолчанию Plotly будет снабжать метками только часть столбцов. Параметр 'dtick': 1 приказывает Plotly помечать все деления. Также мы обновляем заголовок диаграммы и изме- няем имя выходного файла. После выполнения кода в браузере должна появиться диаграмма, примерный вид которой показан на рис. 15.13. На диаграмме показаны примерные результаты, которые могут быть получены для пары кубиков D6. Как видите, реже всего выпадают результаты 2 и 12, а чаще всего 7, потому что эта комбинация может быть выброшена шестью способами, а именно: 1+6, 2+5, 3+4, 4+3, 5+2 и 6+1. Броски кубиков с разным числом граней Создадим кубики с 6 и 10 гранями и посмотрим, что произойдет, если бросить их 50 000 раз: 344 Глава 15 • Генерирование данных Рис. 15.13. Смоделированные результаты 1000 бросков двух шестигранных кубиков dice_visual.py from plotly.graph_objs import Bar, Layout from plotly import offline from die import Die # Создание кубиков D6 и D10. die_1 = Die() ❶ die_2 = Die(10) # Моделирование серии бросков с сохранением результатов в списке. results = [] for roll_num in range(50000): result = die_1.roll() + die_2.roll() results.append(result) # Анализ результатов. # Визуализация результатов. X_values = list(range(2, max_result+1)) data = [Bar(x=x_values, y=frequencies)] x_axis_config = {'title': 'Result', 'dtick': 1} y_axis_config = {'title': 'Frequency of Result'} ❷ my_layout = Layout(title='Results of rolling a D6 and a D10 50000 times', xaxis=x_axis_config, yaxis=y_axis_config) offline.plot({'data': data, 'layout': my_layout}, filename='d6_d10.html') Чтобы создать модель кубика D10, мы передаем аргумент 10 при создании второго экземпляра Die и изменяем первый цикл для моделирования 50 000 бросков Моделирование бросков кубиков в Plotly 345 вместо 1000. Наименьший возможный результат, как и прежде, равен 2, зато наи- больший увеличился до 16; мы соответственно изменяем заголовок, метки оси x и метки серии данных . На рис. 15.14 показана полученная диаграмма. Вместо одного наиболее вероят- ного результата их стало целых пять. Это объясняется тем, что наименьшее (1+1) и наибольшее (6+10) значения по-прежнему могут быть получены только одним способом, но кубик D6 ограничивает количество способов генерирования средних чисел: суммы 7, 8, 9, 10 и 11 можно выбросить шестью способами. Следовательно, именно эти результаты являются наиболее частыми и все эти числа выпадают с равной вероятностью. Рис. 15.14. Результаты 50 000 бросков шести- и десятигранного кубиков Возможность применения Plotly для моделирования бросков кубиков дает су- щественную свободу при исследовании этого явления. За считаные минуты вы сможете смоделировать огромное количество бросков с разнообразными куби- ками. УПРАЖНЕНИЯ 15.6. Два кубика D8: создайте модель, которая показывает, что происходит при 1000-крат- ном бросании двух восьмигранных кубиков. Попробуйте заранее представить, как будет выглядеть визуализация, перед моделированием; проверьте правильность своих интуитив- ных представлений. Постепенно наращивайте количество бросков, пока не начнете заме- чать ограничения, связанные с ресурсами вашей системы. 15.7. Три кубика: при броске трех кубиков D6 наименьший возможный результат равен 3, а наибольший — 18. Создайте визуализацию, которая показывает, что происходит при броске трех кубиков D6. 346 Глава 15 • Генерирование данных 15.8. Умножение: при броске двух кубиков результат обычно определяется суммировани- ем двух чисел. Создайте визуализацию, которая показывает, что происходит при умноже- нии этих чисел. 15.9. Генераторы: для наглядности в списках этого раздела используется длинная форма цикла for . Если вы уверенно работаете с генераторами списков, попробуйте написать гене- ратор для одного или обоих циклов в каждой из этих программ. 15.10. Эксперименты с библиотеками: попробуйте использовать Matplotlib для создания визуализации бросков кубиков, а Plotly — для создания визуализации случайного блужда- ния. (Для выполнения этого упражнения вам придется обратиться к документациям обеих библиотек.) Итоги В этой главе вы научились генерировать наборы данных и строить визуализа- ции этих данных. Вы научились строить простые диаграммы с использованием Мatplotlib и применять точечные диаграммы для анализа случайных блужданий. Вы узнали, как построить гистограмму с использованием Plotly и как исследовать результаты бросков кубиков с разным количеством граней при помощи гистограм- мы. Генерирование собственных наборов данных в программах — интересный и мощ- ный способ моделирования и анализа различных реальных ситуаций. В дальней- ших проектах визуализации данных обращайте особое внимание на ситуации, которые могут быть смоделированы на программном уровне. Присмотритесь к визуализациям, встречающимся в выпусках новостей, — возможно, они были сгенерированы методами, сходными с теми, о которых вы узнали в этих проектах? В главе 16 мы загрузим данные из сетевого источника и продолжим использовать Мatplotlib и Plotly для анализа данных. |