Streamlit

Егор Горбань — Data Engineer

Streamlit: Overview

Streamlit is an open-source Python framework for data scientists and AI/ML engineers to deliver dynamic data apps with only a few lines of code…

Streamlit: Essential

  1. Приложения Streamlit - обычные python скрипты.
  2. Каждый раз, когда приложение открывается в браузере, начинается новая сессия и скрипт исполняется целиком.
  3. В процессе исполнения скрипта Streamlit в лайв-формате отображает его output в браузере.
  4. Каждый раз когда пользователь взаимодействует с приложением и изменяет значение виджета, скрипт исполняется заново и Streamlit отрисовывает приложение с использованием нового значения виджета.
  5. Streamlit позволяет использовать кэширование чтобы не пересчитывать значения тяжёлых функций при каждом рендере.
  6. Streamlit позволяет использовать session state чтобы сохранять состояние сессии при перезапусках.
  7. Streamlit позволяет создавать multipage application: каждый .py файл в папке pages - отдельная страница.

Пример

import pandas as pd
import streamlit as st

st.title("Hello from streamlit")

df = pd.read_csv("examples/data/sales.csv")
st.dataframe(df.sample(2))

Результат выполнения:

Development flow

  • Запуск приложения:
    1. streamlit run your_script.py [-- script args];
    2. python -m streamlit run your_script.py - эквивалентно;
    3. streamlit run https://raw.githubusercontent.com/<...> или python -m ...;
  • Каждый раз когда изменяется исходный код, streamlit определяет это и может автоматически перезапускать приложение в браузере. Для этого нужно выбрать опцию “Always rerun”.
  • Процесс разработки:
    1. Изменить исходный код
    2. Сохранить файл
    3. Посмотреть отображение в браузере
    4. ✨Repeat untill happy✨

Data flow

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

Может можно иначе?

Data flow: дополнительные возможности

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

    my_container = st.container()
    
    st.write("a") # --- 3
    st.write("b") # --- 4
    
    my_container.write("Inside container") # --- 1
    my_container.write("Also inside container") # --- 2
  • Форма - составной элемент интерфейса, который считается заполненным, только когда заполнены все его элементы. Позволяет сгруппировать элементы в единый логический элемент и таким образом перезапускать скрипт только после заполнения всех элементов формы.

    with st.form("my_form"):
        name = st.text_input("Name")
        email = st.text_input("Email")
        st.form_submit_button("Submit")
  • Фрагмент - обособленный участок кода, который может перезагружаться отдельно от основного скрипта.

    • Есть возможность обновлять фрагмент по заданному расписанию с помощью параметра run_every

      (принимает float (seconds) | str (Pandas Timedelta constructor) | datetime.timedelta()).

    • Подробнее здесь

    • ...
      
      @st.experimental_fragment(run_every="10s")
      def fragment():
              ...
              st.button("Rerun fragment manually")
      ...
      fragment()
      ...

Data flow: дополнительные возможности

  • Callback-функции - функции, которые передаются в виджеты через параметры on_change или on_click. Позволяют управлять порядком выполнения кода, так как код в callback-функциях выполняется перед отрисовкой всего скрипта.

    def my_callback():
        ...
    
    checkbox_input = st.checkbox(
        "Yes or No",
        on_change=my_callback,
    )
  • Кеширование. Любую функцию можно дополнить декоратором @st.cache_data, тогда при её вызове вычисления проводиться по возможности не будут и значение будет взято из кэша.

  • Состояние сессии - st.session_state, dictionary-like интерфейс. Позволяет управлять значениями объектов в пределах одной сессии.

Advanced: Кеширование

https://docs.streamlit.io/develop/concepts/architecture/caching

  • Применяется к функции: для набора аргументов сохраняется результат выполнения
  • Используется модуль pickle
  • Два вида кеширования, какое использовать можно посмотреть в таблице в конце раздела

Advanced: Кеширование

  • Кеширование данных: DataFrame, np.ndarray, str, int, float, list, etc.
    • Cоздаётся словарь, где ключ - набор аргументов, а значение - результат выполнения функции на этом наборе.

    • При использовании значения функции помеченной декоратором, создаётся копия закешированного объекта.

      @st.cache_data
      def transform(df):
          df = df.filter(items=['one', 'three'])
          df = df.apply(np.sum, axis=0)
          return df
  • Кеширование ресурсов: несериализуемые объекты – подключения к БД, ML-pipelines
    • Возвращается не копия, а сам объект, то есть singleton: глобальный изменяемый объект.

    • При изменении объекта, изменятся значения всех обращающихся к нему переменных.

    • Bad example

      from transformers import pipeline
      model = pipeline("sentiment-analysis")
    • Good example

      from transformers import pipeline
      
      @st.cache_resource
      def load_model():
          return pipeline("sentiment-analysis")
      
      model = load_model()

Advanced: Кеширование

  • Дополнительно
    • func.clear() | st.cache_data.clear() – очистить кеш
    • ttl: time-to-live – время жизни
    • max_entries - максимальное количество хранящихся закешированных объектов
    • persist=True | persist='disk' - сохраняет результаты кеширования на диск (~/.streamlit/cache/): pickle-объекты
    • _some_arg: некешируемый аргумент в функции.

Advanced: Cостояние сессии

https://docs.streamlit.io/develop/concepts/architecture/session-state

  • st.session_state – dictionary-like интерфейс. Позволяет управлять значениями объектов в пределах одной сессии.
  • Сессия длится в пределах одной вкладки браузера, до тех пор пока пользователь не перезагрузит страницу.
  • Значения хранятся на сервере, а не в браузере.

Values your app stores in session_state are NOT sent to the browser or otherwise available client-side in any way. These only live on the server

Advanced: Cостояние сессии

  • В словарь состояния сессии также попадают значения виджетов, если при их создании передать параметр key.

    def my_callback():
            st.session_state["key"] = "value"
    
    checkbox_input = st.checkbox(
            "Yes or No",
            key="my_checkbox_key",
            on_change=my_callback,
    )
    
    st.write(st.session_state)
  • Можно заранее создать ключ в словаре сессии, тогда созданный после элемент примет указанное значение как дефолтное.

    if "my_slider" not in st.session_state:
            st.session_state.my_slider = 8
    
    st.slider(
            "Slider",
            min_value=0,
            max_value=20,
            key="my_slider",
    )

Что можно отрисовать?

Display almost anything

Отрисовать данные

API Reference

  • Text elements: Markdown, LaTeX, Code, etc.
  • Data elements: DataFrame 1, Data editor, Metric, Json, etc.
  • Chart elements: area, bar, line, scatter charts; maps; объекты Altair, Matplotlib, Plotly, GraphViz, etc.
  • Media: image/gallery, audio, video

Отрисовать виджеты

API Reference

  • Widgets: buttons, form buttons, checkbox, selectbox, slider, toggle,
  • Inputs areas: text, date, time, chat, file uploader
  • Statuses: progress bar, spinner, success/info/warning/error boxes
  • Chat elements: chat input form, chat messages, typewriter effect

ChatGPT-like App: Туториал

Вёрстка и контейнеры

Элементы на странице можно упаковать в контейнеры. Это помогает выстроить более комфортный user-experience, предоставляя пользователю удобную и понятную навигацию внутри приложения.

Более подробно здесь

  • st.columns - разделение элементов по колонкам



  • st.container - группировка элементов



  • st.empty - может содержать только один элемент



  • st.expander - раскрывающийся контейнер



  • st.popover - всплывающий контейнер появляющийся при наведении на элемент



  • st.tabs - позволяет разделить несколько контейнеров и перемещаться между ними с помощью tab-панели



  • st.sidebar - отображает элементы на боковой панели

Конфигурация приложения

  • Как конфигурировать:

    • Передать параметры при запуске через cli при запуске;
    • Использовать файл с настройками config.toml;
  • Местоположение файла:

    • Глобальный файл конфигурации: ~/.streamlit/config.toml (Linux, MacOS) или %userprofile%\.streamlit\config.toml (Windows);
    • Локальный файл конфигурации: ./.streamlit/config.toml;
  • Как проверить текущие и доступные настройки приложения:

    streamlit config show
  • Пример конфигурационного файла

    [server]
    port = 8503
    fileWatcherType = "none"
    maxUploadSize = 2048
    
    [global]
    developmentMode = false
    
    [browser]
    gatherUsageStats = false

Secrets

  • файл с секретами secrets.toml

    db_username = "Jane"
    
    [section]
    user = "Egor"
  • Используются с помощью dict-like интерфейса st.secrets

    st.secrets["db_username"] # Jane
    
    st.secrets["section"]["user"] # Egor
  • Местоположение файла:

    1. Глобальный файл с секретами: ~/.streamlit/secrets.toml (Linux, MacOS) или %userprofile%\.streamlit\secrets.toml (Windows)
    2. Локальный файл с секретами: ./.streamlit/secrets.toml

Connections

  • st.connection("sql") (python) соотносится с секцией [connections.sql] (toml) в файле с секретами.

  • st.connection("my_connection", type="sql") (python) соотносится с секцией [connections.my_connection] (toml) в файле с секретами.

  • Доступны: sql, snowflake. Можно создать своё подключение к любому источнику, подробнее (не рекомендуется)

  • .streamlit/secrets.toml:

    [connections.my_db]
        dialect = "mysql"
        username = "myuser"
        password = "password"
        host = "localhost"
        database = "mydb"
  • Приложение Streamlit:

    conn = st.connection("mydb", type="sql", autocommit=True)

Развертывание

Туториал: бесплатный deployment на серверах streamlit.

Шаги

  1. Перейти на сайт https://share.streamlit.io
  2. Зарегистрироваться в streamlit-community
  3. Загрузить приложение в GitHub в публичный репозиторий
  4. Подвязать в интерфейсе нужный репозиторий, ветку и путь до файла с приложением
  5. Opt: добавить секреты через веб-интерфейс
  6. Done

Сторонние компоненты

https://streamlit.io/components

https://streamlit.io/generative-ai - примеры интересных AI приложений

Вывод

  • Преимущества
    • Хорошо подходит для создания прототипа
    • Удобно использовать одним разработчиком: не требует знаний дополнительных инструментов и языков
    • Кеширование, хранение состояния сессии, навигация с помощью контейнеров, отображение интерактивных элементов, и прочие возможности, улучшающие пользовательский опыт.
    • Большое количество поддерживаемых типов данных и интерактивных виджетов.
  • Недостатки:
    • Плохо масштабируется
    • Перерасчёт кода при каждом перезапуске
    • Негибкое выделение сессий
    • Ограниченные визуал/функциональность