zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Matplotlib时间序列型图表(1)

序列matplotlib 时间 图表
2023-06-13 09:17:12 时间

往期回顾:

在前几篇文章中,我们介绍了数据分布型图表的几种绘制方法,如下图所示(滑动以浏览),对以往的工作做个总结。目的就是简化大家代码的书写过程,拓宽绘图方法,为科研和商业绘图提供帮助。

在前三篇文章中,我们系统介绍了python内置库和pandas中常见的时间处理方法,以此为基础,进入到我们今天的主题——时间序列图的绘制。

时间序列图简介

时间序列图强调数据随时间的变化规律或趋势,X轴一般为时序数据,Y轴为数值型数据,包括了折线图、面积图、雷达图、日历图、柱形图等。其中折线图是用来显示时间序列变化趋势的标准形式,非常适合用于显示相等时间间隔下的数据趋势。

本篇文章我们将学习绘制以下图表(滑动以浏览):

OK,现在开始我们的学习之路吧。


1

折线图

时间折线图语法与matplotlib的plot语法一致,只不过将x轴换为了时间数据。常见的语法参数如下:

#label为数据标签,当一个图绘制多条折线可以使用;alpha为透明度,取值为0-1 
plt.plot(x, y, color, linewidth, label, alpha, **kwargs)

现有一组数据(df),记录了2020年各站点逐日的PM2.5浓度,现要求将1017A和1050A站点的浓度变化用折线图表示。

示例代码如下:

import pandas as pd
import matplotlib.pyplot as plt

#筛选1017A和1050A站点的数据,并抽取指定列
sel_df1 = df.loc[df[df['site'] == '1017A'].index, ['pm2_5','new_date']]
sel_df2 = df.loc[df[df['site'] == '1050A'].index, ['pm2_5','new_date']]

font1 = {'family':'Simsun', 'size': 15} #新建字体样式
fig = plt.figure(figsize = (8, 4))

#绘制1017A站点的图
ax1 = fig.add_subplot(121)
#绘图,颜色为黑色,线宽为1.5,透明度为0.7
ax1.plot(sel_df1['new_date'], sel_df1['pm2_5'].values, c = 'k', linewidth = 1.5, alpha = .7)
ax1.set_ylabel('pm2.5 浓度', font1) #设置y轴标签,字体样式为新建样式
#设置x轴刻度字体样式
plt.setp(ax1.get_xticklabels(), fontproperties = 'Times New Roman', size = 13)
plt.setp(ax1.get_yticklabels(), fontproperties = 'Times New Roman', size = 13)

#绘制1050A站点的图
ax2 = fig.add_subplot(122)
ax2.plot(sel_df2['new_date'], sel_df2['pm2_5'].values, c = 'b', linewidth = 1.5, alpha = .7)
ax2.set_ylim(ax1.get_ylim()) #设置y轴范围与ax1的y轴范围一致
ax2.yaxis.set_ticklabels([]) #设置y轴刻度不可见
#设置x轴刻度字体样式
plt.setp(ax2.get_xticklabels(), fontproperties = 'Times New Roman', size = 13)

fig.autofmt_xdate() #自动调整x轴时间刻度
#为两个子图设置一个总标题,设置字体的属性和大小
plt.suptitle('1017A和1050A站点2020年PM2.5浓度值', fontproperties = 'Simsun', size = 18)
plt.show()

2

面积图

面积图是在折线图的基础之上生成的,它将折线图中折线与自变量坐标轴之间的区域用颜色或纹理填充,可以更好突出趋势信息。

面积图的语法和常见参数解释如下:

#(x, y1)是数据标签,y2是起始基准位置,若y2为0,则表示x轴与y1之间的面积
#facecolor是填充颜色,edgecolor是面积边缘线框颜色,label为标签,当一个子图有多个数据系列可用
plt.fill_between(x, y1, y2, facecolor, edgecolor, label, alpha, **kwargs)

现将上一个数据集中1017A站点在2020年的PM2.5浓度值用面积表示,再绘制一个二类标签的面积图。

fig = plt.figure(figsize = (8, 4))
#绘制第一个图,设置起始基准位置为y=0
ax1 = fig.add_subplot(121)
ax1.fill_between(sel_df1['new_date'], sel_df1['pm2_5'].values, y2 = 0)
ax1.set_ylim(0, sel_df['pm2_5'].max())
fig.autofmt_xdate() #自适应x时间轴 

#绘制多系列的面积图
ax2 = fig.add_subplot(122)
#构造数据
x = np.linspace(-10, 10, 200)
ax2.set_xlim(-10, 10)
y1 = np.sin(x)
y2 = np.cos(x)
ax2.fill_between(x, y1, y2 = 0, facecolor = 'r', edgecolor = 'k', label = 'sin(x)', alpha = .5)
ax2.fill_between(x, y2, y2 = 0, facecolor = 'b', edgecolor = 'k', label = 'cos(x)', alpha = .5)
ax2.set_yticks(np.linspace(-1, 1, 5)) #设置y轴标签,-1,1之间5个刻度
#设置图里显示方式,bbox_to_anchor(x, y, width, height),单位为横纵长度的百分数
#也就是图例中心点画在横轴的110%,纵轴的0%处,宽度为20%y轴坐标处,一般而言前两个参数就可以满足使用
ax2.legendax2.legend(bbox_to_anchor=(1.1, 0, 0.2, 0.2), loc = 'center')

plt.show()

3

日历图

我们常用的日历也可以当作可视化工具,适用于显示不同时间段,以及活动的组织情况。时间段通常以不同单位表示,例如日、周、月、年。

日历图的可视化形式主要有:以年为单位的日历图和以月为单位的日历图。日历图的数据结构一般为(日期-Date,数值-Value),将数值映射到日期在日历图上展示,其中数值映射到颜色


3.1 年日历图

年日历图的绘制需要利用calmap库进行绘制(利用pip工具下载)。其中,输入的数据必须为Series类型,且index为时间类型(DatetimeIndex)。现在绘制1017A站点在2020年的日历图。

先观察数据(sel_df1)的数据结构:

示例代码如下:

import calmap
import matplotlib.pyplot as plt
#数据准备 
new_df = sel_df1.set_index('new_date') #设置索引列 
new_df.index = pd.DatetimeIndex(new_df.index) #索引转为DatetimeIndex
#新建两个字体样式
font1 = {'family':'Times New Roman', 'size': 13}
font2 = {'family':'Simsun', 'size':14}
fig, ax = calmap.calendarplot(new_df['pm2_5'], fillcolor = 'grey', linecolor = 'w', linewidth = 0.1, cmap = 'jet', 
                             yearlabel_kws={'color':'black', 'fontsize':12}, fig_kws = dict(figsize=(10, 6), dpi=80))
#设置x轴刻度字体样式
plt.setp(ax[0].get_xticklabels(), fontproperties = 'Times New Roman', size = 14)
ax[0].set_ylabel('2020', font1) #设置y轴标签字体样式

#添加色条,orientation(绘制方向,默认垂直),extend(是否两边有凸起,默认没有)
#aspect(色条宽度,值越大,越窄),pad(色条与绘图区的距离)
cbar = fig.colorbar(ax[0].get_children()[1], ax = ax, orientation='horizontal', extend = 'both', aspect = 40, pad = 0.05)
cbar.set_label('PM2.5浓度', fontdict = font2)
plt.show()

3.2 月日历图(选看)

python中实现月日历图可以在plotnine库中进行,且代码比较复杂,对于这部分可以选看,如果有需要,可以直接将代码复制,在此我会尽力给代码进行详细注释。

在plotnine中进行绘制月日历图时,使用geom_tile()函数来绘制每日的”瓦片“,借助facet_wrap()函数分面绘制逐月的图像。关键在于月、周、日数据的转换。

先将1017A站点的2020年PM2.5数据(sel_df1)利用月日历图绘制,数据结构同上一个例子,示例代码如下:

#数据准备
import pandas as pd
new_df = sel_df2.set_index(sel_df2['new_date']) #将日期列设置为索引列
new_df.index = pd.DatetimeIndex(new_df.index) #将索引列类型转换为DatetimeIndex格式
new_df['year'] = new_df.index.year #根据索引列获取每一个样本的年份 
new_df['month'] = new_df.index.month #根据索引列获取每一个样本的月份 
new_df['week'] = new_df.index.strftime('%W') #根据索引列计算每一个样本处于当年的第几周
new_df['week'] = new_df['week'].astype('int') #周数转为整形 
new_df['weekday'] = new_df.index.strftime('%u') #根据索引列计算每一个样本是周几(周1为1,周日为7)
new_df['day'] = new_df.index.strftime('%d') #计算每个样本是月内的第几天
new_df['monthweek'] = new_df.groupby('month')['week'].apply(lambda x: x-x.min() + 1) #计算当天是这个月的第几周 

#以下代码可有可无,增加以下语句可以提升图形的可读性
#设置月、周的标签,例如1月为Jan,周一为Mon;首先需要将列转为类型,再将类型赋值给列表
month_label = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
week_label = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
new_df['month'] = new_df['month'].astype('category')
new_df['month'].cat.categories = month_label

new_df['weekday'] = new_df['weekday'].astype('category')
new_df['weekday'].cat.categories = week_label

new_df

转换后的数据格式如下:

接下来生成月日历图,示例代码如下:

base_plot = (ggplot(new_df, aes('weekday', 'monthweek', fill = 'pm2_5'))+ 
            geom_tile(colour = 'white', size = 0.1)+
             scale_fill_cmap(name = 'Value')+
             geom_text(aes(label = 'day'), size = 8)+
             facet_wrap('~month', nrow = 3)+
             scale_y_reverse()+
             xlab('Day')+ylab('Week of month')+
             theme(strip_text=element_text(size=11, face='plain', color = 'black'),
                  axis_title=element_text(size=10, face='plain', color='black'),
                   axis_text = element_text(size=6, face='plain', color='black'),
                  legend_position='right',
                  legend_background=element_blank(),
                  aspect_ratio = 0.85,
                  figure_size = (8, 8),
                  dpi = 100))
print(base_plot)

第一行是指定数据集和所用的列;geom_title生成每日的”瓦片“;scale_fill_map指定每个”瓦片“填充的颜色,应该选择数值列;geom_text为每个瓦片填充文字,应选择”day“列,表示今天是月内的第几天;facet_wrap指定分片,按照月份分片就是按月绘制;theme函数中设置了绘图的详细参数,感兴趣可以自行查找。

结果如下:

其中,空值表示这一天数值缺失,通过日历图可以直观地感受数值的范围和分布情况。


声明:本公众号的所有原创内容,在未经允许的情况下,不得用于商业用途,违者必究。

参考来源:

1.《Python数据可视化之美:专业图表绘制指南》,张杰著,2020年3月第一版.

2.matplotlib官网:

https://matplotlib.org/stable/api/axes_api.html

3.pandas的astype参考手册:

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.astype.html

4.《Python数据可视化之matplotlib实践》,刘大成著,第一版.

5.《利用Python进行数据分析》,Wes Mckinney著,徐敬一译,第一版.