Ipython: % matplotlib inline не работает с взаимодействием ipywidgets 6.0

Созданный на 3 мар. 2017  ·  46Комментарии  ·  Источник: ipython/ipython

Похоже, что в блокноте jupyter %matplotlib inline больше не работает с новыми ipywidgets 6.0 @interact . Насколько я могу судить, встроенный бэкэнд ждет, пока ячейка полностью не завершится, прежде чем отправить сообщение display_data с изображением (см. Хук после выполнения, зарегистрированный на https://github.com/ipython/ipython/blob/7cde22957303ab53df8bd464ad5d7ed616197f31/ IPython / core / pylabtools.py # L383). В ipywidgets 6.0 мы изменили способ работы взаимодействия, чтобы быть более конкретным в отношении захватываемых нами выходных данных - мы фиксируем выходные сообщения, отправленные во время выполнения функции, и отображаем их в интерактивном выходном файле. Однако, поскольку встроенный бэкэнд matplotlib отправляет свое изображение после выполнения функции, мы просто получаем огромную серию изображений в области вывода ячейки.

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])

Эта проблема связана с началом обсуждения, чтобы увидеть, есть ли у нас способ предложить хорошее решение, которое хорошо работает со встроенным сервером (что полезно), а также хорошо работает с взаимодействием ipywidgets.

Например, если бы существовал способ сказать встроенному бэкэнду, чтобы он немедленно очистил свое изображение, а не после завершения выполнения ячейки, этого могло бы быть достаточно. Этот код работает, например ( без включения встроенного бэкенда)

import matplotlib.pyplot as plt
from ipywidgets import interact

x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

CC @SylvainCorlay , @tacaswell.

Самый полезный комментарий

Привет всем, я не следил за техническими деталями этого, но столкнулся с проблемой. Независимо от деталей реализации, я считаю чрезвычайно важным, чтобы matplotlib + interact «просто работал» в максимально возможной степени.

Все 46 Комментарий

Кажется, это работает, но есть ли у кого-нибудь решение лучше?

import matplotlib.pyplot as plt
from ipywidgets import interact
from ipykernel.pylab.backend_inline import flush_figures

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    flush_figures()

CC @minrk , который работал над встроенным сервером mpl.

Я не думаю, что мы можем что-то сделать.

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

Специальный кожух @interact нужно смывать после каждой операции?

Специальный кожух @interact, который нужно промывать после каждой операции?

Хороший момент, спасибо за предложение. Это не очень удобно (в особом случае - один конкретный бэкэнд для matplotlib). Может быть, я подожду, чтобы увидеть, является ли явная проблема для пользователей явной очистки вывода, как в приведенном выше примере. Я имею в виду, что явное лучше, чем неявное и все такое ...

Изменить: подытоживая, я думаю, я согласен с тем, что мы мало что можем (или должны) сделать ...

Оказывается, все это намного проще, чем я думал - должно быть, я сделал что-то не так, когда пытался протестировать это ранее. Нам не нужно вызывать flush_figures, достаточно просто вызвать стандартный plt.show:

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

Я думаю, что совершенно разумно просить людей вставить команду show () в свое взаимодействие, поэтому я закрываю это.

Если мы хотим сохранить это поведение (и другие потенциальные выходы после выполнения), возможно, хуки post-X могут быть явно вызваны во взаимодействии до завершения захвата выходных данных? В общем, я ожидал, что вызову функцию через interact, она будет вести себя точно так же, как если бы я вызвал функцию один раз в ячейке.

Я ожидаю, что люди будут удивлены, когда все примеры взаимодействия с matplotlib перестанут показывать цифры, поскольку «интерактивный» matplotlib обычно означает, что plt.show не нужен. Помещение его в интерактивную функцию, вероятно, не должно этого изменить.

Это немного напоминает мне требование sys.stdout.flush() для просмотра вывода до того, как был введен IOThread. Когда вы задумывались о том, как все работает, было понятно, почему это было необходимо, но это не делало его менее удивительным, потому что он вел себя не так, как люди ожидали.

Я не думаю, что мы должны выполнять все обработчики после выполнения автоматически (кто знает, что они будут делать - может быть, много всего!). Как насчет крючка flush_display ? Мы промываем его перед запуском взаимодействия и снова промываем перед его завершением. Таким образом, это точно аналогично очистке stdout / stdin.

Ну, или, с другой стороны, возможно, имеет смысл вызывать пост-выполнение, если мы думаем о взаимодействии, действительно действующем как отдельная ячейка. Но тогда мы, вероятно, захотим вызвать post-execute до того, как взаимодействие будет запущено, а также после, на всякий случай, если взаимодействие было в ячейке с другим материалом.

Повторное открытие, поскольку разговор еще не завершен ...

Мне нравится идея ловушки flush_display, и она будет работать теоретически, хотя для этого потребуются скоординированные выпуски ipython, ipykernel и ipywidgets, чтобы определить новую ловушку и переключить серверную часть на ее использование.

если мы подумаем о взаимодействии, действительно действующем как отдельная ячейка, сама по себе

Я думаю, что это ключевой момент, и это большое «если». Я так думаю об этом, но я не все (пока).

если мы думаем о взаимодействии, действительно действующем как отдельная ячейка, сама по себе
Я думаю, что это ключевой момент, и это большое «если». Я так думаю об этом, но я не все (пока).

Заблуждение в том, что если бы у нас была ячейка с кодом, затем запускалось взаимодействие, а затем еще немного кода, ячейка фактически выполнялась бы как три ячейки (то есть запуск после выполнения три раза). Я думаю, что в этом случае flush_display имеет больше смысла.

Дополнительный вопрос: будет ли работать зеркальный вывод в JLab?

Виджеты @willingc работают с зеркальным выводом.

@SylvainCorlay Я знаю, что теперь они работают, и людям в Анн-Арборе это нравится. Мой вопрос заключался в том, будет ли это работать, если будут внесены изменения, как описано выше.

Попался извините за шум.

В конечном итоге, я думаю, что подход, который должен работать лучше, - это бэкэнд виджетов ipympl для matplotlib.

Однако нам нужно будет упростить установку всего этого с помощью conf.d и т. Д.

Да, зеркальный вывод должен работать с описанными выше изменениями. Спасибо, что убедились!

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

Действительно, это было бы немного странно. Но разве не два раза? Один для ячейки в целом и один для первого вызова взаимодействия? В зависимости от того, как вы об этом думаете, это также имеет смысл: с @interact вас есть два выполнения: одно для объявления, а другое выполнение для первого вызова функции, с которой взаимодействует.

Я не думаю, что у вас было бы больше одного, если бы вы использовали, например, @interact_manual (я не уверен).

Но разве не два раза? Один для ячейки в целом и один для первого вызова взаимодействия?

Рассмотрим этот код:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

Поскольку построение является кумулятивным, и вы хотите рассматривать взаимодействие как отдельную ячейку, вам нужно очистить фигуру до запуска взаимодействия, затем очистить фигуру внутри взаимодействия, а затем еще раз в конце ячейки. Это именно то, что мы делаем и с промывкой стандартного вывода.

На самом деле, я не уверен, чего ожидает пользователь в приведенном выше примере. Чего бы вы ожидали?

Просто подумайте: приведенный выше пример не очень логичен; лично я ожидал бы трех графиков, из которых только один изменяется динамически, но если бы я сделал что-то подобное, я бы сделал все вызовы графика внутри @interact .

Есть ли причина не вызывать flush_figures после каждого .plot() ?

@myyc - это существенное смысловое изменение поведения. plot добавляет элементы на сюжет ....

Я имел в виду конкретное встроенное поведение ipython , а не matplotlib.

edit : Я уверен, что это более распространенный вариант использования, когда одно взаимодействие на ячейку с этим является «единственной функцией построения графика», в отличие от случая, описанного выше ...

Я понимаю. Хотя не думаю, что у этого должно быть такое поведение.

Правильный подход в будущем, вероятно, будет заключаться в использовании серверной части ноутбука или ipympl и редактировании рисунка.

Реализация специфичных для matplotlib обходных путей для встроенного бэкэнда во взаимодействии ipywidgets не кажется правильной.

В коде, данном Джейсоном:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

ожидаемый результат - это фигура с множеством линий ..

Да, полностью согласен. Я временно исправил это таким образом (переопределив методы построения панд) в моей локальной настройке, «в ожидании исправления» :) Не обращайте на это внимания, это вызывает кучу ужасных побочных эффектов.

Я видел, как эта проблема возникала во множестве проектов github, поэтому мне интересно, каким должен быть лучший путь разрешения.

edit : почему бы тогда не написать это так, более явным образом?

@interact(n=(1,10))
def f(n):
    plt.plot([1,2,3])
    plt.plot([1,n])
    plt.plot([4,5,6])

Вы можете проверить пакет ipympl (https://github.com/matplotlib/jupyter-matplotlib), который является виджетом matplotlib. ipympl все еще находится на довольно ранней стадии, но должен иметь более быстрый график выпуска, чем ядро ​​matplotlib, и более раннюю поддержку jupyterlab и т. д.

Хорошо, оказывается, что переопределение - ужасная идея. Я с нетерпением жду исправления в апстриме :)

Просто чтобы выразить точку зрения необразованного пользователя: после обновления до ipywidgets 6.0 я обнаружил, что большая часть кода моего виджета не работает (из-за проблемы, обсуждаемой здесь). Мне удалось исправить это, добавив вызовы к plt.show() , но я также обнаружил, что мне нужно быть очень осторожным с тем, куда я добавляю такие вызовы; кажется, что иногда вызов этого слишком рано приводит к тому, что более поздние графики не отображаются (даже если plt.show() вызывается снова). Я не изучил это достаточно, чтобы создать минимальный пример, но текущее поведение кажется мне не интуитивно понятным - я программирую методом проб и ошибок, чтобы обойти это.

Какое именно изменение в ipywidgets на самом деле привело к новому поведению? Перехватчики после выполнения не вызываются вообще, или они все еще вызываются, но их вывод теперь отбрасывается, потому что виджет точно определяет, какие выводы принадлежат виджету? Если это последнее, то кажется, что изолировать захваченный вывод в теле функции - неправильный поступок.

Вызываются хуки после выполнения (я почти уверен) - вы можете увидеть это, выполнив:

import matplotlib.pyplot as plt
from ipywidgets import interact
%matplotlib inline

@interact(n=(0,10))
def f(n):
    plt.plot([0,n])

и перемещая ползунок. Графики будут добавлены к ячейке - обработчики после выполнения отправляют сообщения с отображаемыми данными. Выполнения взаимодействия действительно касаются только захвата вывода, происходящего во время вызова функции. Я считаю, что это правильно. Например, два разных взаимодействия могут использовать один и тот же экземпляр слайдера:

import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider
%matplotlib inline

x = IntSlider()
@interact(n=x)
def f(n):
    plt.plot([0,n])
    plt.show()
@interact(n=x)
def f(n):
    plt.plot([0,n,0,n])
    plt.show()

Когда один ползунок настраивается (в любом пользовательском интерфейсе), оба взаимодействия будут показывать перемещение ползунка, и обе функции взаимодействия будут выполняться в одном и том же вызове выполнения. Было бы неправильно, если бы оба взаимодействия пытались захватить какой-либо вывод хука после выполнения. Я думаю, что недостаток здесь в том, что мы используем пост-выполнение для генерации вывода, а это слишком грубо. У нас должен быть более детализированный метод сброса вывода, сгенерированного в блоке кода, который можно вызывать несколько раз за одно выполнение.

У нас должен быть более детальный метод очистки вывода, сгенерированного в блоке кода.

Для построения графика matplotlib эта промывка происходит при выполнении plt.show

Я должен быть очень осторожен с тем, куда я добавляю такие вызовы; кажется, что иногда вызов этого слишком рано приводит к тому, что более поздние сюжеты не появляются

Мне действительно интересно увидеть примеры, когда это происходит. Мы хотим сделать поведение интуитивным и понятным.

Я должен быть очень осторожен с тем, куда я добавляю такие вызовы; кажется, что иногда вызов этого слишком рано приводит к тому, что более поздние сюжеты не появляются

Мне действительно интересно увидеть примеры, когда это происходит. Мы хотим сделать поведение интуитивным и понятным.

Чтобы уточнить, я думаю, вы сможете получить эквивалент предыдущего поведения, просто добавив plt.show() в качестве последней строки любой функции взаимодействия, которая выполняет построение. Мне очень интересно увидеть примеры, когда это не работает.

@jasongrout Спасибо, что посмотрели на это. Код, в котором я это делал, довольно сложен, и, возможно, я делал что-то еще не так. Если я увижу его снова, я постараюсь выделить его и привести пример.

@jasongrout После того, как я немного plt.show() ).

Если создать график в ячейке записной книжки и вызвать plt.show() , то более поздние модификации рисунка не появятся. Вот минимальный пример:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

Появляется только первая линия графика.

Вот еще более простой пример. Две ячейки без бэкэнда %matplotlib inline . Возможно, @tacaswell может пролить свет на то, почему второй plt.show не показывает сюжет.

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,1)
ax.plot([0,1],[0,1])
plt.show()

показывает сюжет

ax.plot([1,0],[0,1])
plt.show()

ничего не показывает

Подтверждено на моей машине с последними стабильными jupyter, ipython, ipywidgets, matplotlib, python 3.

Подтверждено и здесь.

@jasongrout Это ожидаемое поведение, потому что в первой ячейке вы создаете фигуру (а также холст и оси), а затем отображаете ее. Затем во второй ячейке вы добавляете линию к существующим осям (на существующем рисунке). С помощью %matplotlib qt или ipympl (или любого из полных графических интерфейсов пользователя) график _должен_ быть обновлен со второй строкой. Я не ожидаю, что это будет работать со встроенным, поскольку у вас есть только один выстрел для его обновления (вот почему я очень не фанат встроенного, он выбрасывает _все_ интерактивные функции).

@tacaswell Имеет ли

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

Я думаю, что на plt.show встроенный бэкэнд отображает себя в png, а затем отменяет регистрацию фигуры из pyplot, поэтому она не будет отображаться снова на следующем шоу, даже если она находится в той же ячейке, однако несколько вызовов методов осей без дополнительных show s должны работать.

Для ясности, вот встроенный бэкэнд-код: https://github.com/ipython/ipykernel/blob/master/ipykernel/pylab/backend_inline.py

Есть ли версия ipywidgets и / или matplotlib я могу вернуться, чтобы мои интерактивные виджеты снова заработали?

@jasongrout Судя по коду, я мог бы установить InlineBackend.close_figures равным False . Но я не уверен, как установить этот атрибут - мне нужно использовать какую-то магию?

(этот вопрос звучит забавно, но я имею в виду буквально)

https://github.com/jupyter-widgets/ipywidgets/issues/1457 снова поднял эту проблему. @ellisonbg - что, если мы реализуем обходной путь flush_figures, о котором я упоминал выше, - вызовите flush_figures до и после выполнения взаимодействия, чтобы очистить любые графики перед взаимодействием и очистить все, что было построено во время взаимодействия? Я думаю, нам, возможно, придется проверить, работаем ли мы во встроенном бэкэнде mpl.

Это огромный кладж, но, возможно, он все равно достаточно важен для этого. Возможно, мы также сможем определить, действительно ли мы сбрасываем фигуру, и выдаем предупреждение, предлагающее людям выполнить явный вызов show ()?

Привет всем, я не следил за техническими деталями этого, но столкнулся с проблемой. Независимо от деталей реализации, я считаю чрезвычайно важным, чтобы matplotlib + interact «просто работал» в максимально возможной степени.

С plt.show() или flush_figures() , если записная книжка обновлена ​​(без перезапуска ядра), цифра пропадает даже при сохранении записной книжки с виджетами. Без этих смягчений это не так, хотя тогда все еще существует проблема множественных выходных данных. Это предназначено? То же самое применимо, если ядро ​​выключено, тогда как в старых версиях ipywidgets окончательное состояние рисунка будет сохранено в записной книжке, как неинтерактивный график, что было весьма удобно.

Извините, если это выходит за рамки или я делаю что-то не так. Запуск ipywidgets 6.0.0, matplotlib 2.0.2, notebook 5.0.0 и python 3.6.1.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги