我正在使用散景在 Jupyter Lab 笔记本中绘制许多时间序列(> 100)和许多点(〜20,000) 。 在Jupyter中多次执行单元时,Chrome的内存消耗每次运行会增加400mb以上。在执行了多个单元格后,Chrome往往会崩溃,通常是在累积了几GB RAM使用量时。此外,每次执行后绘图都会变慢。
Jupyter中的“清除[所有]输出” 或“重新启动内核并清除所有输出...” 也不会释放任何内存。在经典的 Jupyter Notebook 以及 Firefox 或 Edge 中,也会发生此问题。
我的.ipynp的最低版本:
import numpy as np
from bokeh.io import show, output_notebook
from bokeh.plotting import figure
import bokeh
output_notebook() # See e.g.: https://github.com/bokeh/bokeh-notebooks/blob/master/tutorial/01%20-%20Basic%20Plotting.ipynb
# Just create a list of numpy arrays with random-walks as dataset
ts_length = 20000
n_lines = 100
np.random.seed(0)
dataset = [np.cumsum(np.random.randn(ts_length)) + i*100 for i in range(n_lines)]
# Plot exactly the same linechart every time
plot = figure(x_axis_type="linear")
for data in dataset:
plot.line(x=range(ts_length), y=data)
show(plot)
即使我每次在重新执行上面的(图)单元之前每次都执行以下单元格,这种“内存泄漏”行为仍会继续:
bokeh.io.curdoc().clear()
bokeh.io.state.State().reset()
bokeh.io.reset_output()
output_notebook() # has to be done again because output was reset
为了避免此问题,我是否必须在Jupyter Notebook中以其他方式绘制(或显示绘制)图?还是这仅仅是Bokeh / Jupyter的错误?
系统上已安装的版本(Windows 10):
- Python 3.6.6:Anaconda自定义(64位)
- 散景:1.4.0
- Chrome:78.0.3904.108
- jupyter:
- 核心:4.6.1
- 实验室:1.1.4
- ipywidgets:7.5.1
- labextensions:
- @ bokeh / jupyter_bokeh:1.1.1版
- @ jupyter-widgets / jupyterlab-manager:v1.0。*
答案 0 :(得分:2)
TLDR;这可能值得making an issue(s) for。
关于不同方面的一些注意事项:
首先要注意的是这些:
bokeh.io.curdoc().clear()
bokeh.io.state.State().reset()
bokeh.io.reset_output()
仅影响 Python进程中的数据结构(例如Jupyter内核)。它们永远不会对浏览器的内存使用或占用空间产生任何影响。
仅基于数据,我希望大约在64MB附近:
20000 * 100 * 2 * 2 * 8 = 64MB
也就是:100行带有20k(x,y)点,这也将转换为(sx,sy)屏幕坐标,所有这些都以float64(8byte)类型的数组表示。但是,Bokeh还会为所有数据构造一个空间索引,以支持诸如悬停工具之类的功能。我希望您正在用此数据炸毁该索引。可能值得将该功能进行配置,以便不需要命中测试的人不必为此付费。讨论此功能的功能请求问题将是适当的。
应该有DOM事件触发器,这些触发器将在重新执行笔记本单元时清除。也许这些已经坏了?不幸的是,与一个很小的团队维护三个大型混合Python / JS工具(包括经典Notebook)之间的集成是一个持续的挑战。错误报告问题将是适当的,以便可以对其进行跟踪和调查。
您现在能做什么?
至少对于您在此处具有相同长度的时间序列的特定情况,以上代码以非常次优的方式构造。您应该尝试将所有内容放在单个ColumnDataSource
中:
ts_length = 20000
n_lines = 100
np.random.seed(0)
source = ColumnDataSource(data=dict(x=np.arange(ts_length)))
for i in range(n_lines):
source.data[f"y{i}"] = np.cumsum(np.random.randn(ts_length)) + i*100
plot = figure()
for i in range(n_lines):
plot.line(x='x', y=f"y{i}", source=source)
show(plot)
通过将序列文字传递给line
,您的代码将导致创建99个不必要的CDS对象(每个line
调用一个)。也不会重新使用x
数据,从而导致不必要地向BokehJS发送99 * 20k额外点。而且,通过发送普通列表而不是numpy数组,所有这些都将使用效率较低的(在时间和空间上)默认JSON编码进行编码,而不是使用numpy数组的有效二进制编码。
话虽如此,但这并不是在这里引起所有问题,并且可能不是一个解决方案。但我想确保指出这一点。
为此,您可以考虑将DataShader与Bokeh结合使用。 Holoviews库还自动将Bokeh和Datashader高度集成。通过在Python端预渲染图像,Datashader实际上是一种带宽压缩工具(除其他外)。
Bokeh倾向于权衡各种交互性。但是,如果您实际上不需要这种交互性,那么您将付出一些额外的费用。如果是这种情况,您可以考虑生成静态PNG,而不是:
from bokeh.io.export import get_screenshot_as_png
p = get_screenshot_as_png(plot)
您将需要安装Exporting Plots中列出的其他可选依赖项,并且如果要进行许多绘图,则可能需要考虑为每个调用显式保存和重用Webdriver。