可以从降价链接控制 Jupyter 中的绘图图吗?
Posted
技术标签:
【中文标题】可以从降价链接控制 Jupyter 中的绘图图吗?【英文标题】:Possible to control a plotly diagram in Jupyter from markdown links? 【发布时间】:2021-10-26 15:00:43 【问题描述】:我想使用 Plotly,在 Jupyter 中绘制一个相当简单的线图,比如:
import plotly.graph_objs as go
from plotly.subplots import make_subplots
figSubs = go.FigureWidget(
make_subplots(rows=2, cols=1, specs = [[], []], vertical_spacing = 0.05)
)
figSubs.add_trace(
go.Scatter(mode='lines+markers', x=[0, 1, 2, 3, 4], y=[0, 1000, 990, 980, 970], name='Test', marker='color': 'red', xaxis="x1"),
row=1, col=1
)
figSubs.update_layout(margin=go.layout.Margin(l=20,t=10,b=10,pad=4))
我找到了https://towardsdatascience.com/interactive-visualization-of-decision-trees-with-jupyter-widgets-ca15dd312084,它使用from ipywidgets import interactive
来获取交互式小部件,可以控制 Plotly 情节。
但是,我想要的是有 Markdown 链接,更改 Plotly 图。更具体地说,我想更改范围 - 所以如果我们继续 Plotly: How to set the range of the y axis? 或 How to force Plot.ly Python to use a given yaxis range? ,我希望有如下链接:
[click here for yrange of 0-1000](???)
将执行 figSubs['layout']['yaxis1'].update(range=[0, 1000], autorange=False)
[click here for yrange of 950-1000](???)
将执行 figSubs['layout']['yaxis1'].update(range=[950, 1000], autorange=False)
在大部分 Plotly 设置代码都在 Python 中的 Jupyter Notebook 中,这样的事情是否可行?
【问题讨论】:
【参考方案1】:嗯,显然,这是可能的 - 但是男孩,要获得所有这些信息,以便获得一个可行的示例是 困难 ...
首先,这些是我安装的版本:
$ python3 --version
Python 3.8.10
$ pip3 list | grep 'jupyter \|nbextensions\|plotly'
jupyter 1.0.0
jupyter-contrib-nbextensions 0.5.1
jupyter-nbextensions-configurator 0.4.1
plotly 5.2.2
还有一些关键点:
虽然原则上,might 可以在 link to javascript 内使用 link to javascript - 实际上,它不是:Jupyter Markdown 单元格链接是 sanitized,特别是如果它们使用冒号,则不能href
也被传播给他们(在那里也尝试了 urlencoding,但没有成功)。
这意味着,唯一的解决方案是将您的 Jupyter 单元定义为代码(在本例中为 IPython 内核),然后使用“魔术”命令%%html
,以便能够逐字输出 HTML 和 JavaScript
这里的主要技巧是使用Jupyter.notebook.kernel.execute
JavaScript 函数,从 JavaScript 调用内核(这里是 IPython)代码;但是,请注意 Document the IPython.notebook.kernel.execute function · Issue #2219 · jupyter/notebook 有点缺乏;
我通过How to correctly use kernel.execute method of Jupyter notebook in Javascript (timing issues)? · Issue #4587 · jupyter/notebook 和IPython notebook ~ Using javascript to run python code? 得到了正确的使用示例(JavaScript Promise
和iopub
回调)-> this gist: Run Python code from JavaScript in a Jupyter notebook
使用这种方法,无论何时使用 figure.show()
或 pio.show(figure)
方法,JavaScript/Python 连接中的某些内容都会中断,并且图形将无法更新 - 即使不是这样,代码似乎也可以运行(如由console.log
痕迹证明);在这种情况下似乎唯一有效的功能是display(figure)
请注意,在此示例中:
有一个 Plotly/IPython 交互式下拉菜单,可独立更改绘图范围 有一个 Python 函数可以调整范围,这里既可以从 Python 也可以从 JavaScript(链接)调用它话虽如此,这应该是一个工作示例;在第一个单元格中,使用生成图形的 Python 代码(因此,定义为代码单元格):
import plotly.graph_objs as go
import pandas as pd
from plotly.subplots import make_subplots
# import plotly.io as pio # https://plotly.com/python/getting-started-with-chart-studio/
from IPython.display import display, HTML
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/volcano.csv")
figSubs = go.FigureWidget(
make_subplots(rows=2, cols=1, specs = [[], []], vertical_spacing = 0.05)
)
figSubs.add_trace(
go.Scatter(mode='lines+markers', x=df["V1"], y=df["V55"], name='Test1', marker='color': 'red', xaxis="x1"),
row=1, col=1
)
figSubs.add_trace(
go.Scatter(mode='lines+markers', x=[0, 1, 2, 3, 4], y=[0, 990, 980, 970, 960], name='Test21', marker='color': 'blue', xaxis="x1"),
row=2, col=1
)
figSubs.add_trace(
go.Scatter(mode='lines+markers', x=[0, 1, 2, 3, 4], y=[0, 980, 970, 960, 950], name='Test22', marker='color': 'violet', xaxis="x1"),
row=2, col=1
)
figSubs.update_layout(margin=go.layout.Margin(l=20,t=10,b=10,pad=4))
figSubs.update_yaxes(zeroline=True,showline=True,zerolinewidth=1,zerolinecolor="#000", row=1, col=1)
figSubs.update_yaxes(zeroline=True,showline=True,zerolinewidth=1,zerolinecolor="#000", row=2, col=1)
# Add dropdown
figSubs.update_layout(
updatemenus=[
dict(
buttons=list([
dict(
args=["yaxis.range": [0, 1000], "yaxis.autorange": False, "row": 1, "col": 1],
label="[0, 1000]",
method="relayout"
),
dict(
args=["yaxis.range": [100, 200], "yaxis.autorange": False],
label="[100, 200]",
method="relayout"
)
]),
direction="down",
pad="r": 10, "t": 10,
showactive=True,
x=0.1,
xanchor="left",
y=1.12,
yanchor="top"
),
]
)
# the Python function to adjust the Y range of the first plot - which is also called from JavaScript
def PsetMyYRange(ymin, ymax, dodraw=True):
figSubs['layout']['yaxis'].update(range=[ymin, ymax], autorange=False)
#figSubs.update_yaxes(range=[ymin, ymax]) # changes both!
#figSubs.update_layout(margin=go.layout.Margin(l=200,t=100,b=100,pad=40))
#figSubs.show() # do NOT call this, else cannot manupulate the plot via JavaScript calls of this function later on!
if dodraw:
display(figSubs) #MUST have this to update the plot from JavaScript->Python; note with Plotly in a Jupyter extension, there is no `Plotly` javascript object accessible on the page!
return "; ; ".format(figSubs, ymin, ymax) # just for the console.log printout
PsetMyYRange(110,120,dodraw=False) # call once to make sure it also works from here; but don't "draw", else we get two plots
#figSubs.show() # NOTE: .show() at end, will prevent the PsetMyYRange being able to redraw!
#pio.show(figSubs) # NOTE: also pio.show() at end, will prevent the PsetMyYRange being able to redraw!
display(figSubs) # ... display() works fine however
第二个单元格,本应是 OP 中所需的“Markdown links”单元格,再次必须是代码单元格,使用 %%html
魔术命令:
%%html
<script type='text/javascript'>
window.executePython = function(python)
return new Promise((resolve, reject) =>
var callbacks =
iopub:
output: (data) => resolve(data.content.text.trim())
;
Jupyter.notebook.kernel.execute(`print($python)`, callbacks);
);
function setMyYRange(ymin, ymax)
// NONE of the below quite works - we must call via Promise:
//objstring = IPython.notebook.kernel.execute("global figSubs; print(figSubs)");
//prevstring = IPython.notebook.kernel.execute("print(Jupyter.notebook.get_prev_cell())");
//runstring = "global figSubs; figSubs['layout']['yaxis'].update(range=["+ymin+", "+ymax+"], autorange=False)";
//console.log("setMyYRange " + ymin + " " + ymax + " ... " + objstring + " ... " + prevstring + " ... " + runstring);
//IPython.notebook.kernel.execute(runstring);
// the only thing needed for the code to work:
window.executePython("PsetMyYRange("+ymin+","+ymax+")")
.then(result => console.log(result));
</script>
<a onclick="javascript:setMyYRange(0,1000);" href="javascript:void(0);">here (0,1000)</a>
<a onclick="javascript:setMyYRange(100,200);" href="javascript:void(0);">here (100,200)</a>
【讨论】:
以上是关于可以从降价链接控制 Jupyter 中的绘图图吗?的主要内容,如果未能解决你的问题,请参考以下文章
Pycharm、Spyder、Jupyter notebook“弹出窗绘图”和“控制台绘图”设置
在jupyter-notebook中的markdown单元格边框 - 如何删除?
为 Jupyter 笔记本调整 Julia 内核中的绘图大小