Ищите, фильтруйте и отображайте статистику покемонов и определяйте похожих покемонов
В этой статье мы создадим веб-приложение Pokedex на Streamlit (скриншот выше). Конечный продукт развернут здесь: https://damian-pokedex.streamlit.app/.
Это веб-приложение позволяет пользователю выполнять поиск в наборе данных о покемонах:
- его имя и выпадающее меню совпадений имен
- фильтрация по диапазону базовых характеристик (атака, защита и т. д.) с помощью ряда ползунков
Для каждого покемона отображается его изображение, тип, вид, способности, базовая статистика в виде гистограммы, а также слабости и сопротивления. Также доступна информация о дрессировке и разведении.
Моя любимая часть приложения заключается в том, что оно отображает радарную диаграмму базовой статистики покемонов, а также может искать 20 покемонов с наиболее похожими базовыми характеристиками и их соответствующими радарными диаграммами. Мне было очень весело с этой функцией, когда я искал покемонов, похожих на легендарных птиц, таких как Молтрес, стартовых покемонов, таких как Чармандер, или даже очень слабых покемонов, таких как Пиджи.
Полный код этого проекта приведен в следующем репозитории GitHub.
Для работы приложения это необходимые файлы и папки, также в репозитории GitHub.
Если вам нужна дополнительная информация о Streamlit и о том, как развертывать веб-приложения в Интернете, не стесняйтесь взглянуть на мою предыдущую статью ниже.
Наборы данных
Во-первых, давайте посмотрим на задействованные наборы данных.
Набор данных покемонов
Первый набор данных можно получить в виде файла .csv по этой ссылке Kaggle. Как вы понимаете, большая часть кода будет посвящена синтаксическому анализу этого набора данных и отображению его в красивом формате на Streamlit. Чтобы коды работали, этот файл нужно переименовать в ‘pokedex.csv’
.
Набор данных изображений покемонов
Изображения покемонов доступны в папке, также от Kaggle здесь. Изображения названы в соответствии с номером Pokedex каждого покемона. Обратите внимание на скриншот ниже, что некоторые покемоны имеют разные вариации, такие как Primal Groudon, Primal Kyogre, Mega Latios, а также Нормальную, Атакующую, Защитную и Скоростную форму Деоксиса.
Это означает, что должна быть написана функция для поиска пути изображения для каждого покемона, соответствующего его номеру покедекса и варианту. Все изображения помещаются в папку с именем ‘pokemon_images’
для работы приведенных ниже кодов.
Без лишних слов, давайте погрузимся в код. Основная часть кода находится в файле app.py.
Импорт и конфигурация страницы
Во-первых, нам нужны следующие импорты: PIL
для отображения изображений покемонов, numpy
и pandas
для чтения и анализа файла pokedex.csv
и, конечно же, matplotlib
и plotly
для отображения графиков статистики покемонов. Мы также установили заголовок страницы и настроили макет как «широкий», чтобы максимально аккуратно отображать как можно больше информации в нашем Pokedex.
Чтение в наборе данных Pokemon и файле CSS
Затем мы определяем две функции и вызываем их при запуске приложения.
Функция local_css
читает файл таблицы стилей style.css
. Я не буду вдаваться в детали файла, но вы можете посмотреть его здесь. По сути, он состоит из информации о стиле для отображения типов покемонов в удобном формате, как показано ниже. Эти типы отображаются аккуратным шрифтом, и каждый тип связан с другим цветом, чтобы он выглядел более покемоновским!
Строка 4 приведенного выше кода считывает файл CSS и записывает его в виде HTML-кода, помещенного в середину тегов <style>
. HTML-код можно написать в Streamlit с помощью метода markdown()
. Теперь мы можем получить доступ к классам, определенным в таблице стилей позже.
Функция get_data
считывает файл pokedex.csv
в DataFrame с именем df
для последующего анализа и фильтрации. Для простоты мы сохраняем только первых 846 покемонов (до поколения 6), нарезая их с помощью iloc.
Боковая панель: поиск покемонов по имени
Теперь мы готовы запрограммировать некоторые элементы пользовательского интерфейса! Давайте начнем с элементов для поиска покемона по его имени и возврата совпадений имени.
В следующем коде мы сначала печатаем заголовок боковой панели как Pokédex. Затем мы включаем поле text_input
в строку 3, чтобы пользователь мог ввести имя. Мы сравниваем этот ввод со всеми именами в df
DataFrame и возвращаем совпадения имен в списке matches
в строке 5. Чтобы избежать каких-либо проблем с чувствительностью к регистру, мы преобразуем как пользовательский ввод, так и имена покемонов в DataFrame в нижний регистр, используя lower()
метод, прежде чем сравнивать их.
Если есть какие-либо совпадения имен, мы выводим список matches
в раскрывающийся список (называемый полем выбора в Streamlit) в строке 8 приведенного выше кода, позволяя пользователю выбрать точное имя интересующего покемона. Затем в строке 13 мы возвращаем соответствующую строку DataFrame, которая соответствует этому конкретному покемону. Эта строка называется match
и очень важна, так как вся информация в ней будет проанализирована и выведена на главную страницу.
Пример строки, соответствующей Бульбазавру, показан ниже.
Боковая панель: выберите информацию для просмотра
Следующая часть боковой панели — это multiselector
, которая позволяет пользователю выбирать, какую информацию отображать на главной странице Pokedex. Например, пользователи, которым информация о дрессировке и разведении кажется скучной, могут ее вычеркнуть. По умолчанию вся информация выбрана.
В приведенном выше коде показано, как устроено поле multiselect
. Четыре раздела информации на главной странице: ‘Basic Information’, ‘Base Stats & Type Defenses’, ‘Training and Breeding’, ‘Radar Chart’
. Вывод из этого поля сохраняется в списке selected_info
, который будет использоваться ближе к концу статьи, когда мы будем отображать всю информацию о покемонах.
Давайте теперь сначала погрузимся в функции для отображения каждой информации.
Показать основную информацию
Основная информация о покемоне состоит из его изображения, типа, роста, веса, вида и способностей. Эта информация отображается в трех аккуратных столбцах следующим образом на примере Mewtwo.
Вся эта информация хранится в функции с именем display_basic_info
. Функция принимает переменную match
, которая представляет собой строку в DataFrame, соответствующую интересующему покемону, описанному ранее.
Первая часть вышеуказанной функции извлекает соответствующую информацию (рост, вес, вид и т. д.) из строки match
и соответствующих имён столбцов. Затем в строке 15 печатаются покемоны name
и id
в качестве заголовка, например, «Mewtwo #150», как показано выше. Затем мы разделили пользовательский интерфейс на три столбца: col1
, col2
и col3
в строке 16, чтобы аккуратно отобразить информацию.
Следующая часть показанной выше функции отображает изображение покемона в первом и самом левом столбце col1
. Сначала в строке 3 вызывается функция get_image_path()
, чтобы найти путь изображения покемона из его name
и id
. Вы можете найти эту функцию в файле app.py. Я не буду вдаваться в подробности функции, но в основном код должен быть написан вручную, чтобы вытягивать изображения покемонов с различными вариациями, как обсуждалось ранее (например, Мега, X, Y, разные формы, разные плащи и т. д.) .
Затем мы открываем изображение с помощью метода Image
в библиотеке PIL
и отображаем его (строки 4 и 5). Мы помещаем весь код в блок try except
, чтобы приложение не ломалось, если изображение не найдено. Скорее, он просто отобразит «Изображение недоступно».
Приведенный выше код отображает атрибуты типа, высоты и веса во втором столбце col2
. Эти переменные уже были извлечены в начале функции. Обратите внимание, что type_text
в строке 5 записано в HTML между тегами <span>
. Атрибут class
соответствует классу, определенному в упомянутом ранее файле style.css
. Это отображает тип покемонов в красиво отформатированном виде.
Например, HTML-код для отображения типа пожара — <span class=”icon type-fire”>fire</span>
. Строки 6 и 7 добавляются к HTML-коду, хранящемуся в type_text
, если для покемона есть второй тип.
Строка 9 отображает этот код в HTML. Строки 10 и 11 затем отображают рост и вес с использованием метода metric
очень аккуратно, как показано на снимке экрана ранее.
Наконец, приведенный выше код отображает виды и способности в крайнем правом столбце col3
. Не у всех покемонов есть все три способности, поэтому циклы if должны показывать способности только в том случае, если они присутствуют.
Показать диаграмму базовой статистики и тип защиты
Базовая статистика и тип защиты отображаются в двух столбцах следующим образом, снова на примере информации Mewtwo.
Код для отображения этой информации находится в функции display_base_stats_type_defenses
.
Первая часть приведенной выше функции показывает, как анализируются и сохраняются слабости и сопротивления типов. Набор данных, т. е. строка match
, не хранит слабости и сопротивления в списках. Скорее он показывает множитель урона, который каждый тип может нанести конкретному покемону.
Например, в строке, показанной ниже, покемон слаб against_flying
, так как летающие типы наносят в 2 раза больше урона. Кроме того, несмотря на то, что он устойчив against_fairy
к феям, он наносит ему только 0,5-кратный урон. Обратите внимание, что покемоны с несколькими типами могут быть вдвойне слабыми (x4) или вдвойне устойчивыми (x0,25) к определенным типам.
В строках с 10 по 21 приведенного выше кода мы перебираем каждый из этих столбцов, чтобы найти значения множителя урона и сохранить их в соответствующих списках слабостей (x2 и x4) и сопротивлений (x0,25 и x0,5), определенных в строках. От 3 до 6. Обратите внимание, что нас не интересуют значения с множителем 1,0x, поскольку покемоны не слабы и не устойчивы к этим типам.
Приведенный выше код затем получает базовую статистику (хп, атака и т. д.) покемона из строки match
и красиво переименовывает столбцы в строках с 4 по 6. Результат сохраняется в df_stats
, и его структура показана ниже. Затем они наносятся на горизонтальную гистограмму с использованием matplotlib
в строке 10 и отображаются в левом столбце col1
в строке 12. Обратите внимание, что диапазон каждой базовой статистики составляет от 0 до 250, и мы фиксируем этот диапазон по оси x.
Последняя часть кода, показанного выше, перебирает каждый из типов в списках типов слабостей и сопротивлений и отображает их в правом столбце col2
соответственно. Опять же, мы используем HTML, чтобы гарантировать, что типы отображаются в красивом стиле, как объяснялось ранее.
Отображение информации о тренировках и разведении
Следующая часть приложения отображает информацию о тренировках и разведении в двух столбцах ниже:
Код для получения и отображения этой информации находится в функции display_training_breeding
. Поскольку это похоже на другие функции отображения, описанные выше, я оставлю читателю задание разобраться с кодом в качестве упражнения!
Обратите внимание, что информация о разведении не всегда доступна. Например, в случае с Mewtwo, описанном выше, типы яиц не обнаружены. Следовательно, информация о процентном соотношении мужских и женских яйцеклеток также недоступна, поэтому мы должны соответствующим образом анализировать ее в коде.
Показать радарную диаграмму базовой статистики
Я люблю радарные диаграммы, потому что они отображают статистику покемонов таким элегантным и симметричным образом. На такой диаграмме легко определить сильные и слабые стороны. К счастью, библиотека plotly express позволяет нам легко построить это. Радарная диаграмма базовой статистики Mewtwo выглядит так, как показано.
В приведенной выше функции мы получаем базовую статистику и сохраняем ее в df_stats
, аналогично тому, как мы делали это при построении горизонтальной гистограммы ранее. Затем в строке 9 показано, как мы можем использовать библиотеку plotly express, чтобы отобразить это на лепестковой диаграмме, а в строке 10 это отображается в Streamlit.
Теперь, не было бы неплохо поискать во всем наборе данных покемонов покемонов с похожей базовой статистикой? Если вы заметили на скриншоте выше, есть кнопка под названием Search for Pokemons with Similar Base Stats
. Когда эта кнопка нажата, мы вызываем для этого другую функцию, как показано в строках 12 и 13 кода выше.
Поиск покемонов с похожими базовыми характеристиками
Каким бы могущественным ни был Мьюту, подобные покемоны столь же сильны. Два наиболее похожих покемона показаны ниже. Обратите внимание, насколько похожи их радарные диаграммы по сравнению с диаграммами Mewtwo, показанными выше. Для поклонников покемонов неудивительно, что Shaymin Sky Forme и Mega Houndoom похожи на Mewtwo.
Как упоминалось в начале статьи, мне очень понравилось использовать эту функцию в приложении. Не стесняйтесь пытаться искать покемонов, похожих на легендарных, таких как Kyogre, или даже стартовых покемонов, таких как Чармандер.
Теперь давайте научимся делать это в коде:
Во-первых, мы еще раз получаем df_stats
, чтобы показать всю статистику интересующего покемона. Затем в строках 7 и 8 мы получаем статистику всех других покемонов во всем df
DataFrame и сохраняем ее в df_stats_all
. Затем мы получаем разницу в статистике между df_stats
для этого конкретного покемона и всеми строками в df_stats_all
в строке 11.
Затем мы используем эту разницу для расчета нормированного расстояния между каждым покемоном и этим конкретным покемоном. Это делается с помощью функции np.linalg.norm
в строке 14 и сохранения расстояний в norm_df
. Наконец, мы берем статистику покемонов с 20 наименьшими расстояниями (исключая самого исходного покемона) и сохраняем их в similar_pokemons_df
DataFrame.
Затем из нашего similar_pokemons_df
DataFrame мы используем приведенный ниже код, чтобы вывести имя, изображение и радарную диаграмму каждого из покемонов и отобразить их. Наконец, мы отображаем сводную таблицу всех 20 похожих покемонов и их характеристики в последних двух строках кода.
Боковая панель: поиск покемонов в диапазоне базовых характеристик
Теперь давайте вернемся к одному сегменту кода боковой панели, который я не обсуждал. Здесь у нас есть ряд ползунков, соответствующих каждой базовой статистике покемонов, поэтому пользователь может искать покемонов в пределах определенного диапазона базовых характеристик.
Код для отображения этих ползунков показан ниже и в основном говорит сам за себя. Обратите внимание, что эти ползунки связаны вместе, сгруппировав сегмент кода под одним и тем же st.sidebar.form
с соответствующей кнопкой отправки под названием Search Pokemon
. Последняя строка кода сохраняет состояние кнопки в логической переменной pressed
. Эта переменная становится истинной при нажатии кнопки.
При нажатии кнопки мы хотим отобразить всех покемонов, которые соответствуют нашему диапазону поиска. Но сначала поговорим о том, что происходит, если кнопка Search Pokemon
не нажата.
Отображение данных после поиска пользователем по имени
Если кнопка не нажата, это означает, что пользователь использует функцию Search Name
для поиска интересующего его покемона, и это обрабатывается кодом ниже:
Напомним в предыдущей части статьи, что данные этого покемона хранятся в строке под названием match
. После того, как пользователь сделает выбор, match
будет иметь ровно одну строку, а строки с 9 по 16 приведенного выше кода вызывают каждую функцию отображения для отображения соответствующей информации о покемонах.
Каждая функция вызывается только в том случае, если пользователь выбрал отображаемую информацию. Напомним, что пользователь может выбрать это из multiselector
, описанного ранее, и выбранная пользователем информация сохраняется в списке selected_info
.
Отображение данных после поиска пользователем по базовой статистике
Если нажата кнопка Search Pokemon
, приведенный ниже код получает характеристики всех покемонов и применяет фильтр к диапазону характеристик, которые пользователь указал в ползунках. Все соответствующие покемоны и их статистика будут отображаться в таблице. Обратите внимание, что мы не показываем диаграммы или другую информацию о каждом покемоне, так как этот список может быть довольно большим, и мы не хотим, чтобы приложение вылетало!
Пример пользователя, ищущего покемонов с достаточно высокими характеристиками, показан ниже. Это определенно не обычные покемоны!
Вот еще один забавный факт: существуют ли покемоны со статистикой HP выше 200? Да! Представляем нашу любимую Chansey и Zygarde Complete Forme.
Боковая панель Кредиты
Наконец, давайте закончим код app.py
отказом от ответственности, что мы не владеем никакими данными или изображениями покемонов! :)
файлы requirements.txt и config.toml
В дополнение к файлу app.py нам нужно включить файл requirements.txt
, чтобы показать список необходимых библиотек Python для установки, чтобы их можно было установить автоматически на сервер, на котором развернуто приложение.
streamlit pandas plotly matplotlib
При желании я также создал папку с именем .streamlit
, которая включает файл config.toml
, чтобы установить базовую тему как «светлую» вместо «темной».
[theme] base=”light”
Запустите приложение локально
Это все, что вам нужно для приложения. Чтобы запустить приложение локально, просто запустите следующую команду в терминале в той же папке, что и ваш файл app.py
. Убедитесь, что ваш файл pokedex.csv
и папка pokemon_images
также находятся внутри.
streamlit run app.py
В случае успеха должно появиться следующее:
В вашем браузере также должен появиться указанный выше локальный URL-адрес, и ваше приложение Pokedex должно работать в браузере! Поздравляю!
Удаленное развертывание приложения
Если вы хотите развернуть приложение удаленно, как я сделал здесь, в статье ниже я обобщил три варианта.
Еще раз, вот ссылка на репозиторий GitHub.
Получайте удовольствие от своего приложения Pokedex; не стесняйтесь добавлять или предлагать новые функции!