До этого момента мы в основном занимались функциональностью приложения. Мы научились загружать данные, строить графики, создавать фильтры, работать с несколькими страницами и принимать пользовательские файлы.
Но чем больше возможностей появляется в dashboard, тем чаще начинает возникать новая проблема — производительность.
На небольших таблицах это обычно незаметно. Приложение работает быстро, графики строятся мгновенно, а фильтры реагируют практически без задержек. Однако ситуация меняется, когда пользователь загружает файл на десятки или сотни тысяч строк.
В этот момент могут появиться первые неприятные симптомы:
- таблицы начинают открываться медленнее;
- графики строятся заметно дольше;
- приложение "задумывается" после каждого действия пользователя;
- интерфейс начинает казаться тяжёлым.
Очень часто причина оказывается не в Pandas и не в компьютере пользователя. Всё дело в том, как работает сам Streamlit. В этом уроке мы разберём жизненный цикл Streamlit-приложения, узнаем, что такое rerun, научимся использовать кэширование и рассмотрим несколько практических способов ускорения dashboard.
К концу урока наше приложение станет значительно более отзывчивым и сможет гораздо комфортнее работать с большими объёмами данных.
Что вы узнаете в рамках статьи
В этом уроке мы разберём внутреннюю механику работы Streamlit и научимся находить узкие места в производительности приложения.
Мы рассмотрим:
- как работает rerun;
- что происходит после взаимодействия с widget;
- жизненный цикл приложения;
- использование
st.cache_data; - уменьшение количества вычислений;
- работу с большими таблицами;
- практические методы ускорения dashboard.
Ключевые термины
- Rerun — повторный запуск Streamlit-приложения после изменения состояния интерфейса.
- Жизненный цикл приложения — последовательность этапов выполнения Streamlit-скрипта.
- Кэширование — сохранение результатов вычислений для повторного использования.
st.cache_data— механизм Streamlit для кэширования данных.- Производительность — скорость работы приложения.
- Оптимизация — набор действий, направленных на ускорение работы программы.
Что происходит после нажатия на widget
Для начала разберёмся с одной из самых важных особенностей Streamlit.
Представьте, что у вас есть такой код:
category = st.selectbox(
"Категория",
categories
)
Пользователь выбирает новое значение. Что происходит дальше? Многие новички предполагают, что обновляется только selectbox или какой-то небольшой участок страницы.
На самом деле Streamlit работает иначе. После изменения widget весь скрипт запускается заново сверху вниз. Именно это и называется rerun.
Почему rerun не является проблемой
На первый взгляд такая схема может показаться странной. Если приложение каждый раз запускается заново, почему оно вообще работает быстро? Ответ довольно простой.
Большинство Streamlit-приложений содержат относительно небольшой объём данных, выполняют несложные вычисления и быстро строят интерфейс, поэтому пользователь обычно даже не замечает повторный запуск приложения.Проблемы начинают появляться тогда, когда внутри приложения находятся тяжёлые операции.
Например:
df = pd.read_csv(
"large_dataset.csv"
)
Если такой файл содержит сотни тысяч строк, его повторное чтение после каждого изменения widget может серьёзно замедлить работу dashboard.
Жизненный цикл Streamlit-приложения
Упрощённо жизненный цикл выглядит следующим образом. Пользователь открывает приложение. Streamlit запускает весь скрипт сверху вниз. После этого пользователь взаимодействует с интерфейсом. Например, пользователь двигает slider, выбирает категорию в выпадающем списке или переключает checkbox. Затем происходит rerun, и приложение снова выполняет весь код.
Именно поэтому важно понимать, какие участки кода являются дорогими с точки зрения вычислений.
Что такое кэширование
Представьте, что приложение загружает большой CSV-файл.
df = pd.read_csv(
"sales.csv"
)
Если Streamlit будет выполнять этот код после каждого rerun, приложение начнёт тратить время на одну и ту же работу снова и снова. Здесь на помощь приходит кэширование.
Идея очень простая. Если результат вычисления уже был получен ранее и входные данные не изменились, можно использовать сохранённый результат вместо повторного выполнения кода.
Используем st.cache_data
Для кэширования данных Streamlit предоставляет декоратор:
@st.cache_data
Например:
@st.cache_data
def load_data():
return pd.read_csv(
"sales.csv"
)
df = load_data()
Теперь файл будет считан только один раз. При последующих rerun Streamlit возьмёт данные из кэша.
Почему это даёт такой большой выигрыш
Представьте файл на миллион строк. Чтение может занимать несколько секунд. Если пользователь двигает slider десять раз подряд, без кэширования приложение прочитает файл десять раз. С кэшированием чтение произойдёт только один раз. На практике это один из самых простых и эффективных способов ускорения dashboard.
Кэширование вычислений
Кэшировать можно не только загрузку данных.
Например:
@st.cache_data
def calculate_metrics(df):
return (
df.groupby("category")
["price"]
.sum()
)
Теперь дорогостоящие вычисления также будут сохраняться.
Уменьшаем количество перерасчётов
Ещё одна распространённая проблема — выполнение тяжёлых операций слишком часто.
Например:
filtered_df = df[
df["category"] == category
]
result = (
filtered_df
.groupby("product")
.sum()
)
Если одна и та же агрегация используется в нескольких местах приложения, лучше выполнить её один раз и сохранить результат.
Например:
product_stats = (
filtered_df
.groupby("product")
.sum()
)
После этого использовать готовый DataFrame во всех графиках и таблицах.
Работа с большими таблицами
Когда данные становятся действительно большими, стоит помнить несколько простых правил.
Не нужно отображать весь DataFrame целиком.
Например:
st.dataframe(df)
может работать заметно медленнее для очень больших таблиц.
Во многих случаях разумнее показать только часть данных:
st.dataframe(
df.head(100)
)
Пользователь увидит структуру данных, а приложение будет работать быстрее.
Ограничение количества данных для графиков
Графики тоже могут становиться узким местом. Например, если попытаться построить диаграмму по миллиону строк, браузеру придётся обработать огромный объём информации. На практике обычно используют агрегации, группировки и выборку части данных, чтобы уменьшить объём информации и ускорить построение визуализаций. Часто пользователю гораздо полезнее увидеть агрегированные показатели, чем миллион отдельных точек.
Использование Session State
Если данные уже были загружены ранее, их можно сохранить в session_state.
Например:
if "df" not in st.session_state:
st.session_state["df"] = (
pd.read_csv(
"sales.csv"
)
)
Такой подход помогает избежать повторной загрузки данных в рамках одной пользовательской сессии.
Практические рекомендации
Когда приложение начинает работать медленно, полезно проверить несколько вещей.
- Во-первых, нет ли повторного чтения одних и тех же файлов.
- Во-вторых, используются ли тяжёлые вычисления внутри каждого rerun.
- В-третьих, не строятся ли графики по слишком большим объёмам данных.
Во многих случаях даже небольшие изменения позволяют ускорить dashboard в несколько раз.
Наиболее частые ошибки
- Отсутствие кэширования при загрузке данных
Приложение повторно читает один и тот же файл после каждого rerun. - Многократное выполнение одинаковых вычислений
Одна и та же агрегация пересчитывается в разных частях приложения. - Отображение огромных таблиц целиком
Это создаёт дополнительную нагрузку как на серверную часть, так и на браузер пользователя. - Построение графиков по необработанным данным
Большие наборы данных часто стоит агрегировать до визуализации.
Наиболее частые вопросы
- Всегда ли нужно использовать
st.cache_data?
Нет, но для загрузки файлов и тяжёлых вычислений это обычно хорошая практика. - Можно ли кэшировать результаты работы Pandas?
Да, это один из самых распространённых сценариев использованияst.cache_data. - Почему приложение запускается заново после каждого изменения widget?
Так устроена архитектура Streamlit. Благодаря этому код остаётся простым и предсказуемым. - Нужно ли оптимизировать небольшие приложения?
Обычно нет. Оптимизация становится важной тогда, когда появляются реальные проблемы с производительностью.
Итоги
Сегодня мы разобрались с тем, как Streamlit выполняет приложения и почему после каждого изменения widget происходит rerun. Мы познакомились с жизненным циклом приложения, узнали, как работает кэширование, и научились использовать st.cache_data для ускорения загрузки данных и выполнения вычислений.
Кроме того, мы рассмотрели несколько практических подходов к работе с большими таблицами и научились уменьшать количество лишних перерасчётов. Эти знания особенно важны для реальных dashboard-проектов, где объём данных может быть значительно больше учебных примеров.
Теперь наше приложение не только умеет работать с данными, но и делает это значительно быстрее и эффективнее.