Python学习笔记#07 - matplotlib.pyplot如何设置坐标轴上下限及间隔、如何将日期作为横坐标

问题来由:《Python编程:从入门到实践》16.1.6的代码无法绘制出与图16-2一模一样的图表。
如果你没有这本书也不要紧,我会给出书中代码、图片和数据。

书中16.1.6的代码如下
绘制斯特卡地区2014年7月份最高气温折线图。

import csv
from datetime import datetime
    
from matplotlib import pyplot as plt
    
# 从文件中获取日期和最高气温
filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)
	
	dates, highs = [], []
	for row in reader:
    	current_date = datetime.strptime(row[0], "%Y-%m-%d")
    	dates.append(current_date)
    	
    	high = int(row[1])
    	highs.append(high)

# 根据数据绘制图形
fig = plt.figure(dpi=128, figsize=(10, 6))
plt.plot(dates, highs, c='red')

# 设置图形的格式
plt.title("Daily high temperatures, July 2014", fontsize=24)
plt.xlabel('', fontsize=16)
fig.autofmt_xdate()
plt.ylabel("Temperature (F)", fontsize=16)
plt.tick_params(axis='both', which='major', labelsize=16)

plt.show()

【代码中所需的sitka_weather_07-2014.csv可以在这里(书本配套资源)下载到,进入网站点击Download .zip即可。
Python学习笔记#07 - matplotlib.pyplot如何设置坐标轴上下限及间隔、如何将日期作为横坐标_第1张图片
所需csv文件在路径ehmatthes-pcc-f555082\chapter_16中。】

书中图16-2是这样的
Python学习笔记#07 - matplotlib.pyplot如何设置坐标轴上下限及间隔、如何将日期作为横坐标_第2张图片
而那段代码画出来的图是这样的
Python学习笔记#07 - matplotlib.pyplot如何设置坐标轴上下限及间隔、如何将日期作为横坐标_第3张图片
hmmm

Python学习笔记#07 - matplotlib.pyplot如何设置坐标轴上下限及间隔、如何将日期作为横坐标_第4张图片

仔细对比,两幅折线图存在如下差异:

  1. 纵轴上下限不一致
  2. 横轴日期格式不一致
  3. 横轴日期间隔不一致
  4. 横轴日期上下限不一致
  5. 刻度线朝向不一致
  6. 书中折线图上边和右边也带刻度

于是找到matplotlib文档查找对坐标轴的操作:

1. 坐标轴上下限设置

可以用pyplot模块中的axis()方法方便地完成横纵坐标轴上下限的设置,使用这个方法时四个限值必须全部给出。

matplotlib.pyplot.axis([xmin, xmax, ymin, ymax])

也可以用xlim()、ylim()方法分别设置横轴与纵轴的上下限。

matplotlib.pyplot.xlim(left, right) # 或 xlim((left, right))
matplotlib.pyplot.ylim(bottom, top) # 或 ylim((bottom, top))

使用这个方法时,可以通过关键词传递实参,只设置单个限值。

xlim(right=3)  # 只调整横轴上限

2. 坐标轴间隔设置

可以用pyplot模块中的xticks()、yticks()方法设置横纵坐标轴的上下限和间隔。

matplotlib.pyplot.xticks(ticks, [labels], **kwargs)
matplotlib.pyplot.yticks(ticks, [labels], **kwargs)

ticks接收一个序列,作为坐标轴的刻度标签,或者作为可选参数lable的位置标记,可选参数lable接收一个序列,放在ticks标记的位置上,**kwargs收集text属性,用于控制lable的外观。
例如:

plt.xticks(range(1, 13, 1), calendar.month_name[1:13], rotation=20)

3. 获取日期对象

为了将横坐标的单位变为日期,需要将日期对象作为实参传递给xlim()方法。通过之前已经导入的datetime类,我们可以获取datetime对象。

datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None)

其中可选参数tzinfo(time zone information,时区信息)接收tzinfo对象。
也可以使用datetime模块中的date类,获取一个date对象。

from datetime import date

date(year, month, day) 

4. 日期格式

书中表16-1提及了一部分日期格式代码,但是偏偏就没有所需的月份名缩写代码,这里列出完整的日期格式代码表,这些格式代码继承自C语言。

代码 含义
%a 星期的三位字母缩写
%A 星期的全称
%w 一位数字表示的星期,其中0为周日,6为周6
%d 两位数字表示的月份中的日期
%b 月份的三位字母缩写
%B 月份的全称
%m 两位数字表示的月份
%y 两位数字表示的年份
%Y 四位数字表示的年份
%H 两位数字表示的24小时制的小时数
%I 两位数字表示的12小时制的小时数
%p 月份的全称
%p AM或PM
%M 两位数字表示的分钟数
%S 两位数字表示的秒钟数
%f 六数字表示的毫秒数
%z 以±HHMM表示的世界时偏移量
%Z 时区名
%j 三位数字表示的一年中的天数
%U 两位数字表示的一年中的周数,周日作为一周首日,一年中第一个周日前的日子视作第0周
%W 两位数字表示的一年中的周数,周一作为一周首日,一年中第一个周一前的日子视作第0周
%c 当地时间日期格式
%x 当地日期格式
%X 当地的时间格式
%% %字符

从上面代码绘制出的折线图可知,默认的日期格式为“%Y-%m-%d”而书中绘制出折线图使用的日期格式为“%b %d %Y”

5. 设置日期格式

有了日期格式代码,下一步就需要把它作为参数传递给相关方法,但上面提到的pyplot中设置坐标轴格式的几个方法并不接收日期格式实参。
matplotlib还有一个名为axis的模块,其中包含坐标轴和坐标轴标记的类,这些类中提供了对坐标轴进行更多的操作的方法,其中XAxis和YAxis类,就有设置坐标轴格式的set_major_formatter()方法。

XAxis.set_major_formatter(self, formatter)
YAxis.set_major_formatter(self, formatter)

那么怎么获得这两个类的对象呢?可以pyplot模块中的gca()方法,该方法返回与关键词实参相匹配图标的XAxis类的xaxis对象和YAxis类的yaxis对象。

matplotlib.pyplot.gca(**kwargs)

gca
有了对象和方法,可以进行参数传递了,可是set_major_formatter()方法只接收formatter对象,而我们只有表示日期格式的字符串,这时需要从matplotlib中再导入一个新的模块:dates,其中的DateFormatter类,可以把字符串转换为用于日期的DateFomatter对象,这是一种专门用于日期的formatter对象。

matplotlib.dates.DateFormatter(fmt, tz=None)

其中fmt接收日期格式的字符串,tz接收tzinfo对象,设置时区。

6. 设置日期间隔

同样地,在XAxis和YAxis类中有设置坐标轴间隔的set_major_locator()方法。

XAxis.set_major_locator(self, locator)
YAxis.set_major_locator(self, locator)

这个方法接收locator对象,我们需要将日期间隔设置为3天,相应地,在dates模块中有DayLocator类。

matplotlib.dates.DayLocator(bymonthday=None, interval=1, tz=None)

其中bymonthday接收一个序列,默认情况下会标记出一月中的每一天,即bymonthday=range(1,32),interval设置日期间隔,tz设置时区。

7. 设置刻度线朝向

pyplot模块中的tick_params()方法可以修改坐标刻度,刻度标签和网格线的外观

matplotlib.pyplot.tick_params(axis='both', **kwargs)

可选形参axis可以传入三种实参{‘x’, ‘y’, ‘both’},指示对哪个坐标轴进行修改,默认值为’both’;看到**kwargs我们就知道这里可以传入一堆关键词实参来修改坐标刻度,刻度标签和网格线的外观,这里只介绍涉及的几个形参:witch可以传入三种实参{‘major’, ‘minor’, ‘both’},默认值为’major’,指示对主刻度还是辅刻度进行修改;direction可以传入三种实参{‘in’, ‘out’, ‘inout’},设置刻度线朝向;labelsize可以传入浮点数或字符串,通过数值或字符串(例如’large’)的方式修改刻度标签的字体大小。

我们可以看到书中代码已经使用了这个方法:

plt.tick_params(axis='both', which='major', labelsize=16)

所以只需要再传递direction实参就可以修改刻度线朝向了。

8. 设置双坐标轴

书中折线图上边和右边也带刻度,其实是建立了双坐标轴。matplotlib的axes模块提供了twinx()、twiny()方法建立双坐标轴,需要注意的是twinx()方法建立的是共享x轴的双轴,亦即建立的是双y轴,相应twiny()建立的是双x轴。

建立双坐标轴之后,我们只需将先前设置好的x、y坐标轴的上下限和刻度间隔复制到新建立的x、y坐标轴上,然后将这两个轴的刻度标签隐藏即可。

twinx()、twiny()方法返回的都是axes对象,我们可以使用axes模块中提供的方法设置坐标轴格式:get_xlim()、get_ylim()方法用以获取指定坐标轴的上下限,get_xticks()、get_yticks()方法用以获取指定坐标轴的刻度位置列表,set_xlim()、set_ylim()方法用以设置指定坐标轴的上下限,set_xticks()、set_yticks()方法用以设置指定坐标轴的刻度位置,set_xticklabels()、set_yticklabels()方法用以设置指定坐标轴的刻度标签格式,可以传入空列表来关闭坐标轴的刻度标签,需要注意的是x轴以日期为单位,处理起来总是特殊一些,需要使用xaxis_date()方法,将x轴刻度和刻度标签中的数据当做日期对待。


于是,要画出和书中一模一样的折线图,代码如下:

#coding=gbk
import csv
from datetime import datetime

from matplotlib import pyplot as plt
from matplotlib import dates as mdates

filename = 'sitka_weather_07-2014.csv'
with open(filename) as f:
    reader = csv.reader(f)
    header_row = next(reader)

    # 从文件中获取日期和最高气温
    dates, highs = [], []
    for row in reader:
        current_date = datetime.strptime(row[0], "%Y-%m-%d")
        dates.append(current_date)
        
        highs.append(int(row[1]))

    # 根据数据绘制图表
    fig = plt.figure(figsize=(10, 6))
    plt.plot(dates, highs, c='red') 

    # 设置图表的格式
    plt.title("Daily high temperatures, July 2014", fontsize=24)
    
    plt.xlim([datetime(2014, 7, 1),datetime(2014, 7, 31)]) # 日期上下限
    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b %d %Y')) # 日期格式,%B为月份名,%b为月份名缩写
    plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=3)) # 日期间隔
    plt.xlabel('', fontsize=16)
    fig.autofmt_xdate() # 斜的日期标签

    plt.yticks(range(54, 74, 2)) # 温度上下限和间隔
    plt.ylabel("Temperature (F)", fontsize=16)
    plt.tick_params(axis='both', which='major', direction='in', labelsize=12)
    
    xlm = plt.gca().get_xlim()
    xtk = plt.gca().get_xticks()
    ylm = plt.gca().get_ylim()
    ytk = plt.gca().get_yticks()

    x2 = plt.gca().twiny()
    x2.xaxis_date()
    x2.set_xlim(xlm)
    x2.set_xticks(xtk)
    x2.set_xticklabels([])
    plt.tick_params(axis='x', which='major', direction='in')

    y2 = plt.gca().twinx()
    y2.set_ylim(ylm)
    y2.set_yticks(ytk)
    y2.set_yticklabels([])
    plt.tick_params(axis='y', which='major', direction='in')
    
    plt.show()

注意,这样写会报错:

from matplotlib import dates

plt.gca().xaxis.set_major_formatter(dates.DateFormatter('%Y/%m/%d'))

因为dates在之前已用作变量

你可能感兴趣的:(Python学习笔记#07 - matplotlib.pyplot如何设置坐标轴上下限及间隔、如何将日期作为横坐标)