Оформляем красивые отчеты и приложения в Streamlit и запускаем в облака

от автора

Каждый исследователь, студент или преподаватель рано или поздно сталкивается с необходимостью создавать интерактивные и красивые отчеты, которые позже следует приложить к статье, сдать на проверку или поделиться со студентами. Все чаще в топовых ВУЗах приевшиеся методички заменяют на Jupiter ноутбуки, а возможность сварганить красивый отчет «на коленке» прельщает огромное количество студентов.

Выделим основные недостатки отчетов в Jupiter Notebook:

  • Много кода. В большинстве отчетов важны таблицы, графики и выводы, а не куски кода, которые помогли эти данные получить и составляют, как правило, более половины отчета.

  • Сложность в интерактивной демонстрации. Для полноценной работы Jupiter Notebook необходимо иметь приложение и интерпретатор питон (или онлайн сервис).

Streamlit

Это low-code фреймворк, созданные для исследователей, которые используют python. Благодаря встроенному набору элементов можно быстро накидать свой отчет или веб приложение, а главное поделиться им с другими, развернув его в облаке всего за пять минут!

Установка

Устанавливается streamlit через pip:

pip install streamlit

После установки можно запустить пример, введя в терминале команду:

streamlit

Когда приложение запустится, в терминале будет приветственное сообщение и адрес, причем окно в браузере, скорее всего, откроется самостоятельно.

Открывшееся окно приложения

Открывшееся окно приложения

На страничке мы видим ссылку на сайт и много других полезных ссылок, которые пригодятся во время создания своего приложения.

Создаем свой отчет

Все начитается с того, что нужно установить и импортировать streamlit

import streamlit as st

Я хочу сделать так, чтобы в одном проекте можно было переключаться между разными отчетами, которые будут представлять отдельные странички. Для этого используется виджет sidebar:

page = st.sidebar.selectbox("Выбрать страницу",                              ["Тяжелые хвосты распределений",                               "Iris Dataset"])

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

Чтобы переключать страницы, нужно лишь использовать условный оператор if:

    if page == "Тяжелые хвосты распределений":         st.header("""Демонстрация Fisher's Iris датасета""")     elif page == "Iris Dataset":       st.header("""Сгенерировать N случайных событий из распределения Фреше с функцией распределения:""")

Для формирования заголовка используется виджет st.header

Так же есть возможность использовать latex:

st.latex(r'''                 F(x) = exp(-(\gamma x)^{-1/\gamma}1\{x>0\})           ''')

Итак, код для отображения страницы «Тяжелые хвосты распределений»:

def main():     page = st.sidebar.selectbox("Выбрать страницу", ["Тяжелые хвосты распределений", "Iris Dataset"])      if page == "Тяжелые хвосты распределений":         st.header("""Сгенерировать N случайных событий из распределения Фреше с функцией распределения:""")         st.latex(r'''                 F(x) = exp(-(\gamma x)^{-1/\gamma}1\{x>0\})             ''')         st.text("Для получения результата:")         st.markdown("* Сгенерируем N нормально распределенных случайных величин $U_i$ [0,1] (нулевое среднее и единичная диспресия).")         st.markdown("* Вычислим N cлучайных величин с распределением Фреше по формуле:")         st.latex(r'''                         X_i=\dfrac{1}{\gamma}\left(-lnU_i)^{-\gamma}\right)                 ''')         mu, sigma = 0, 1  # mean and standard deviation         gamma = st.slider('Желаемая гамма', 0.25, 2.25, 0.5, 0.25)         N = st.number_input("Желаемое N", 100, 10000, 10000)         U = np.abs(np.random.normal(mu, sigma, N))         X = 1 / gamma * (-np.log(U)) ** (-gamma)         X2 = X[X < 20]         fig, ax = plt.subplots()         count, bins, ignored = plt.hist(X2, 100, density=True)         plt.plot(bins,                  np.exp(- (gamma * bins) ** (-1 / gamma)) * (1 / gamma) * (gamma * bins) ** (-1 / gamma - 1) * gamma,                  linewidth=2, color='r')         st.pyplot(fig)

Кэширование данных

Каждый раз, когда пользователь нажимает что-то в интерфейсе весь скрипт выполняется заново. Это не очень хорошо, если мы скачиваем какие-то датасеты и работаем с ML моделями. Специально для этого есть две аннотации:

  • st.cache_data — используется для кеширования загружаемых датасетов.

  • st.cache_resource — используется для ML моделей.

Так, чтобы загрузить известный датасет ирисов, напишем функцию с аннотацией st.cache_data:

@st.cache_data def load_data():         if not os.path.isfile("data/iris.csv"):         # Download the Iris Fisher dataset         url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"         response = requests.get(url)         data = response.text         # Save the dataset to a local file         with open("data/iris.csv", "w") as file:             file.write(data)     # Load the dataset into a pandas DataFrame     df = pd.read_csv("data/iris.csv", header=None,                      names=["sepal_length", "sepal_width", "petal_length", "petal_width", "class"])     return df

Я поместил датсет в папку data не спроста, она нам потом понадобиться.

Отобразим его на соответствующей странице, используя st.pyplot:

    elif page == "Iris Dataset":         st.header("""Демонстрация Fisher's Iris датасета""")         df = load_data()          # Plotting the dataset         fig, ax = plt.subplots()         plt.scatter(df['sepal_length'], df['sepal_width'], c='blue', label='Iris-setosa')         plt.scatter(df['sepal_length'], df['petal_width'], c='red', label='Iris-versicolor')         plt.scatter(df['sepal_length'], df['petal_length'], c='green', label='Iris-virginica')          plt.xlabel('sepal_length')         plt.ylabel('sepal_width')          plt.title('Iris Fisher Dataset')          plt.legend()         st.pyplot(fig)

Запуск приложения

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

  1. Запустить командой streamlit run app.py, через параметр--server.port=8057 можно указать, какой порт будет слушать приложение.

    Не забываем в нашем файле указать точку входа:

    if __name__ == "__main__":     main()

  2. Для любителей запускать всего через python main.py:

    Создадим отдельный файл main.py и обозначим точку входа, где пропишем, что нужно запустить и с каким портом:

    from streamlit.web.cli import main import sys  if __name__ == "__main__":     # Set prog_name so that the Streamlit server sees the same command line     # string whether streamlit is called directly or via `python -m streamlit`.     sys.argv = ["streamlit", "run", "app.py", ""]     sys.exit(main(prog_name="streamlit"))

В итоге получаем красивый отчет:

Полученный отчет

Полученный отчет

Разворачиваем в облаке

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

  • Воспользоваться Streamlit Community Cloud — облако от создателей данного фреймворка.

  • Через хостинг у облачных провайдеров. Это довольно затратный и сложный метод для такой задачи, поэтому его опустим.

  • Разворачивание в облаке через пуш в GIT. Это такие сервисы, как Heroku и Amvera.

Amvera Cloud

  1. Переходим на страничку входа и создаем аккаунт, если его ещё нет.

  2. Нажимаем на кнопку «Создать» и вводим название проекта и тариф. Для большинтства проектов подойдет «Начальный»

    Создание проекта

    Создание проекта
  3. На ПК переходим в папку с нашим проектом. Создаем там GIT репозиторий командой git init

  4. На странице проекта находим инструкцию, как подключиться к удаленному репозиторию. Для этого нужно выполнить git remote add amvera <адрес удаленного репозитория>

  5. Генерируем инструкцию для облака, как развернуть наше приложение:

    1. Переходим на страницу генератора инструкций, которая, кстати, тоже написана с помощью streamlit.

    2. Указываем, что мы используем python, обязательно иметь файл requirements.txt, где указаны все используемые библиотеки (в том числе и streamlit).

    3. Если локально запускали все через команду streamlit run app.py, то ставим галку на «укажу свою» и прописываем эту команду.

    4. Тут ещё есть поле «Введите mountpoint», где указывается, где будут храниться данные, которые не стираются после перезапуска или остановки проекта. Так, если вы собираете какую-то статистику или собираетесь хранить данные, то их слудует класть в отдельную папку, путь до которой указывается в этом поле. Я не хочу аждый раз при перезапуске проекта скачивать заново весь датесет, поэтому положил его в папку data и указал этот путь в конфигурации.

    5. Нажимаем на кнопку Generate YAML. Добавляем полученный файл amvera.yml в корень нашего проекта.

  6. Моя папка с проектом выглядит следующим образом (venv у вас может отсутствовать)

    Папка с проектом

    Папка с проектом
  7. Заносим наши изменения в репозиторий, выполнив:

git add . git commit -m "Initial commit" git push amvera master
  1. Ждем статуса «Успешно развернуто на странице проекта

    Успешно развернутый проект

    Успешно развернутый проект
  2. Переходи по ссылке, указанной на этой же странице, у меня это https://streamlit-amvera-services.amvera.io и наслаждаемся нашим отчетом.

  3. (Опционально) В разделе «Настройки» можно привязать свое доменное имя.

Вот такими простыми действиями мы развернули наш отчет и сделали его публичным. При этом, чтобы сэкономить, можно включать и выключать проект, нажимая на символ паузы. Деньги со счета будут списываться только в то время, когда проект включен.


ссылка на оригинал статьи https://habr.com/ru/companies/amvera/articles/744952/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *