Python3.绘图基础matplotlib.pyplot

Python3.绘图基础matplotlib.pyplot

  能够方便地绘图是MATLAB一大优势,但这方面现在并不领先Python。

  以环境物理学原理课一次作业为例,除去数据处理部分,使用matplotlib.pyplot结合一点pandas,绘制出不输MATLAB的常用曲线图。

  千言万语都在源代码里(图在最后,刚学Markdown,图片排版有待研究):

# -*- coding: utf-8 -*-
"""
根据2012年太湖某站半小时气象数据(气压p、气温Ta和相对湿度RH)
计算实际水汽压ea、空气比湿q、水汽密度rhov和水汽摩尔分数Cva
(半小时值、日小时值、日均值、月均值和年均值)。
Created on Mon Oct  2 12:02:29 2017
@author: 墨大宝
"""
# 导入将要用到的模块,matplotlib.pyplot负责绘图
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import meteorolopy as mtr  # 自建模块,用于计算各湿度指标
from datetime import date, timedelta
from matplotlib.dates import DateFormatter

#%% 读取数据,并简单整理
fn = 'E:\硕士课件\环境物理学原理\hw1_大气湿度的计算_20170930\hw1_data_20170930.xlsx'
data = pd.read_excel(fn, skiprows=[0,2], skip_footer=1)  # 读取数据
data.rename(columns={
   
   '日序': '半小时序', 
                     '气温': '气温(℃)', 
                     '相对湿度': '相对湿度(%)', 
                     '气压': '气压(kPa)'}, inplace=True)  # 重命名表头

#%% 计算各气象指标
es = mtr.tc2es(data['气温(℃)'])  # kPa
es.name = '饱和水汽压(kPa)'
ea = mtr.esRH2ea(es, data['相对湿度(%)'])  # kPa
ea.name = '实际水汽压(kPa)'
Cva = mtr.eap2Cva(ea, data['气压(kPa)'])  # ppm
Cva.name = '水汽摩尔分数(ppm)'
q = mtr.Cva2q(Cva) * 1e3  # g/kg
q.name = '比湿(g/kg)'
Cva *= 1e6  # ppm
rhov = mtr.eatc2rhov(ea, data['气温(℃)']) * 1e3  # g/m3
rhov.name = '水汽密度(g/m$^3$)'

#%% 整理计算结果
timenames = ['半小时序', '日序', '月序']
rawnames = ['气温(℃)', '相对湿度(%)', '气压(kPa)']
moistnames = ['实际水汽压(kPa)', '比湿(g/kg)', 
              '水汽密度(g/m$^3$)', '水汽摩尔分数(ppm)']
data['日序'] = np.int64(data['半小时序'])
data['月序'] = [(timedelta(int(x)) + date(2011, 12, 31)).month 
     for x in data['日序']]
data = pd.concat([data[timenames + rawnames], ea, q, rhov, Cva], axis=1)
del ea, q, rhov, Cva, es, fn  # 删除已不再需要的中间变量
daymean = pd.concat(
        [data.groupby('日序')[x].mean() for x in rawnames + moistnames], 
        axis=1)
daymean.index = pd.date_range('20120101', '20121231')  # 生成时间序列作为索引
monthmean = pd.concat(
        [data.groupby('月序')[x].mean() for x in rawnames + moistnames], 
        axis=1)
monthmean.index = pd.date_range('20120101', periods=12, freq='MS')
yearmean = pd.concat(
        [data.groupby(np.tile(range(48), 366))[x].mean() for x in rawnames + moistnames], 
        axis=1)
yearmean.index = pd.date_range('201201010000', periods=48, freq='30min')

#%% 绘图,日均值季节变化
fontdict = {
   
   'family' : 'SimHei', 'fontsize': 8}  # 图中字体设置
for factors in [rawnames, moistnames]:
    fig, axes = plt.subplots(len(factors), 1, sharex=True, dpi=600)  # 生成图形和子图句柄,并设置子图共享横坐标
    fig.set_size_inches(5, 3.8)  # 设置出图尺寸,单位inch
    fig.subplots_adjust(hspace=0, left=0.15, right=0.98, bottom=0.1, top=0.99)  # 设置子图间无空隙以及子图空间布置
    for xid, x in enumerate(factors):
        axes[xid].plot(daymean[x], label=x)  # 绘制曲线,单条曲线label图例可省略
        axes[xid].set_xlim([daymean[x].index[0], daymean[x].index[-1]])  # 设置x轴显示范围
        axes[xid].xaxis.set_major_formatter(DateFormatter('%m-%d'))  # x轴刻度标签格式化为指定日期格式
        axes[xid].xaxis.set_tick_params(labelsize=7)  # 设置x轴刻度标签字号
        axes[xid].yaxis.set_tick_params(labelsize=7)  # 设置y轴刻度标签字号
        axes[xid].set_ylabel(x, **fontdict)  # 设置y轴标题和字体字号
        axes[xid].set_xlabel('日期', **fontdict)  # 设置x轴标题和字体字号
        axes[xid].grid(True)  # 显示网格
    fig.savefig(factors[0]+'日期.png', dpi=600)  # 保存指定dpi图片
    fig.show()  # 显示图片

#%% 绘图,月均值季节变化
for factors in [rawnames, moistnames]:
    fig, axes = plt.subplots(len(factors), 1, sharex=True, dpi=600)
    fig.set_size_inches(2.8, 3.8)
    fig.subplots_adjust(hspace=0, left=0.2, right=0.98, bottom=0.1, top=0.99)
    for xid, x in enumerate(factors):
        axes[xid].plot(monthmean[x], label=x)
        axes[xid].set_xlim([monthmean[x].index[0], monthmean[x].index[-1]])
        axes[xid].xaxis.set_major_formatter(DateFormatter('%m'))
        axes[xid].xaxis.set_tick_params(labelsize=7)
        axes[xid].yaxis.set_tick_params(labelsize=7)
        axes[xid].set_ylabel(x, **fontdict)
        axes[xid].set_xlabel('月份', **fontdict)
        axes[xid].grid(True)
    fig.savefig(factors[0]+'月份.png', dpi=600)
#    fig.show()

#%% 绘图,年均值日变化
for factors in [rawnames, moistnames]:
    fig, axes = plt.subplots(len(factors), 1, sharex=True, dpi=600)
    fig.set_size_inches(12, 4)
    fig.subplots_adjust(hspace=0, left=0.15, right=0.98, bottom=0.1, top=0.99)
    for xid, x in enumerate(factors):
        axes[xid].plot(yearmean[x], label=x)
        axes[xid].set_xlim([yearmean[x].index[0], yearmean[x].index[-1]])
        axes[xid].xaxis.set_major_formatter(DateFormatter('%H:%M'))
        axes[xid].xaxis.set_tick_params(labelsize=7)
        axes[xid].yaxis.set_tick_params(labelsize=7)
        axes[xid].set_ylabel(x, **fontdict)
        axes[xid].set_xlabel('时间', **fontdict)
        axes[xid].grid(True)
    fig.savefig(factors[0]+'时间.png', dpi=600)
    fig.show()

我认为需要注意的地方有:

  • “#%%”是Spyder中的分节标记,类似于MATLAB的“%%”,不确定在其他Python的IDE中适不适用,但用了肯定没有语法问题。
  • plt.plot(x, y)函数和MATLAB中plot(x, y)函数类似,传入参数也几乎相同。
  • 本文中使用的是axes子图对象的.plot()方法,效果等价于plt.plot()函数,只不过是更方便的表达形式。
  • 当传入单个向量如plt.plot(y)时,其实和MATLAB中plot(y)一样,横坐标默认是y向量的整数索引,但是本文中传入的是一个<pandas.Series>对象,其索引已经是对应的时间,所以超级方便!Python+1
  • matplotlib本身并不直接支持中文输出,必须指定中文字体才能在图中正确显示中文,但其实一点都不麻烦,反正字号通常都是要调整的,一并在fontdict中列出来就好了。
  • fig.savefig()最好先于fig.show(),因为保存机制貌似所见即所得,如果设置的图片尺寸大于电脑屏幕,会自动更改显示比例,然后再保存的话便不是自己fig.set_size_inches()中所设置的尺寸了。fig.show()并不是必须的,据说大量出图时只使用fig.set_size_inches()效率极高。
  • Spyder中默认在控制台中出图,并且是立即出图,不管有没有fig.show(),这应该是属于IPython的特征,在控制台中输入“%matplotlib qt5”可以切换为像MATLAB弹窗出图,这个魔法指令根据自己电脑的环境自行变化。

  MATLAB可能也有很方便的画图方法我没有发现,尤其是一些随近几年版本新出现的方法,这也是我改投Python怀抱的原因之一。

  显然绘图不仅仅是画几条折线图而已,还包括双Y坐标轴图、直方图、极坐标图、三维图等等,Python都能画,慢慢学,以后再说。

附图如下:
1
2
3
4
5
6

猜你喜欢

转载自blog.csdn.net/modabao/article/details/78395510