Виджеты IPython для интерактивной интерактивности Matplotlib
Я хотел бы использовать виджеты ipython для добавления некоторой степени интерактивности к строкам matplotlib.
В целом сюжет может быть довольно тяжелым, и я хочу только обновить конкретный элемент сюжета. Я понимаю, что у виджета есть встроенная встроенная функция дросселирования, которая помогает не заливать ядро, но когда график берется, пусть говорят 30 секунд, я не хочу так долго ждать, чтобы обновить строку.
Прочитав ноутбуки с примерами, я смог создать базовый пример, в котором я добавляю перекрестный курсор (управляемый двумя ползунками) к оси mpl.
Проблема заключается в том, что рисунок отображается дважды. Вот код (ячейка 1):
fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])
... фигура отображается..., ячейка 2 (редактирование: спасибо Thomas K за улучшение):
vline = ax.axvline(1)
hline = ax.axhline(0.5)
def set_cursor(x, y):
vline.set_xdata((x, x))
hline.set_ydata((y, y))
display(fig)
и, наконец, (ячейка 3):
interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01))
снова показывает фигуру с виджетами.
Итак, вопрос:
- Как я могу запретить отображение первой фигуры?
- - это правильный способ сделать это или есть лучший подход?
ИЗМЕНИТЬ
Я нашел ручку конфигурации ipython, которая, согласно этому ноутбуку, позволяет запретить отображение фигур
%config InlineBackend.close_figures = False
В то время как пример ноутбука работает, я не могу понять, как использовать этот параметр сам по себе (без класса контекстного менеджера, представленного в связанном примере), чтобы скрыть отображение фигур.
РЕДАКТИРОВАТЬ 2
Я нашел некоторую документацию настраиваемого InlineBackend.close_figures
.
РЕДАКТИРОВАТЬ 3
Запущен на ответ @shadanan, я хочу уточнить, что моя цель - добавить курсор к существующей фигуре, не перерисовывая график с нуля при каждом перемещении курсора. Объединение трех ячеек в одной ячейке:
fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])
vline = ax.axvline(1)
hline = ax.axhline(0.5)
def set_cursor(x, y):
vline.set_xdata((x, x))
hline.set_ydata((y, y))
display(fig)
interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01))
он должен "работать", но это не так. При первом запуске ячейки отображаются две цифры. После взаимодействия с виджетами отображается только одна цифра. Это "странное поведение", которое требует обходного пути, как показано в ответе @shadanan. Может ли ipython dev прокомментировать это? Это ошибка?
Ответы
Ответ 1
Решение оказывается действительно простым. Чтобы не показывать первый рисунок, нам просто нужно добавить вызов close()
перед вызовом interact
.
Вспоминая пример вопроса, ячейка, подобная этой, будет правильно отображать одну интерактивную фигуру (вместо двух):
fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])
plt.close(fig)
vline = ax.axvline(1)
hline = ax.axhline(0.5)
def set_cursor(x, y):
vline.set_xdata((x, x))
hline.set_ydata((y, y))
display(fig)
interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01))
Более чистый подход определяет функцию add_cursor
(в отдельной ячейке или script):
def add_cursor(fig, ax):
plt.close(fig)
vline = ax.axvline(1, color='k')
hline = ax.axhline(0.5, color='k')
def set_cursor(x, y):
vline.set_xdata((x, x))
hline.set_ydata((y, y))
display(fig)
interact(set_cursor, x=ax.get_xlim(), y=ax.get_ylim())
а затем вызовите его, когда мы хотим добавить интерактивный курсор:
fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])
add_cursor(fig, ax)
Ответ 2
Вы можете сделать это очень быстро, используя новый (ish) notebook
backend
%matplotlib notebook
import matplotlib.pyplot as plt
from IPython.html.widgets import interactive
fig, ax = plt.subplots()
ax.plot(range(5))
vline = ax.axvline(1, color='k')
hline = ax.axhline(0.5, color='k')
def set_cursor(x, y):
vline.set_xdata((x, x))
hline.set_ydata((y, y))
ax.figure.canvas.draw_idle()
и в отдельной ячейке:
interactive(set_cursor, x=ax.get_xlim(), y=ax.get_ylim())
Это все равно будет перерисовывать всю фигуру каждый раз, когда вы перемещаете курсор, потому что notebook
в настоящее время не поддерживает blitting (который обрабатывается на https://github.com/matplotlib/matplotlib/pull/4290)
Ответ 3
У меня есть хакерское обходное решение, которое отображает только одну фигуру. Кажется, проблема состоит в том, что в коде есть две точки, которые генерируют фигуру, и на самом деле, мы хотим только вторую, но мы не можем избежать подавления первого. Обходной путь - использовать первый для первого исполнения, второй - для всех последующих. Здесь некоторый код, который работает путем переключения между двумя в зависимости от инициализированного флага:
%matplotlib inline
import matplotlib.pyplot as plt
from IPython.html.widgets import interact, interactive, fixed
from IPython.html import widgets
from IPython.display import clear_output, display, HTML
class InteractiveCursor(object):
initialized = False
fig = None
ax = None
vline = None
hline = None
def initialize(self):
self.fig, self.ax = plt.subplots()
self.ax.plot([3,1,2,4,0,5,3,2,0,2,4])
self.vline = self.ax.axvline(1)
self.hline = self.ax.axhline(0.5)
def set_cursor(self, x, y):
if not self.initialized:
self.initialize()
self.vline.set_xdata((x, x))
self.hline.set_ydata((y, y))
if self.initialized:
display(self.fig)
self.initialized = True
ic = InteractiveCursor()
def set_cursor(x, y):
ic.set_cursor(x, y)
interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01));
Мое мнение таково, что это должно считаться ошибкой. Я попробовал его с объектно-ориентированным интерфейсом, и у него такая же проблема.