第四章 图表样式美化
1.图表样式概述
1.1.默认图表样式
matplotlib在绘图的过程中会读取存储在本地的配置文件matplotlibrc,通过matplotlibrc文件中的缺省配置信息指定图表元素的默认样式,完成图表元素样式的初始设置。
matplotlib文件包含众多图表元素的配置项,可以通过rc_params()
函数查看全部的配置项:
import matplotlib
matplotlib.rc_params()
以上可以得知,rc_params()函数返回一个RcParams对象,该对象是一个字典对象,其中字典的键是由配置要素(如ytick)及其属性(如right)组成的配置项,值为配置项的默认值。
所有的配置项按作用对象的不同主要分为10种配置要素,包括lines(线条)、patch(图形)、font(字体)、text(文本)、axes(坐标系)、xtick和ytick(刻度)、grid(网格)、legend(图例)、figure(画布)及savefig(保存图像)。matplotlib常用的配置项及其说明如下:
配置项 | 说明 | 默认值 |
---|---|---|
lines.color | 线条颜色 | ‘C0’ |
lines.linestyle | 线条颜色 | ‘-’ |
lines.linewidth | 线条宽度 | 1.5 |
lines.marker | 线条标记 | ‘None’ |
lines.markeredgecolor | 标记边框颜色 | ‘auto’ |
lines.markeredgewidth | 标记边框宽度 | 1.0 |
lines.markerfacecolor | 标记颜色 | auto |
lines.markersize | 标记大小 | 6.0 |
font.family | 系统字体 | [‘sans-serif’] |
font.sans-serif | 无衬线字体 | |
font.size | 字体大小 | 10.0 |
font.style | 字体风格 | ‘normal’ |
axes.unicode_minus | 采用Unicode编码的减号 | True |
axes.prop_cycle | 属性循环器 | |
figure.constrained_layout.use | 使用约束布局 | False |
需要说明的是,matplotlib载入时会主动调用rc_params()函数获取包含全部配置项的字典,并将该字典赋值给变量rcParams,以便用户采用访问字典rcParams的方式设置或获取配置项。
1.2.图表样式修改
matplotlib通过灵活地修改配置项以改变图表的样式,而不必拘泥于系统默认的配置。图表的样式可以通过两种方式进行修改:局部修改和全局修改。
-
**局部修改:**局部修改的方式是指通过代码动态地修改matplotlib配置项,此方式用于程序局部定制的需求。若希望局部修改图表的样式,可以通过以下任一种方式实现:
- 第1种—通过给绘图函数或设置图表元素函数的关键字参数传值来修改图表的样式。
- 第2种—通过“rcParams[配置项]”重新为配置项赋值来修改图表的样式。
- 第3种—通过给rc()函数的关键字参数传值来修改图表的样式。
第1种方式只能对某一图表中指定元素的样式进行修改,而第2种和第3种方式可以对整个py文件中指定元素的样式进行修改。
-
**全局修改:**全局修改的方式是指直接修改matplotlibrc文件的配置项,此方式用于满足程序全局定制的需求,可以将指定的图表样式进行统一修改,无需每次在具体的程序中进行单独修改。 matplotlib使用matplotlibrc文件的路径搜索顺序:
- 当前工作路径:程序运行的目录。
- 用户配置路径:通常位于HOME/.matplotlib/目录中,可以通过环境变量MATPLOTLIBRC进行修改。
- 系统配置路径:位于matplotlib安装路径的mpl-data目录中。
matplotlib可以使用
matplotlib_fname()
函数查看当前使用的matplotlibrc文件所在的路径。
import matplotlib
matplotlib.matplotlib_fname()
2.使用颜色
图表在使用颜色时应遵循一定的基本规则,既要避免使用过多的颜色,又要避免随意使用颜色,否则会直接影响可视化的效果且不易让人理解。合理使用颜色可以参考以下基本规则:
2.1.使用基础颜色
matplotlib的基础颜色主要有3种表示方式:单词缩写或单词、十六进制或HTML模式、RGB模式。
- **单词缩写或单词表示的颜色 :**matplotlib中支持使用单词缩写或单词表示的8种颜色:青色、洋红色、黄色、黑色、红色、绿色、蓝色、白色。
- **十六进制/HTML模式表示的颜色:**matplotlib支持使用十六进制或HTML/CSS的形式表示更多的颜色,它将这些颜色存储在
colors.cnames字典
中,可通过访问colors.cnames字典查看全部的颜色。
import matplotlib
for name, hex in matplotlib.colors.cnames.items():
print(name, hex)
-
**RGB模式表示的颜色:**matplotlib支持使用RGB模式的三元组表示颜色,其中元组的第1个元素代表红色值,第2个元素代表绿色值,第3个元素代表蓝色值,且每个元素的取值范围均是[0,1]。
color = (0.3, 0.3, 0.4)
以上3种方式表示的颜色都可以传入matplotlib中带有表示颜色的color或c参数的函数或方法中,从而为图表的相应元素设置颜色。
- 第一种:
plt.plot([1, 2, 3], [3, 4, 5], color='g')
- 第二种:
plt.plot([1, 2, 3], [3, 4, 5], color='#2E8B57')
- 第三种:
plt.plot([1, 2, 3], [3, 4, 5], color=(0.0, 0.5, 0.0))
示例:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [3, 4, 5], color=(0.0, 0.5, 0.0))
plt.show()
2.2.使用颜色映射表
matplotlib中内置了众多预定义的颜色映射表,使用这些颜色表可以为用户提供更多的颜色建议,节省大量的开发时间。pyplot提供了colormaps()函数可以查看所有可用的颜色映射表
import matplotlib.pyplot as plt
cm = plt.colormaps()
print(cm)
运行结果:
['magma', 'inferno', 'plasma', 'viridis', 'cividis', 'twilight', 'twilight_shifted', 'turbo', 'Blues', 'BrBG', 'BuGn', 'BuPu', 'CMRmap', 'GnBu', 'Greens', 'Greys', 'OrRd', 'Oranges', 'PRGn', 'PiYG', 'PuBu', 'PuBuGn', 'PuOr', 'PuRd', 'Purples', 'RdBu', 'RdGy', 'RdPu', 'RdYlBu', 'RdYlGn', 'Reds', 'Spectral', 'Wistia', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'binary', 'bone', 'brg', 'bwr', 'cool', 'coolwarm', 'copper', 'cubehelix', 'flag', 'gist_earth', 'gist_gray', 'gist_heat', 'gist_ncar', 'gist_rainbow', 'gist_stern', 'gist_yarg', 'gnuplot', 'gnuplot2', 'gray', 'hot', 'hsv', 'jet', 'nipy_spectral', 'ocean', 'pink', 'prism', 'rainbow', 'seismic', 'spring', 'summer', 'terrain', 'winter', 'Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3', 'tab10', 'tab20', 'tab20b', 'tab20c', 'magma_r', 'inferno_r', 'plasma_r', 'viridis_r', 'cividis_r', 'twilight_r', 'twilight_shifted_r', 'turbo_r', 'Blues_r', 'BrBG_r', 'BuGn_r', 'BuPu_r', 'CMRmap_r', 'GnBu_r', 'Greens_r', 'Greys_r', 'OrRd_r', 'Oranges_r', 'PRGn_r', 'PiYG_r', 'PuBu_r', 'PuBuGn_r', 'PuOr_r', 'PuRd_r', 'Purples_r', 'RdBu_r', 'RdGy_r', 'RdPu_r', 'RdYlBu_r', 'RdYlGn_r', 'Reds_r', 'Spectral_r', 'Wistia_r', 'YlGn_r', 'YlGnBu_r', 'YlOrBr_r', 'YlOrRd_r', 'afmhot_r', 'autumn_r', 'binary_r', 'bone_r', 'brg_r', 'bwr_r', 'cool_r', 'coolwarm_r', 'copper_r', 'cubehelix_r', 'flag_r', 'gist_earth_r', 'gist_gray_r', 'gist_heat_r', 'gist_ncar_r', 'gist_rainbow_r', 'gist_stern_r', 'gist_yarg_r', 'gnuplot_r', 'gnuplot2_r', 'gray_r', 'hot_r', 'hsv_r', 'jet_r', 'nipy_spectral_r', 'ocean_r', 'pink_r', 'prism_r', 'rainbow_r', 'seismic_r', 'spring_r', 'summer_r', 'terrain_r', 'winter_r', 'Accent_r', 'Dark2_r', 'Paired_r', 'Pastel1_r', 'Pastel2_r', 'Set1_r', 'Set2_r', 'Set3_r', 'tab10_r', 'tab20_r', 'tab20b_r', 'tab20c_r']
颜色映射表一般可以划分为以下3类:
- Sequential:表示同一颜色从低饱和度到高饱和度的单色颜色映射表。
- Diverging:表示颜色从中间的明亮色过渡到两个不同颜色范围方向的颜色映射表。
- Qualitative:表示可以轻易区分不同种类数据的颜色映射表。
此外,开发人员可以自定义新的颜色映射表,再通过matplotlib.cm.register_cmap()
函数将自定义的颜色映射表添加到matplotlib中。
matplotlib主要有两种使用颜色映射表的方式,第一种方式是在调用函数或方法绘制图表或添加辅助元素时将颜色映射表传递给关键字参数cmap,第二种方式是直接调用set_cmap()函数进行设置。
- 第一种:
plt.scatter(x, y, c=np.random.rand(10), cmap=matplotlib.cm.jet)
- 第二种:
plt.set_cmap(matplotlib.cm.jet)
2.3.实例:两个地区对不同种类图书的采购情况
高尔基说:“书籍是人类进步的阶梯”。据统计韩国人的年均阅读量为7本,日本人的年均阅读量为40本,相比较而言,中国人的年均阅读量为0.7本,还有很大的进步空间。
本实例要求根据下表的数据,绘制反映地区1和地区2对各类图书采购情况的堆积柱形图,并分别使用“#FFCC00”和“#B0C4DE”这两种颜色进行区分。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = np.arange(5)
y1 = [1200, 2400, 1800, 2200, 1600]
y2 = [1050, 2100, 1300, 1600, 1340]
bar_width = 0.6
tick_label = ['家庭', '小说', '心理', '科技', '儿童']
fig = plt.figure()
# 参数111的意思是:将画布分割成1行1列,图像画在从左到右从上到下的第1块。当只想画一张图时就使用111
ax = fig.add_subplot(111)
# 绘制柱形图,并使用颜色
ax.bar(x, y1, bar_width, color='#FFCC00', align='center', label='地区1')
ax.bar(x, y2, bar_width, bottom=y1, color='#B0C4DE', align='center', label='地区2')
ax.set_ylabel('采购数量(本)')
ax.set_xlabel('图书种类')
ax.set_title('地区1和地区2对各类图书的采购情况')
ax.grid(True, axis='y', color='gray', alpha=0.2)
ax.set_xticks(x)
ax.set_xticklabels(tick_label)
ax.legend()
plt.show()
3.选择线型
3.1.选择线条的类型
图表中每个线条均具有不同的含义,一般可以通过颜色、宽度、类型来区分线条,其中类型是区分线条的常见方式之一。matplotlib中内置了4种线条的类型:短虚线、点划线、长虚线和实线。
在使用pyplot绘制折线图、显示网格或添加参考线时,可以将线型取值传递给linestyle
或ls
参数,以选择其它的线条类型。
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [3, 4, 5], linestyle='--')
plt.plot([1, 2, 3], [3, 4, 5], ls='--')
plt.show()
3.2.实例:2017年7月与2019年7月国际
外汇市场美元/人民币汇率走势
汇率又称外汇利率,指两种货币之间兑换的比率,亦可视为一个国家的货币对另一种货币的价值。汇率会因诸多外界因素的影响而出现上下波动,从而产生货币贬值和货币升值的现象。
本实例要求根据下表的数据,将日期列的数据作为x轴的刻度范围,将2017年汇率和2019年汇率两列的数据作为y轴的数据,绘制反映2017年7月与2019年7月美元/人民币汇率走势的折线图,并使用实线和长虚线进行区分。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 汇率
eurcny_2017 = np.array([6.8007, 6.8007, 6.8015, 6.8060, 6.8060,
6.8060, 6.8036, 6.8025, 6.7877, 6.7835,
6.7758, 6.7700, 6.7463, 6.7519, 6.7511,
6.7511, 6.7539, 6.7265, 6.7265, 6.7265])
eurcny_2019 = np.array([6.8640, 6.8705, 6.8697, 6.8697, 6.8697,
6.8881, 6.8853, 6.8856, 6.8677, 6.8662,
6.8662, 6.8662, 6.8827, 6.8761, 6.8635,
6.8860, 6.8737, 6.8796, 6.8841, 6.8841])
data_x = np.array([3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13,
14, 17, 18, 19, 24, 25, 26, 31])
fig = plt.figure()
ax = fig.add_subplot(111)
# 第一条折线:湖绿色、实线、线宽2
ax.plot(data_x, eurcny_2017, color='#006370', lw=2,
label='2017年7月美元/人民币汇率')
# 第二条折线:紫色、长虚线、线宽2
ax.plot(data_x, eurcny_2019, color='#8a2e76',ls='--', lw=2,
label='2019年7月美元/人民币汇率')
ax.set_title('2017年7月与2019年7月美元/人民币汇率走势')
ax.set_xlabel('日期')
ax.set_ylabel('汇率')
ax.legend()
plt.show()
4.添加数据标记
数据标记一般指代表单个数据的圆点或其它符号等,用于强数据点的位置,常见于折线图和散点图中。
4.1.添加折线图或散点图的数据标记
matplotlib中内置了许多数据标记,使用这些数据标记可以便捷地为折线图或散点图标注数据点。数据标记可以分为填充型数据标记和非填充型数据标记。
- 填充型数据标记:
- 非填充型数据标记:
在使用pyplot的plot()
或scatter()
函数绘制折线图或散点图时,可以将标记取值传递给marker参数,从而为折线图或散点图添加数据标记。
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [3, 4, 5], marker='*')
plt.show()
除此之外,还可以给plot()
或scatter()
函数的以下参数传值来设置数据标记的属性:
markeredgecolor或mec
:表示数据标记的边框颜色。markeredgewidth或mew
:表示数据标记的边框宽度。markerfacecolor或mfc
:表示数据标记的填充颜色.。markerfacecoloralt或mfcalt
:表示数据标记备用的填充颜色。markersize或ms
:表示数据标记的大小。
例如,为刚刚添加星形标记设置大小和颜色:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [3, 4, 5], marker='*', markersize=20,
markerfacecolor='y')
plt.show()
matplotlib在绘制折线图时,可以使用字符串分别为线条指定颜色、线型和数据标记这三种样式,但每次都需要分别给参数color、linestyle、marker传值进行一一设置,使得编写的代码过于繁琐。为此,matplotlib提供了由颜色、标记、线型构成的格式字符串。
格式字符串是快速设置线条基本样式的缩写形式的字符串:
'[颜色][标记][线型]'
以上格式的每个选项都是可选的,选项之间组合的顺序也是可变的。颜色只能是字母缩写方式表示的颜色。若格式字符串中只有颜色一个选项,可以使用十六进制、单词拼写等其它形式表示的颜色。
pyplot的plot()函数的fmt参数可接收格式字符串,以便能同时为线条指定多种样式,但该参数不支持以fmt为关键字的形式传参,而支持以位置参数的形式传递即可。
plt.plot([1, 2, 3], [3, 4, 5], 'mo--')
4.2.实例:标记不同产品各季度的销售额
本实例要求根据下表的数据,将季度列的数据作为x轴的刻度标签,将产品A、产品B、产品C这三列的数据作为y轴的数据,使用plot()函数绘制反映产品A、产品B和产品C各季度销售额的折线图,并使用不同的线型、颜色、标记进行区分。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
sale_a = [2144, 4617, 7674, 6666]
sale_b = [853, 1214, 2414, 4409]
sale_c = [153, 155, 292, 680]
fig = plt.figure()
ax = fig.add_subplot(111)
# 绘制具有不同线条样式的折线图
ax.plot(sale_a, 'D-', sale_b, '^:', sale_c, 's--')
ax.grid(alpha=0.3)
ax.set_ylabel('销售额(万元)')
ax.set_xticks(np.arange(len(sale_c)))
ax.set_xticklabels(['第1季度', '第2季度', '第3季度', '第4季度'])
ax.legend(['产品A', '产品B', '产品C'])
plt.show()
5.设置字体
不同的字体给人的直观感受不同,例如,宋体给人雅观、大气的感觉;黑体给人厚重、抢眼的感觉。由于每种字体具有不同的特点、使用场景,因此合适的字体可以对图表起到很好的修饰作用。 合理使用字体可以参考以下基本规则:
5.1.设置字体样式
matplotlib中文本是text模块的Text类对象,可以通过之前介绍的text()、annotate()、title()等函数进行创建。Text类中提供了一系列设置字体样式的属性,包括字体类别、字体大小、字体风格、字体角度等。
上表中的属性也可以作为text()、annotate()、title()函数的同名关键字参数,以便用户在创建文本的同时设置字体的样式。
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [3, 4, 5])
plt.text(1.9, 3.75, 'y+x+2', bbox=dict(facecolor='y'),
family='serif', fontsize=18, fontstyle='normal',
rotation=-60)
plt.show()
5.2.未来15天最高气温和最低气温(设置字体样式)
在第二章的折线图实例中既没有使用数据标记标注数据的位置,也没有使用注释文本标注具体的数值,影响用户的阅读体验。
本实例要求对前面的折线图进行调整,为该图中的折线添加数据标记和注释文本,并设置注释文本的字体样式 。
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = np.arange(4, 19)
y_max = np.array([32, 33, 34, 34, 33, 31, 30, 29, 30,
29, 26, 23, 21, 25, 31])
y_min = np.array([19, 19, 20, 22, 22, 21, 22, 16, 18,
18, 17, 14, 15, 16, 16])
# 绘制折线图
plt.plot(x, y_max, marker='o', label='最高温度')
plt.plot(x, y_min, marker='o', label='最低温度')
plt.show()
# 为图表添加注释并设置字体的样式
x_temp = 4
for y_h, y_l in zip(y_max, y_min):
plt.text(x_temp-0.3, y_h + 0.7, y_h, family='SimHei', fontsize=8, fontstyle='normal')
plt.text(x_temp-0.3, y_l + 0.7, y_l, family='SimHei', fontsize=8, fontstyle='normal')
x_temp += 1
plt.title('未来15天最高气温和最低气温的走势')
plt.xlabel('日期')
plt.ylabel('温度($^\\circ$C)')
plt.ylim(0, 40)
plt.legend()
plt.show()
6.切换主题风格
matplotlib.style模块中内置了一些图表的主题风格,通过切换不同的主题风格以满足用户的不同需求。所有的主题风格都存储于matplotlib
配置文件夹的stylelib目录中,可以通过访问available变量查看所有可用的主题风格。
import matplotlib.style as ms
print(ms.available)
使用matplotlib库中的use()函数可以切换图表的主题风格。
use(style)
以上函数的参数style表示图表的主题风格,它可以接收matplotlib中所有可用的主题风格的字符串,也可以接收“default”来恢复默认的主题风格。
例如,将折线图风格切换为“seaborn-dark”:
import matplotlib.style as ms
plt.plot([1, 2, 3], [3, 4, 5])
plt.text(1.9, 3.75, 'y+x+2', bbox=dict(facecolor='y'),
family='serif', fontsize=18, fontstyle='normal',
rotation=-60)
ms.use('seaborn-dark')
plt.show()
7.填充区域
7.1.填充多边形或曲线之间的区域
matplotlib中提供了多个函数用于填充多边形或区域,分别为fill()
、fill_between()
和fill_betweenx()
,其中fill()函数用于填充多边形,fill_between()
或fill_betweenx()
函数分别用于填充两条水平曲线或垂直曲线之间的区域。
1.使用fill()
函数填充多边形:
fill(*args, data=None, facecolor, edgecolor, linewidth, **kwargs)
*args
:表示x坐标、y坐标或颜色的序列。facecolor
:表示填充的背景颜色。edgecolor
:表示边框的颜色。linewidth
:表示边框的宽度。
2.使用fill_betweenx()
函数填充两条垂直曲线之间的区域:
fill_between(x, y1, y2=0, where=None, interpolate=False, step=None, data=None, **kwargs)
x
:表示x轴坐标的序列。y1
:表示第一条曲线的y轴坐标。y2
:表示第二条曲线的y轴坐标。where
:布尔值,表示要填充区域的条件。y1>y2说明第一条曲线位于第二条曲线上方时填充;y1<y2说明第二条曲线位于第一条曲线上方时填充。
fill_betweenx(y, x1, x2=0, where=None, step=None, interpolate=False, data=None, **kwargs)
y
:表示y轴坐标的序列。x1
:表示第一条曲线的x轴坐标。x2
:表示第二条曲线的x轴坐标。where
:布尔值,表示要填充区域的条件。x1>x2说明第一条曲线位于第二条曲线右方时填充;y1<y2说明第二条曲线位于第一条曲线右方时填充。
7.2.彩色的“雪花”
雪花一般呈六角形,且通体为白色。本实例要求使用matplotlib绘制一个由多个六角形组成的雪花形状的多边形,并将该多边形填充为浅橙色 。
import numpy as np
import matplotlib.pyplot as plt
def koch_snowflake(order, scale=10):
def _koch_snowflake_complex(order):
if order == 0:
# 初始三角形
angles = np.array([0, 120, 240]) + 90
return scale / np.sqrt(3) * np.exp(np.deg2rad(angles) * 1j)
else:
ZR = 0.5 - 0.5j * np.sqrt(3) / 3
p1 = _koch_snowflake_complex(order - 1) # 起点
p2 = np.roll(p1, shift=-1) # 终点
dp = p2 - p1 # 连接向量
new_points = np.empty(len(p1) * 4, dtype=np.complex128)
new_points[::4] = p1
new_points[1::4] = p1 + dp / 3
new_points[2::4] = p1 + dp * ZR
new_points[3::4] = p1 + dp / 3 * 2
return new_points
points = _koch_snowflake_complex(order)
x, y = points.real, points.imag
return x, y
x, y = koch_snowflake(order=2)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill(x, y, facecolor='skyblue', edgecolor='blue', linewidth=3)
plt.title("彩色的雪花")
plt.show()