Функция interactive
на самом деле не дает вам доступа к этому уровню детализации. Он всегда запускает весь scatterplt
обратный вызов. По сути, смысл interactive
состоит в том, чтобы сделать класс проблем действительно легким - как только вы выйдете из этого класса проблем, это не очень применимо.
Затем вам придется вернуться к остальной части механизма виджетов. Поначалу это может быть немного трудно понять, поэтому, чтобы минимизировать скачок, я начну с объяснения того, что interactive
делает под капотом.
Когда вы вызываете interactive(func, widget)
, он создает widget
и связывает обратный вызов с каждым изменением widget
. Обратный вызов выполняется func
в виджете Output
( docs ). Виджет Output
захватывает весь вывод func
. interactive
затем упаковывает widget
и выходной виджет в VBox
(контейнер для размещения виджетов).
Вернуться к тому, что вы хотите сделать сейчас. Ваша заявка имеет следующие критерии:
- нам нужно поддерживать некоторую форму внутреннего состояния: приложение должно помнить координаты x и y случайных переменных
- нам нужно другое поведение для запуска в зависимости от того, какой слайдер был запущен.
Чтобы удовлетворить (1), нам, вероятно, следует создать класс для поддержания состояния. Чтобы удовлетворить (2), нам нужны различные обратные вызовы для запуска в зависимости от того, как был назван слайдер.
Кажется, что-то подобное делает то, что вам нужно:
import numpy as np
import ipywidgets as widgets
import matplotlib.pyplot as plt
class LinRegressDisplay:
def __init__(self, rand=3.0, num_points=20, slope=1.0):
self.rand = rand
self.num_points = num_points
self.slope = slope
self.output_widget = widgets.Output() # will contain the plot
self.container = widgets.VBox() # Contains the whole app
self.redraw_whole_plot()
self.draw_app()
def draw_app(self):
"""
Draw the sliders and the output widget
This just runs once at app startup.
"""
self.num_points_slider = widgets.IntSlider(
value=self.num_points,
min=10,
max=50,
step=5,
description='Number of points:'
)
self.num_points_slider.observe(self._on_num_points_change, ['value'])
self.slope_slider = widgets.FloatSlider(
value=self.slope,
min=-1,
max=5,
step=0.1,
description='Slope:'
)
self.slope_slider.observe(self._on_slope_change, ['value'])
self.rand_slider = widgets.FloatSlider(
value=self.rand,
min=0,
max=50,
step=3,
description='Randomness:', num_points=(10, 50, 5)
)
self.rand_slider.observe(self._on_rand_change, ['value'])
self.container.children = [
self.num_points_slider,
self.slope_slider,
self.rand_slider ,
self.output_widget
]
def _on_num_points_change(self, _):
"""
Called whenever the number of points slider changes.
Updates the internal state, recomputes the random x and y and redraws the plot.
"""
self.num_points = self.num_points_slider.value
self.redraw_whole_plot()
def _on_slope_change(self, _):
"""
Called whenever the slope slider changes.
Updates the internal state, recomputes the slope and redraws the plot.
"""
self.slope = self.slope_slider.value
self.redraw_slope()
def _on_rand_change(self, _):
self.rand = self.rand_slider.value
self.redraw_whole_plot()
def redraw_whole_plot(self):
"""
Recompute x and y random variates and redraw whole plot
Called whenever the number of points or the randomness changes.
"""
pcent_rand = self.rand
pcent_decimal = pcent_rand/100
self.x = [
n*np.random.uniform(low=1-pcent_decimal, high=1+pcent_decimal)
for n in np.linspace(3, 9, self.num_points)
]
self.y = [
n*np.random.uniform(low=1-pcent_decimal, high=1+pcent_decimal)
for n in np.linspace(3, 9, self.num_points)
]
self.redraw_slope()
def redraw_slope(self):
"""
Recompute slope line and redraw whole plot
Called whenever the slope changes.
"""
a = np.linspace(0, 9, self.num_points)
b = [(self.slope * n) for n in a]
self.output_widget.clear_output(wait=True)
with self.output_widget as f:
plt.figure(figsize=(9, 9), dpi=80)
plt.ylim(ymax=max(self.y)+1)
plt.xlim(xmax=max(self.x)+1)
plt.scatter(self.x, self.y)
plt.plot(a, b)
plt.show()
app = LinRegressDisplay()
app.container # actually display the widget
В качестве последнего примечания, анимация остается немного неприятной, когда вы перемещаете ползунки. Для лучшей интерактивности я предлагаю взглянуть на bqplot . В частности, у Чакри Черукури есть отличный пример линейной регрессии , который чем-то похож на то, что вы пытаетесь сделать.