14 June 2020

Юпитер и интерактивность - темная строна

Давайте теперь попробуем вывести уже два графика. Для того, чтобы разместить их на одном полотне, используется функция sublots, которая выделает под это области для каждого графика. Вычисления мы тоже немного усложним - добавим данные для второго графика в y2

# глобальные x, y и y2
x, y, y2  = [], [], []

# вычисляем их в отдельной функции с двумя параметрами
def calc(f, phase):
    global x,y,y2
    x = np.linspace(0, 10, 100)
    y = np.sin(f * x + phase)
    y2 = np.cos(f * x + phase)

   
Также усложнится функция рисования

# функция рисивания с двумя параметрами
# перед рисованием она вызывает функцию calc для обновления данных
def plot_f(f, phase):
    global x,y,y2
    calc(f, phase)
   
    # само рисование
   
    # создаем две области рисования по вертикали в одной колонке
    # сами области будут доступны из списка ax
    # делаем высоту ( 4 ) поменьше
    fig, ax = plt.subplots(2,1,figsize=(16,4), dpi= 80)
   
    # добавляем к первой области сетку
    ax[0].grid(True,c='black')
    # рисуем первую область
    ax[0].plot(x,y, color='tab:red', marker='D')
   
    # добавляем ко второй области сетку
    ax[1].grid(True,c='black')
    # рисуем первую область
    ax[1].plot(x,y2, color='tab:blue', marker='D')

   
Обработчик кнопки остается тем же самым - мы поменяли функцию рисования.
И вот тут начинаются чудеса.
При первом вызове мы видим два нужных графика, но как уже
видно на снимке - график появился непосредственно за виджетам, а не
в outt



И вот уже второй вызов просто дает нам еще одну пару графиков там же



Все... приехали... - интерактивность совершенно разрушилась.
Для примеров я просмотрел и вот этут статью
Ipywidgets with matplotlib

Однако тот подход тоже не помогает.
В самом деле - давайте попробуем создать графики в глобальном пространстве, а потом использовать замену данных в функции рисования
Поэтому сначала создадим outt

outt = widgets.Output()

А потом нарисуем в глобальном пространстве с его указанием

with outt:
    fig, ax = plt.subplots(2,1,figsize=(16,4), dpi= 80)
   
    ax[0].grid(True,c='black')
    ln, = ax[0].plot(x,y, color='tab:red', marker='D')
   
    ax[1].grid(True,c='black')
    ln2, = ax[1].plot(x,y2, color='tab:blue', marker='D')


Так вот, графики уже нарисуются после этого вызова, разумеется пустые - мы же ни разу функцию вычисления не дергали и списки - пустые


Теперь поменяем функцию рисования - пусть она будет ставить данные на существующие графики, для начала на один

def plot_f(f, phase):
    global x,y,y2, ax, ln, fig
    calc(f, phase)
    ln.set_xdata(x)
    ln.set_ydata(y)
    plt.show()

   
Меняем значения и нажимаем кнопку - и ... ничего не происходит. Новых графиков не пояляется, а на старом ничего не изменилось. При этом данные присутствуют - достаточно набрать в отдельной ячейке y и убедиться в этом.

Даже пример с той ссылки работает некорректно


Как можно видеть -  график появился не после output, а сразу за выполеным кодом.
Если же попробовать выполнить магическую команду %matplotlib widget, упомянутую в статье, то она свалится с ошибкой


Суть которой в конце такая: ModuleNotFoundError: No module named 'ipympl'

В статье указан такой список:
Python 3.8.1
matplotlib 3.1.3
NumPy 1.18.1
ipywidgets 7.5.1
ipympl 0.4.1


Запустим conda list прямо в Юпитере и смотрим что у меня:
python   3.7.6  h0371630_2
matplotlib  3.1.3  py37_0
numpy 1.18.1 py37h4f9e942_0
ipywidgets 7.5.1 py_0
...И нет ipymp
l

В общем, ipympl сразу в дистрибутиве не идет и надо его ставить отдельно.
Но к тому времени, как я убил много попыток на то, чтобы заставить это
работать, я пришел к другому решению - поставил plotly

No comments:

Post a Comment