天天看点

python绘制时间节点事件图前言例子封装更新-插图

文章目录

  • 前言
  • 例子
  • 封装
  • 更新-插图

前言

偶然在官网发现了一个时间节点事件图便学习了一下,横轴为时间列表,纵轴为相应时间的文本内容,可以展示各个时间节点的一些事件。虽然和前端画的没法比,但封装好用起来还是挺方便的。

例子

这个例子来自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()
           

演示的结果如下

python绘制时间节点事件图前言例子封装更新-插图

封装

以后用的时候,当然不愿意写这么多代码,自己简单封装一下,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)
           

最后得到的结果

python绘制时间节点事件图前言例子封装更新-插图

更新-插图

由于评论中提出插图的需求,所以将这个需求的实现更新一下。

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绘制时间节点事件图前言例子封装更新-插图

虽然python能够实现,但是复杂的绘图展示还是交给前端和美工比较合适。比如这里要调整大量的插图位置,就比较麻烦,所以仅仅给出一张图的示例。

python绘制时间节点事件图前言例子封装更新-插图