嵌入 Altair 绘图到 Hexo 博客中
目录
Altair 是又一个 Python 绘图库,可交互,基于 Vega 和 Vega-Lite,官方称其为 Declarative Visualization in Python,声明式可视化绘图。在我看来这是一个比较轻量级的绘图库,可能和 Bokeh 是一类,相比 Plotly 要轻很多。由于其可交互的特性,当我们需要在博客上分享某些图时,读者阅读时要方便有效很多。
本文主要聚焦于如何在 hexo 这种静态博客中嵌入或者说显示 altair plot,但如何使用 altair 并不在本文讨论范围内。我之前也写过一篇文章讲如何嵌入 bokeh,感兴趣的话可以瞅上两眼。
下面我们就直入主题吧。
单张图
我们先从一个热力图说起。最近电视剧《开端》很热,反炸CP、司锅姨、今麦郎等梗也是非常多。我也是刚看完没多久,觉得确实很不错,国内相关题材上算是最好的一个了,但感觉最后一集实在是有些仓促……
话扯远了,说回正题。
我抓取了《开端》的豆瓣小组上的帖子,总共约 2.7 万篇。其中有一个字段是“最后回应时间”,表示该帖子最后一次被回复的时间。我们可以据此推断出在哪些时间段讨论比较热烈,所以这就是我们今天要绘制的热力图,横轴是一天中的 24 小时,纵轴是以天为单位的日期,日期范围是最早和最晚帖子的被回复日期(截至我抓取时 2 月 3 日)。
原始数据样例如下:
然后我们先把 last_reply_time
拉出来,获取对应的 hour,按照天进行 resample 并统计每个小时内的贴子数,最终处理成 altair 需要的格式,即 x、y、z 各成一列:
1 | grouped = df.resample('D', on='last_reply_time') |
最终得到的数据样例如下:
小时 | 日期 | 计数 | |
---|---|---|---|
0 | 0 | 2022-01-02 | 0 |
1 | 1 | 2022-01-02 | 0 |
2 | 2 | 2022-01-02 | 0 |
3 | 3 | 2022-01-02 | 0 |
4 | 4 | 2022-01-02 | 0 |
这就是我们要传给 altair 的数据了,横轴是 小时
,纵轴是 日期
,颜色使用 计数
:
1 | chart = alt.Chart(altair_data).mark_rect().encode( |
最终的效果图如下:
可是我们如何将这个图放到我们的博客里呢?
最简单的方式就是导出为 PNG 或者 SVG,就像上面这样,可是这样的话就丢失了可交互性这个重要的特性了。所以最佳方案就是显示绘图的同时保留可交互性。
根据官方文档上的说法,可选的方案有导出为 JSON 或者 HTML。前者需要配合 vegaEmbed 使用,后者也需要,只不过已经内置在 HTML 中了。由于前者需要将 JSON 文件托管在某个地方,因此我们不选用这种方案。我们将使用 HTML 的方式。
我们可以使用 chart.save('chart.html')
来导出到 HTML 文件,下面是一个该文件的样例:
1 |
|
按理说我们需要让有 altair plot 的页面加载 <script>
标签中的文件。但我们不能直接将这个 <script>
标签和 <body>
标签中的内容直接复制到 markdown 文件中,这样是没有效果的。我们需要先找到生成 <head>
标签的地方,这个不同主题位置可能不同。然后修改那里的程序,将我们的 <script>
标签加进去即可。
当然我们希望在有 altair plot 的页面加载这些 js 程序,按需加载,避免拖慢其他页面的加载速度。所以我们可以加一个设置:vega
。当 vega: true
时才加载这些 js,默认为 false
不加载。
综合来说,步骤如下:
- 找到生成
<head>
标签的地方。我目前用的主题是 tranquilpeak,我这个主题生成<head>
的程序是在tranquilpeak/layout/_partial/head.ejs
中。 - 添加
<script>
标签。我们找到上述文件,在最后的</head>
上面加入如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<% if (page.vega) { %>
<style>
.vega-actions a {
margin-right: 12px;
color: #757575;
font-weight: normal;
font-size: 13px;
}
.error {
color: red;
}
</style>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega@5"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-lite@4.8.1"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm//vega-embed@6"></script>
<% } %> - 在博文中加入绘图代码。然后写博客时,在上面的 metadata 里加上
vega: true
,然后将导出的 HTML 文件中的<body>
内容直接复制到想要显示绘图的位置,如本文:这样就可以显示出 altair plot 了,并且鼠标 hover 可以显示当前点的信息,保留了可交互性:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15---
title: 嵌入 Altair 绘图到 Hexo 博客中
date: 2022-02-04 14:19:00
tags:
- hexo
- Python
- DataViz
vega: true
---
<!-- 其他文本 -->
<!-- 这里放 HTML 文件中的 <body> 内容,一般是一个 div 和一个 script。-->
<!-- 其他文本 -->
多张图
从上面的样例可以看出,绘图其实是显示在 id="vis"
的 div 中。而一个 HTML 中 id 不能重名。所以当我们有多张图需要显示时,我们必须更改第二以及后面的图的 id,比如我们可以直接递增,如 vis2
。具体来说,要改的地方有 3 个:
1 | <div id="vis2"></div> <!-- 修改 1 --> |
下面我们将上面的热力图稍微改下,将 计数
视为 O
类型数据,即离散有序数据,此时便会以离散的 colorscale 来显示数据: