前言
偶然在官网发现了一个时间节点事件图便学习了一下,横轴为时间列表,纵轴为相应时间的文本内容,可以展示各个时间节点的一些事件。虽然和前端画的没法比,但封装好用起来还是挺方便的。
例子
这个例子来自matplotlib官网,我只是在其中做了些注释,用到的numpy操作比较复杂,我加了打印语句便于查看矩阵的变化。
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from datetime import datetime
# 构造数据
names = ['v2.2.4', 'v3.0.3', 'v3.0.2', 'v3.0.1', 'v3.0.0', 'v2.2.3',
'v2.2.2', 'v2.2.1', 'v2.2.0', 'v2.1.2', 'v2.1.1', 'v2.1.0',
'v2.0.2', 'v2.0.1', 'v2.0.0', 'v1.5.3', 'v1.5.2', 'v1.5.1',
'v1.5.0', 'v1.4.3', 'v1.4.2', 'v1.4.1', 'v1.4.0']
dates = ['2019-02-26', '2019-02-26', '2018-11-10', '2018-11-10',
'2018-09-18', '2018-08-10', '2018-03-17', '2018-03-16',
'2018-03-06', '2018-01-18', '2017-12-10', '2017-10-07',
'2017-05-10', '2017-05-02', '2017-01-17', '2016-09-09',
'2016-07-03', '2016-01-10', '2015-10-29', '2015-02-16',
'2014-10-26', '2014-10-18', '2014-08-26']
# 转换类型 date strings (e.g. 2014-10-18) to datetime
dates = [datetime.strptime(d, "%Y-%m-%d") for d in dates]
# Choose some nice levels 定义纵轴长度
levels = np.tile([-5, 5, -3, 3, -1, 1],
int(np.ceil(len(dates)/6)))[:len(dates)]
# 上取整拼凑多块瓷砖,截取和dates一样长的一段
print(np.tile([-5, 5, -3, 3, -1, 1],
int(np.ceil(len(dates)/6))))
print(levels)
# Create figure and plot a stem plot with the date
fig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)
# 标题
ax.set(title="Matplotlib release dates")
# 添加线条, basefmt设置中线的颜色,linefmt设置线的颜色以及类型
markerline, stemline, baseline = ax.stem(dates, levels,
linefmt="C3-", basefmt="k-",
)
# 交点空心,zorder=3设置图层,mec="k"外黑 mfc="w"内白
plt.setp(markerline, mec="k", mfc="w", zorder=3)
# 通过将Y数据替换为零,将标记移到基线
markerline.set_ydata(np.zeros(len(dates)))
# 构造描述底部、顶部的array
vert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]
print(np.array(['top', 'bottom']))
print(levels > 0)
print([(levels > 0).astype(int)])
print(vert)
# 添加文字注释
for d, l, r, va in zip(dates, levels, names, vert):
ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),
textcoords="offset points", va=va, ha="right")
# 设置x轴间隔为每四个月
ax.get_xaxis().set_major_locator(mdates.MonthLocator(interval=4))
ax.get_xaxis().set_major_formatter(mdates.DateFormatter("%b %Y\n"))
# 逆时针30度,刻度右对齐
plt.setp(ax.get_xticklabels(), rotation=30, ha="right")
# 隐藏y轴线
ax.get_yaxis().set_visible(False)
# 隐藏左、上、右的边框
for spine in ["left", "top", "right"]:
ax.spines[spine].set_visible(False)
# 边距仅设置y轴
ax.margins(y=0.1)
plt.show()
演示的结果如下
封装
以后用的时候,当然不愿意写这么多代码,自己简单封装一下,names和dates作为参数传递进去就行,dateFormat和levels设置默认值,也可以外界传入。
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates
from datetime import datetime
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文无法显示的问题
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
def draw_time_text(names, dates, dateFormat="%Y-%m-%d", levels=None):
# 转换类型 date strings (e.g. 2014-10-18) to datetime
dates = [datetime.strptime(d, dateFormat) for d in dates]
if levels == None:
# Choose some nice levels
levels = np.tile([-5, 5, -3, 3, -1, 1],
int(np.ceil(len(dates)/6)))[:len(dates)]
# Create figure and plot a stem plot with the date
fig, ax = plt.subplots(figsize=(8.8, 4), constrained_layout=True)
# 标题
ax.set(title="Marvel movies")
# 添加线条, basefmt设置中线的颜色,linefmt设置线的颜色以及类型
markerline, stemline, baseline = ax.stem(dates, levels,
linefmt="C3-", basefmt="k-",
)
# 交点空心,zorder=3设置图层,mec="k"外黑 mfc="w"内白
plt.setp(markerline, mec="k", mfc="w", zorder=3)
# 通过将Y数据替换为零,将标记移到基线
markerline.set_ydata(np.zeros(len(dates)))
# 构造描述底部、顶部的array
vert = np.array(['top', 'bottom'])[(levels > 0).astype(int)]
# 添加文字注释
for d, l, r, va in zip(dates, levels, names, vert):
ax.annotate(r, xy=(d, l), xytext=(-3, np.sign(l)*3),
textcoords="offset points", va=va, ha="right")
# 设置x轴间隔为每四个月
ax.get_xaxis().set_major_locator(mdates.MonthLocator(interval=4))
ax.get_xaxis().set_major_formatter(mdates.DateFormatter("%b %Y\n"))
# 逆时针30度,刻度右对齐
plt.setp(ax.get_xticklabels(), rotation=30, ha="right")
# 隐藏y轴线
ax.get_yaxis().set_visible(False)
# 隐藏左、上、右的边框
for spine in ["left", "top", "right"]:
ax.spines[spine].set_visible(False)
# 边距仅设置y轴
ax.margins(y=0.1)
plt.show()
if __name__ == '__main__':
names = ['黑寡妇', '永恒族', '上气', '奇异博士2', '雷神4']
dates = ['2020-05-01', '2020-11-06', '2021-02-21', '2021-05-07', '2021-11-05']
draw_time_text(names, dates)
最后得到的结果
更新-插图
由于评论中提出插图的需求,所以将这个需求的实现更新一下。
left, bottom, width, height = 0.2, 0.56, 0.25, 0.32# 插图画板位置
inset = fig.add_axes([left, bottom, width, height])# 在指定位置生成插图画板
import matplotlib.image as mpimg
img = mpimg.imread('永恒族.png') # 打开指定图片
inset.imshow(img) # 插图画板中插入指定图片
inset.set_xticks([]) # 取消x轴刻度
inset.set_yticks([]) # 取消y轴刻度
只需要在显示图片之前加入上面的代码,像这样子。
虽然python能够实现,但是复杂的绘图展示还是交给前端和美工比较合适。比如这里要调整大量的插图位置,就比较麻烦,所以仅仅给出一张图的示例。