跳转到主要内容

静态图像导出,适用于零依赖的基于Web的可视化库

项目描述

概述

安装Kaleido

kaleido包可以使用pip从PyPI安装...

$ pip install kaleido

或从conda-forge使用conda安装。

$ conda install -c conda-forge python-kaleido

核心kaleido C++可执行文件的版本作为资产附加到GitHub发布,在https://github.com/plotly/Kaleido/releases

使用Kaleido将plotly.py图表导出为静态图像

Plotly Python库的4.9版本及以上将自动使用kaleido进行静态图像导出,前提是已经安装了kaleido。例如

import plotly.express as px
fig = px.scatter(px.data.iris(), x="sepal_length", y="sepal_width", color="species")
fig.write_image("figure.png", engine="kaleido")

然后,在当前工作目录中打开figure.png

fig

有关更多信息,请参阅plotly静态图像导出文档:https://plotly.com/python/static-image-export/

低级Kaleido作用域开发者API

kaleido Python包提供了一个低级Python API,专为像Plotly这样的高级绘图库而设计。以下是一个使用低级Kaleido API导出Plotly图表的示例

注意:此特定示例使用来自CDN位置的在线副本的plotly JavaScript库,因此在没有互联网连接的情况下无法工作。当plotly Python库使用Kaleido(如上面的示例所示)时,它提供自己本地离线副本的plotly.js路径,因此不需要互联网连接。

from kaleido.scopes.plotly import PlotlyScope
import plotly.graph_objects as go
scope = PlotlyScope(
    plotlyjs="https://cdn.plot.ly/plotly-latest.min.js",
    # plotlyjs="/path/to/local/plotly.js",
)

fig = go.Figure(data=[go.Scatter(y=[1, 3, 2])])
with open("figure.png", "wb") as f:
    f.write(scope.transform(fig, format="png"))

然后,在当前工作目录中打开figure.png

figure

背景

尽管听起来很简单,但使用基于Web的可视化库(例如Plotly.js、Vega-Lite等)程序化生成静态图像(例如PNG格式的位图或SVG格式的矢量图)是一个复杂的问题。这是图书馆开发者多年来一直在努力解决的问题,它推迟了这些库在依赖基于印刷出版物分享其研究的研究社区中的采用。核心困难在于基于Web的可视化库本身并不渲染图表(即着色像素),而是将这项工作委托给Web技术,如SVG、Canvas、WebGL等。这与Matplotlib依赖各种后端来显示图表类似,基于Web的可视化库依赖Web浏览器渲染引擎来显示图表。

当图表在浏览器窗口中显示时,可视化库提供导出图像按钮相对简单,因为它可以完全访问浏览器进行渲染。困难在于尝试程序化导出图像(例如从Python),而不在浏览器中显示,也不需要用户交互。为了完成这项任务,可视化库的Python部分需要程序化访问Web浏览器的渲染引擎。

目前,Python基于Web的可视化库中使用了三种主要方法

  1. Bokeh、Altair、bqplot和ipyvolume依赖于Selenium Python库来控制系统Web浏览器(如Firefox或Chrome/Chromium)以执行图像渲染。
  2. plotly.py依赖于Orca,这是一个自定义的无头Electron应用程序,它使用Electron中内置的Chromium浏览器引擎来执行图像渲染。Orca作为本地Web服务器运行,并且plotly.py通过本地端口向其发送请求。
  3. 当在Jupyter笔记本或JupyterLab中操作时,ipyvolume还通过使用ipywidgets协议将导出请求发送到浏览器来支持程序化图像导出。

虽然方法1和方法2都可以使用conda安装,但它们仍然依赖于完整的Web浏览器的所有系统依赖项,即使是实际上不需要渲染可视化的一部分。例如,在Linux上,它们都需要安装与音频(libasound.so)、视频(libffmpeg.so)、GUI工具包(libgtk-3.so)、屏幕保护程序(libXss.so)和X11(libX11-xcb.so)支持相关的系统库。其中许多在JupyterHub、Binder、Colab、Azure笔记本、SageMaker等无头Linux安装中通常不包含。此外,conda的普及程度也不如pip包管理器,并且这两种方法都不能使用pip包安装。

此外,1和2都通过本地网络端口在Python进程和Web浏览器之间进行通信。虽然这通常不是问题,但某些防火墙和容器配置可能会干扰这种本地网络连接。

选项3的优势在于它不会引入额外的系统依赖。缺点是它只能在笔记本运行时工作,因此不能用于独立的Python脚本。

最终结果是,所有这些库都有详细的文档页面,介绍了如何实现图像导出,以及如何解决不可避免的故障和边缘情况。尽管与几年前的情况相比,这是一个巨大的改进,并且已经投入了大量出色的工作,使这些方法尽可能无缝地工作,但上述基本限制仍然导致用户体验不佳。特别是当将基于Web的可视化库与matplotlib和ggplot2等传统绘图库进行比较时,后者在特定环境中图像导出是否可行永远不会成为问题。

Kaleido项目的目标是使基于Web的可视化库的静态图像导出与matplotlib和ggplot2一样普遍可用和可靠。

方法

为了实现这一目标,Kaleido引入了一种新的方法。Kaleido的核心是一个独立的C++应用程序,它将开源Chromium浏览器作为库嵌入。这种架构允许Kaleido使用C++ API与Chromium浏览器引擎进行通信,而不是需要本地网络连接。一个薄的Python包装器作为子进程运行Kaleido C++应用程序,并通过将图像导出请求写入标准输入与它通信,通过从标准输出读取结果。未来可以相当容易地编写其他语言包装器(例如R、Julia、Scala、Rust等),因为接口仅依赖于使用JSON请求的标准输入/标准输出通信。

通过将Chromium编译为库,我们对Chromium构建中包含的内容有一定的控制权。特别是,在Linux上,我们可以以无头模式构建Chromium,这消除了包括上述音频、视频、GUI工具包、屏保和X11依赖在内的大量运行时依赖。然后可以将剩余的依赖项捆绑到库中,这样Kaleido就可以在不需要额外依赖的最小Linux环境中运行。这样,Kaleido可以作为一个自包含的库分发,其作用类似于matplotlib后端。

优势

与Orca相比,Kaleido为plotly.py用户带来了广泛改进。

pip安装支持

64位Linux、MacOS和Windows的预编译轮子在PyPI上可用,可以使用pip进行安装。与Orca一样,Kaleido也可以使用conda进行安装。

改进的启动时间和资源使用

Kaleido的启动速度比Orca快约两倍,使用的系统内存约减少一半。

Docker兼容性

Kaleido可以在基于Ubuntu 14.04+或Centos 7+(或任何在~2014年后发布的其他Linux发行版)的Docker容器中运行,无需使用apt或yum安装额外的依赖项,也不依赖于Xvfb作为无头X11服务器。

托管笔记本服务兼容性

Kaleido可以在几乎任何允许使用pip安装kaleido包的在线笔记本服务中使用。这些包括Colab、Sagemaker、Azure Notebooks、Databricks、Kaggle等。此外,Kaleido还与Binder默认Docker映像兼容。

安全策略/防火墙兼容性

有时,严格的安全策略和/或防火墙服务会阻止Orca绑定到本地端口。由于Kaleido不使用端口进行通信,因此没有这种限制。

缺点

尽管这种方法有很多优点,但主要缺点是构建Chromium不是一件轻松的事情。即使在功能强大的工作站上,下载和构建Chromium代码库也需要50GB以上的磁盘空间和几个小时的时间。在Linux上,这项工作可以一次性完成,然后作为大型Docker容器进行分发,但我们没有为Windows和MacOS提供类似的快捷方式。

范围(插件)架构

虽然受plotly.py需求的启发,但我们从一开始就决定设计Kaleido,使其支持添加其他库变得相对简单。Kaleido中的插件称为“范围”。更多信息,请参阅https://github.com/plotly/Kaleido/wiki/Scope-(Plugin)-Architecture

语言包装架构

虽然Python是Kaleido的初始目标语言,但它已被设计得可以相对简单地添加对其他语言的支持。更多信息,请参阅https://github.com/plotly/Kaleido/wiki/Language-wrapper-architecture

构建Kaleido

构建Kaleido的说明在不同操作系统之间略有不同。所有这些方法都假设Kaleido存储库已经被克隆,并且工作目录已设置为存储库根目录。

$ git clone git@github.com:plotly/Kaleido.git
$ cd Kaleido

Linux

在Linux上构建Kaleido有两种方法,这两种方法都依赖于Docker。

Linux构建依赖于jonmmease/chromium-builder Docker镜像和repos/linux_scripts中的脚本,以将chromium源代码下载到本地文件夹,然后构建它。下载Docker镜像

$ docker pull jonmmease/chromium-builder:0.9

获取Chromium代码库并检出特定标签,然后同步所有依赖项

$ /repos/linux_scripts/fetch_chromium

然后将kaleido应用程序构建到repos/build/kaleido,并捆绑共享库和字体。该应用程序的输入源存储在repos/kaleido/cc/下。构建步骤还会在repos/kaleido/py/dist/下创建Python wheel

$ /repos/linux_scripts/build_kaleido x64

上面的命令将为64位Intel架构构建Kaleido。Kaleido也可以使用以下方法构建64位ARM架构

$ /repos/linux_scripts/build_kaleido arm64

MacOS

要在MacOS上构建,首先安装XCode 11.0+,nodejs 12和Python 3。有关构建要求的更多信息,请参阅https://chromium.googlesource.com/chromium/src/+/master/docs/mac_build_instructions.md

然后获取chromium代码库

$ /repos/mac_scripts/fetch_chromium

然后将Kaleido构建到repos/build/kaleido。构建步骤还会在repos/kaleido/py/dist/下创建Python wheel

$ /repos/mac_scripts/build_kaleido

Windows

要在Windows上构建,首先安装Visual Studio 2019(社区版即可),nodejs 12和Python 3。有关构建要求的更多信息,请参阅https://chromium.googlesource.com/chromium/src/+/master/docs/windows_build_instructions.md

然后从Power Shell命令提示符获取chromium代码库

$ /repos/win_scripts/fetch_chromium.ps1

然后将Kaleido构建到repos/build/kaleido。构建步骤还会在repos/kaleido/py/dist/下创建Python wheel

$ /repos/mac_scripts/build_kaleido.ps1 x64

上面的命令将生成64位构建。可以使用以下方法生成32位构建

$ /repos/mac_scripts/build_kaleido.ps1 x86

构建Docker容器

chromium-builder

chromium-builder容器主要遵循https://chromium.googlesource.com/chromium/src/+/master/docs/linux/build_instructions.md中的说明来安装depot_tools并运行install-build-deps.sh来安装所需的构建依赖项以及适当的稳定版Chromium。该镜像基于ubuntu 16.04,这是在Linux上构建Chromium推荐的操作系统。

使用以下命令构建容器

$ docker build -t jonmmease/chromium-builder:0.9 -f repos/linux_scripts/Dockerfile .

kaleido-builder

此容器包含预编译的chromium源代码树。构建需要几个小时!

$ docker build -t jonmmease/kaleido-builder:0.9 -f repos/linux_full_scripts/Dockerfile .

更新chromium版本

要更新未来的Chromium版本,需要更新Docker镜像。遵循linux_scripts/DockerfileDEPOT_TOOLS_COMMITCHROMIUM_TAG环境变量的说明。

https://chromereleases.googleblog.com/search/label/Desktop%20Update找到稳定的铬版本标签。在GitHub上查看相关标签的日期https://github.com/chromium/chromium/。例如,2020年5月19日的稳定铬版本标签为83.0.4103.61,设置CHROMIUM_TAG="83.0.4103.61"

在depot_tools提交日志(https://chromium.googlesource.com/chromium/tools/depot_tools/+log)中搜索同一天提交的提交哈希。例如,2020年5月19日的depot_tools提交哈希为e67e41a,设置DEPOT_TOOLS_COMMIT=e67e41a

环境变量还必须在repos/linux_scripts/checkout_revisionrepos/mac_scripts/fetch_chromiumrepos/win_scripts/fetch_chromium.ps1脚本中更新。

CMakeLists.txt

repos/目录下的CMakeLists.txt文件仅用于帮助IDE(如CLion/KDevelop)确定如何索引铬源树。它不能用于实际构建铬。使用此方法,可以在CLion中从repos/kaleido/cc/kaleido.cc获得完整的补全和代码导航。

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面