为Plotly.js作图增加ggplot2风格

前言

之前用Python做数据可视化的时候经常用Plotly,感觉效果还不错,尤其是类似R语言里ggplot2包的风格,看上去比较专业。前不久使用Plotly.js,却发现居然没有这个风格,想想这不科学啊,所以干脆把这个风格的配置提取了出来,用在了JS上面。

Plotly Python风格设置

Plotly模板对象

Python的Plotly库支持设置主题风格,有内置的主题,并且支持自定义主题。具体的说明参见:Theming and templates in Python

1
2
import plotly.io as pio
pio.templates

image-20220502111611183

通过上面的代码查看内置的模板(在Jupyter Lab中),可以发现自带的有ggplot2模板。由技术文档可知,模板是plotly.graph_objs.layout._template.Template类,其中由两个关键的属性:

  • layout,布局属性,是一个图形(对象,其结构与图形的布局属性完全相同。当为模板布局的属性提供值时,这些值将用作应用此模板的任何图中的默认值;
  • data,数据属性用于自定义添加到应用模板的图窗的迹线(traces)属性的默认值。此数据属性包含一个类型为 go.layout.template.Data 的图形对象,该对象具有以每种支持的trace类型命名的属性,然后为这些类型属性分配相应类型的图形对象跟踪的列表或元组。也就是不同类型的图表(例如scatter、bar、histogram)的风格是分别定义的。

此外,还可以看到一个关键的方法——to_plotly_json,可以将Template对象导出为JSON对象,这个东西,很有可能与我们为Plotly.js设置风格有关。

模板对象调用分析

Plotly Python最后的渲染是是通过Plotly.js实现的,那么这个过程中,模板对象是怎么使用的呢?看一下源码一探究竟。

根据Plotly能够导出HTML, 看了一下plotly.io模块,其中的to_html()能够将Figure对象转换为html代码:

image-20220502162524087

其中关键的代码部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Serialize config dict to JSON
jconfig = _json.dumps(config)
script = """\
if (document.getElementById("{id}")) {{\
Plotly.newPlot(\
"{id}",\
{data},\
{layout},\
{config}\
){then_addframes}{then_animate}{then_post_script}\
}}""".format(
id=plotdivid,
data=jdata,
layout=jlayout,
config=jconfig,
then_addframes=then_addframes,
then_animate=then_animate,
then_post_script=then_post_script,
)

上面代码中的Plotly.newPlot()正是Plotly.js用于画图的函数。进一步分析发现,jlayout参数中有template字段。

那么,template就是这么起作用的吗?

进一步查看plotly\express\_core.py中的代码,可以发现:

  • apply_default_cascade()将传入的template参数转换为Template对象,并对颜色、符号进行设置;
  • configure_cartesian_marginal_axes()会对template缺少的一些参数设置默认值;
  • make_figure()通过update_layout()template实例赋给Figure对象。

这样,等到作图的时候,所有template的设置就传递给Plotly.newPlot()了,而对于不是plotly.express的作图,也就是普通作图的时候,都是通过update_layout()来实现的。

因此,我们所要做的就是,把Plotly Python中的template JSON作为Plotly.js作图layout参数的template字段传递进去

Plotly.js添加模板

以ggplot2模板为例。

首先,我们使用to_plotly_json()pio.templates['ggplot2']导出为JSON。

根据Ploltly.js官方的说明文档——JavaScript Figure Reference: layout,将导出的JSON对象layout参数的template属性即可。

以下是测试效果:

image-20220502214314145

See the Pen Plotly.js with template by ZodiacWind (@zodiacwind) on CodePen.

小结

基于对Plotly Python和Plotly.js的使用经验,为Plotly.js提供了ggplot2的模板,当然其他自带的模板也可以用类似的方法添加。此外,我还尝试使用Typescript,虽然Plotly.js官方也提供了类型定义,但是发现可能因为版本更新的缘故,template的类型定义有一个小bug。总而言之,我觉得Plotly还是不错的,值得作为Echarts等流行可视化库之外的一个选择。

参考资料

  1. https://segmentfault.com/q/1010000007827611
  2. https://simguo.github.io/2017/08/03/在hexo-next博文中使用codepen/
  3. https://plotly.com/javascript/plotlyjs-function-reference/