2025, Dec 13 06:01
Почему set_ylim в Shiny for Python не опускает ось y до нуля и что делать
Разбираем, почему в Shiny for Python + Matplotlib ось y в столбчатой диаграмме игнорирует set_ylim(bottom=0), и показываем рабочий способ исправить данные и оси.
Зафиксировать нулевую отметку по оси y в столбчатой диаграмме Matplotlib внутри Shiny for Python на первый взгляд просто: достаточно вызвать set_ylim(bottom=0). Однако иногда ось упрямо начинается с первого значения, например 4.1, и игнорирует ваши границы. Обычно это признак того, что график получает данные не в том виде, в каком вы ожидаете.
Проблема: ось y не начинается с нуля
Ниже приведён минимальный пример на Shiny Express, в котором проявляется эта ситуация. Диаграмма строится, но ось y стартует примерно с 4.1, а set_ylim(bottom=0) визуально ничего не меняет.
from shiny.express import input, render, ui
import matplotlib.pyplot as plt
import numpy as np
series_map = {
"Maturity": ["1Y", "2Y", "3Y", "4Y", "5Y", "6Y", "7Y", "8Y"],
"Yield": [4.1, 4.3, 4.5, 4.7, 4.8, 4.9, 5.0, 5.1],
}
bundle = np.array([series_map["Maturity"], series_map["Yield"]])
print(bundle[1])
def make_bars():
x_vals = bundle[0]
y_vals = bundle[1]
fig, ax = plt.subplots()
ax.bar(x_vals, y_vals)
ax.set_xlabel("Maturity")
ax.yaxis.set_label_text("Yield (%)")
ax.set_ylim(bottom=0)
return fig
@render.plot
def show_chart():
return make_bars()
Почему кажется, что set_ylim игнорируется
С самой командой для осей всё в порядке. Неожиданное поведение связано с тем, как набор данных обрабатывается перед построением. Преобразуя исходную структуру в единый массив, где перемешаны подписи и числовые значения, вы лишаете этап построения возможности видеть по оси y простую числовую последовательность. В итоге меняется расчёт осей, и установка границ по y перестаёт работать.
Решение: избегайте преобразования в массив
Оставьте структуру данных простой и передавайте в Matplotlib подписи по x и числовые значения по y напрямую. На практике отказ от объединения в массив устраняет проблему с осями.
from shiny.express import render
import matplotlib.pyplot as plt
curve = {
"1Y": 4.1, "2Y": 4.3, "3Y": 4.5, "4Y": 4.7,
"5Y": 4.8, "6Y": 4.9, "7Y": 5.0, "8Y": 5.1,
}
@render.plot
def draw_bars():
cats = list(curve.keys())
vals = list(curve.values())
fig, ax = plt.subplots()
ax.bar(cats, vals)
ax.set_xlabel("Maturity")
ax.yaxis.set_label_text("Yield (%)")
ax.set_ylim(bottom=0)
return fig
Почему это важно
В реактивных дашбордах даже небольшие преобразования данных могут незаметно изменить то, как библиотеки построения графиков интерпретируют входные значения. Как только структура перестаёт быть простой парой «подписи + числовой ряд», операции с осями могут вести себя не так, как вы рассчитываете. Сохраняйте предсказуемость пайплайна — это самый простой способ гарантировать, что настройки графика, вроде границ по оси y, применяются стабильно.
Выводы
Если set_ylim(bottom=0) не опускает базовую линию к нулю в столбчатой диаграмме Shiny for Python + Matplotlib, проверьте путь данных. Передавайте в Matplotlib обычные списки: категориальные подписи по x и числовые значения по y, не сворачивайте их в единый массив. Этот небольшой шаг возвращает оси ожидаемое поведение и делает график корректным.