Jupyter (IPython) notebook 中的交互式绘图,带有可拖动点,拖动时调用 Python 代码
Interactive plots in Jupyter (IPython) notebook with draggable points that call Python code when dragged
我想在 Jupyter notebook 中制作一些交互式绘图,其中绘图中的某些点可以由用户拖动。然后,这些点的位置应该用作 Python 函数(在笔记本中)更新绘图的输入。
这里已经完成了这样的事情:
http://nbviewer.ipython.org/github/maojrs/ipynotebooks/blob/master/interactive_test.ipynb
但回调是 Javascript 函数。在某些情况下,更新情节的代码需要非常复杂,并且需要很长时间才能在 Javascript 中重写。如有必要,我愿意在 Javascript 中指定可拖动点,但是是否可以回调 Python 来更新情节?
我想知道像 Bokeh 或 Plotly 这样的工具是否可以提供此功能。
tl;dr - Here's a link to the gist showing update-on-drag.
要做到这一点,您需要知道:
- 如何与 Jupyter Javascript 的 IPython 内核交互
前端。现在是通过
Jupyter.Kernel.execute
(current
源代码 ).
- 够d3.js舒服了。 (喜欢用屏幕绘制坐标转换。)
- 您选择的 d3-via-Python 库。 mpld3 对于此示例。
mpld3 有其 own plugin for draggable points and capability for a custom mpld3 plugin. But right now there is no feature to redraw the plot on update of data;维护者说现在最好的方法是在更新时删除并重新绘制整个图,或者真正深入到 javascript.
Ipywidgets 是,就像你说的(据我所知),一种在使用 IPython 内核,所以不是你想要的。但比我提议的要容易一千倍。 ipywidgets github repo 的 README links to the correct IPython notebook to start with 在他们的示例套件中。
关于 Jupyter notebook 与 IPython 内核直接交互的最佳博客 post 来自 Jake Vanderplas in 2013. It's for IPython<=2.0 and commenters as recent as a few months ago (August 2015) posted updates for IPython 2 and IPython 3,但代码不适用于我的 Jupyter 4 notebook。
问题似乎是 javascript API for the Jupyter kernel 在不断变化。
我在要点中更新了 mpld3 dragging example 和 Jake Vanderplas 的示例(link 位于此回复的顶部)以提供尽可能简短的示例,因为这已经很长了,但是下面的片段试图更简洁地传达这个想法。
Python
Python 回调可以有任意多的参数,甚至可以是原始代码。内核将通过 eval
语句 运行 它并发回最后的 return 值。输出,无论是什么类型,都将作为字符串 (text/plain
) 传递给 javascript 回调。
def python_callback(arg):
"""The entire expression is evaluated like eval(string)."""
return arg + 42
Javascript
Javascript 回调应该有一个参数,即 Javascript
Object
遵循结构 documented here.
javascriptCallback = function(out) {
// Error checking omitted for brevity.
output = out.content.user_expressions.out1;
res = output.data["text/plain"];
newValue = JSON.parse(res); // If necessary
//
// Use newValue to do something now.
//
}
使用函数 Jupyter.notebook.kernel.execute
从 Jupyter 调用 IPython 内核。发送到的内容
内核是 documented here.
var kernel = Jupyter.notebook.kernel;
var callbacks = {shell: {reply: javascriptCallback }};
kernel.execute(
"print('only the success/fail status of this code is reported')",
callbacks,
{user_expressions:
{out1: "python_callback(" + 10 + ")"} // function call as a string
}
);
mpld3 插件中的 Javscript
修改 mpld3 库的插件以添加一个独特的 class
HTML 个要更新的元素,以便我们可以在
未来。
import matplotlib as mpl
import mpld3
class DragPlugin(mpld3.plugins.PluginBase):
JAVASCRIPT = r"""
// Beginning content unchanged, and removed for brevity.
DragPlugin.prototype.draw = function(){
var obj = mpld3.get_element(this.props.id);
var drag = d3.behavior.drag()
.origin(function(d) { return {x:obj.ax.x(d[0]),
y:obj.ax.y(d[1])}; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
// Additional content unchanged, and removed for brevity
obj.elements()
.data(obj.offsets)
.style("cursor", "default")
.attr("name", "redrawable") // DIFFERENT
.call(drag);
// Also modify the 'dragstarted' function to store
// the starting position, and the 'dragended' function
// to initiate the exchange with the IPython kernel
// that will update the plot.
};
"""
def __init__(self, points):
if isinstance(points, mpl.lines.Line2D):
suffix = "pts"
else:
suffix = None
self.dict_ = {"type": "drag",
"id": mpld3.utils.get_id(points, suffix)}
你试过bqplot? The Scatter
has an enable_move
parameter, that when you set to True
they allow points to be dragged. Furthermore, when you drag you can observe a change in the x
or y
value of the Scatter
or Label
and trigger a python function through that, which in turn generates a new plot. They do this in the Introduction笔记本了吗
Jupyter 笔记本代码:
# Let's begin by importing some libraries we'll need
import numpy as np
from __future__ import print_function # So that this notebook becomes both Python 2 and Python 3 compatible
# And creating some random data
size = 10
np.random.seed(0)
x_data = np.arange(size)
y_data = np.cumsum(np.random.randn(size) * 100.0)
from bqplot import pyplot as plt
# Creating a new Figure and setting it's title
plt.figure(title='My Second Chart')
# Let's assign the scatter plot to a variable
scatter_plot = plt.scatter(x_data, y_data)
# Let's show the plot
plt.show()
# then enable modification and attach a callback function:
def foo(change):
print('This is a trait change. Foo was called by the fact that we moved the Scatter')
print('In fact, the Scatter plot sent us all the new data: ')
print('To access the data, try modifying the function and printing the data variable')
global pdata
pdata = [scatter_plot.x,scatter_plot.y]
# First, we hook up our function `foo` to the colors attribute (or Trait) of the scatter plot
scatter_plot.observe(foo, ['y','x'])
scatter_plot.enable_move = True
我想在 Jupyter notebook 中制作一些交互式绘图,其中绘图中的某些点可以由用户拖动。然后,这些点的位置应该用作 Python 函数(在笔记本中)更新绘图的输入。
这里已经完成了这样的事情:
http://nbviewer.ipython.org/github/maojrs/ipynotebooks/blob/master/interactive_test.ipynb
但回调是 Javascript 函数。在某些情况下,更新情节的代码需要非常复杂,并且需要很长时间才能在 Javascript 中重写。如有必要,我愿意在 Javascript 中指定可拖动点,但是是否可以回调 Python 来更新情节?
我想知道像 Bokeh 或 Plotly 这样的工具是否可以提供此功能。
tl;dr - Here's a link to the gist showing update-on-drag.
要做到这一点,您需要知道:
- 如何与 Jupyter Javascript 的 IPython 内核交互
前端。现在是通过
Jupyter.Kernel.execute
(current 源代码 ). - 够d3.js舒服了。 (喜欢用屏幕绘制坐标转换。)
- 您选择的 d3-via-Python 库。 mpld3 对于此示例。
mpld3 有其 own plugin for draggable points and capability for a custom mpld3 plugin. But right now there is no feature to redraw the plot on update of data;维护者说现在最好的方法是在更新时删除并重新绘制整个图,或者真正深入到 javascript.
Ipywidgets 是,就像你说的(据我所知),一种在使用 IPython 内核,所以不是你想要的。但比我提议的要容易一千倍。 ipywidgets github repo 的 README links to the correct IPython notebook to start with 在他们的示例套件中。
关于 Jupyter notebook 与 IPython 内核直接交互的最佳博客 post 来自 Jake Vanderplas in 2013. It's for IPython<=2.0 and commenters as recent as a few months ago (August 2015) posted updates for IPython 2 and IPython 3,但代码不适用于我的 Jupyter 4 notebook。 问题似乎是 javascript API for the Jupyter kernel 在不断变化。
我在要点中更新了 mpld3 dragging example 和 Jake Vanderplas 的示例(link 位于此回复的顶部)以提供尽可能简短的示例,因为这已经很长了,但是下面的片段试图更简洁地传达这个想法。
Python
Python 回调可以有任意多的参数,甚至可以是原始代码。内核将通过 eval
语句 运行 它并发回最后的 return 值。输出,无论是什么类型,都将作为字符串 (text/plain
) 传递给 javascript 回调。
def python_callback(arg):
"""The entire expression is evaluated like eval(string)."""
return arg + 42
Javascript
Javascript 回调应该有一个参数,即 Javascript
Object
遵循结构 documented here.
javascriptCallback = function(out) {
// Error checking omitted for brevity.
output = out.content.user_expressions.out1;
res = output.data["text/plain"];
newValue = JSON.parse(res); // If necessary
//
// Use newValue to do something now.
//
}
使用函数 Jupyter.notebook.kernel.execute
从 Jupyter 调用 IPython 内核。发送到的内容
内核是 documented here.
var kernel = Jupyter.notebook.kernel;
var callbacks = {shell: {reply: javascriptCallback }};
kernel.execute(
"print('only the success/fail status of this code is reported')",
callbacks,
{user_expressions:
{out1: "python_callback(" + 10 + ")"} // function call as a string
}
);
mpld3 插件中的 Javscript
修改 mpld3 库的插件以添加一个独特的 class HTML 个要更新的元素,以便我们可以在 未来。
import matplotlib as mpl
import mpld3
class DragPlugin(mpld3.plugins.PluginBase):
JAVASCRIPT = r"""
// Beginning content unchanged, and removed for brevity.
DragPlugin.prototype.draw = function(){
var obj = mpld3.get_element(this.props.id);
var drag = d3.behavior.drag()
.origin(function(d) { return {x:obj.ax.x(d[0]),
y:obj.ax.y(d[1])}; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
// Additional content unchanged, and removed for brevity
obj.elements()
.data(obj.offsets)
.style("cursor", "default")
.attr("name", "redrawable") // DIFFERENT
.call(drag);
// Also modify the 'dragstarted' function to store
// the starting position, and the 'dragended' function
// to initiate the exchange with the IPython kernel
// that will update the plot.
};
"""
def __init__(self, points):
if isinstance(points, mpl.lines.Line2D):
suffix = "pts"
else:
suffix = None
self.dict_ = {"type": "drag",
"id": mpld3.utils.get_id(points, suffix)}
你试过bqplot? The Scatter
has an enable_move
parameter, that when you set to True
they allow points to be dragged. Furthermore, when you drag you can observe a change in the x
or y
value of the Scatter
or Label
and trigger a python function through that, which in turn generates a new plot. They do this in the Introduction笔记本了吗
Jupyter 笔记本代码:
# Let's begin by importing some libraries we'll need
import numpy as np
from __future__ import print_function # So that this notebook becomes both Python 2 and Python 3 compatible
# And creating some random data
size = 10
np.random.seed(0)
x_data = np.arange(size)
y_data = np.cumsum(np.random.randn(size) * 100.0)
from bqplot import pyplot as plt
# Creating a new Figure and setting it's title
plt.figure(title='My Second Chart')
# Let's assign the scatter plot to a variable
scatter_plot = plt.scatter(x_data, y_data)
# Let's show the plot
plt.show()
# then enable modification and attach a callback function:
def foo(change):
print('This is a trait change. Foo was called by the fact that we moved the Scatter')
print('In fact, the Scatter plot sent us all the new data: ')
print('To access the data, try modifying the function and printing the data variable')
global pdata
pdata = [scatter_plot.x,scatter_plot.y]
# First, we hook up our function `foo` to the colors attribute (or Trait) of the scatter plot
scatter_plot.observe(foo, ['y','x'])
scatter_plot.enable_move = True