前言
matplotlib 提供的所有绘图都带有默认样式。虽然这可以进行快速绘图,但有时可能需要自定义绘图的颜色和样式,以对绘制更加精美、符合审美要求的图像。matplotlib 的设计考虑到了此需求,很容易调整 matplotlib 图形的样式。
控制填充样式
matplotlib 提供了填充图案用于填充平面。这些填充图案,对于仅包含黑白两色的图形中具有重要作用。
import numpy as np import matplotlib.pyplot as plt n = 10 a = np.random.random(n) b = np.random.random(n) x = np.arange(n) plt.bar(x, a, color='w', hatch='x', edgecolor='black') plt.bar(x, a+b, bottom=a, color='w', edgecolor='black', hatch='/') plt.show()
Tips:具有填充呈现性的函数(如 pyplot.bar() )接受可选参数hatch控制填充样式,此参数的可选值包括:"/", "\", "|", "-", "+", "x", "o", "O","."和"*",每个值对应于不同的填充图案;edgecolor参数可用于控制图案填充的颜色。
控制标记样式
在《详解常见统计图的绘制》中,我们已经了解了如何如何绘制曲线,并明白了曲线是由点之间的连线构成的;此外,散点图表示数据集中的每个点。而 matplotlib 提供了多种形状,可以用其他类型的标记替换点的样式。
标记的指定方式包括以下几种:
1. 预定义标记:预定义的形状,表示为 [0, 8] 范围内的整数或某些预定义的字符串。
2. 顶点列表:值对列表,用作形状路径的坐标。
3. 正多边形:表示 N 边正多边形的三元组 (N, 0, angle),其中 angle 为旋转角度。
4. 星形多边形:它表示为三元组 (N, 1, angle),代表 N 边正星形,其中 angle 为旋转角度。
import numpy as np import matplotlib.pyplot as plt a = np.random.standard_normal((100, 2)) a += np.array((-1, -1)) b = np.random.standard_normal((100, 2)) b += np.array((1, 1)) plt.scatter(a[:,0], a[:,1], color = 'm', marker = 'x') plt.scatter(b[:,0], b[:,1], color = 'c', marker = '^') plt.show()
Tips:使用marker参数,可以为每个数据集合集合指定不同的标记。
我们已经在《Matplotlib使用自定义颜色绘制统计图》中学习了如何在散点图中为每个点定义不同的颜色,如果我们需要为每个点定义不同样式该怎么办呢?问题在于,与 color 参数不同,marker 参数不接受标记样式列表作为输入。因此,我们不能实现 plt.scatter() 的单次调来显示具有不同标记的多个点集。解决方案是,将每种类型的数据点分隔置不同集合中,并为每个集合单独调用 pyplot.scatter() 调用:
import numpy as np import matplotlib.pyplot as plt label_list = ( b'Iris-setosa', b'Iris-versicolor', b'Iris-virginica', ) colors = ['c','y','m'] def read_label(label): return label_list.index(label) data = np.loadtxt('iris.data', delimiter = ',', converters = { 4 : read_label }) marker_set = ('^', 'x', '.') for i, marker in enumerate(marker_set): data_subset = np.asarray([x for x in data if x[4] == i]) plt.scatter(data_subset[:,0], data_subset[:,1], color = colors[i], marker = marker) plt.show()
对于 pyplot.plot(),也可以使用相同的标记参数访问标记样式。当数据点密集时,每个点都使用标记进行显示将会导致图片混乱,因此 matplotlib 提供了 markevery 参数,允许每隔 N 个点显示一个标记:
import numpy as np import matplotlib.pyplot as plt x = np.linspace(-6, 6, 1024) y_1 = np.sinc(x) y_2 = np.sinc(x) + 1 plt.plot(x, y_1, marker = 'x', color = '.75') plt.plot(x, y_2, marker = 'o', color = 'k', markevery = 64) plt.show()
控制标记大小
标记的大小可选参数 s 进行控制:
import numpy as np import matplotlib.pyplot as plt a = np.random.standard_normal((100, 2)) a += np.array((-1, -1)) b = np.random.standard_normal((100, 2)) b += np.array((1, 1)) plt.scatter(a[:,0], a[:,1], c = 'm', s = 100.) plt.scatter(b[:,0], b[:,1], c = 'c', s = 25.) plt.show()
Tips:标记的大小由 plt.scatter() 的参数 s 设置,但应注意它设置的是标记的表面积倍率而非半径。
plt.scatter() 函数还可以接受列表作为s参数的输入,其表示每个点对应一个大小:
import numpy as np import matplotlib.pyplot as plt m = np.random.standard_normal((1000, 2)) r_list = np.sum(m ** 2, axis = 1) plt.scatter(m[:, 0], m[:, 1], c = 'w', edgecolor='c', marker = 'o', s = 32. * r_list) plt.show()
Tips:plt.plot() 函数允许在 markersize (或简写为 ms )参数的帮助下更改标记的大小,但是此参数不接受列表作为输入。
创建自定义标记
虽然 matplotlib 提供了多种标记形状。但是在某些情况下我们可能仍然找不到适合具体需求的形状。例如,我们可能希望使用公司徽标等作为形状。
在 matplotlib 中,将形状描述为一条路径——一系列点的连接。因此,如果要定义我们自己的标记形状,必须提供一系列的点:
import numpy as np import matplotlib.path as mpath from matplotlib import pyplot as plt shape_description = [ ( 1., 2., mpath.Path.MOVETO), ( 1., 1., mpath.Path.LINETO), ( 2., 1., mpath.Path.LINETO), ( 2., -1., mpath.Path.LINETO), ( 1., -1., mpath.Path.LINETO), ( 1., -2., mpath.Path.LINETO), (-1., -2., mpath.Path.LINETO), (-1., -1., mpath.Path.LINETO), (-2., -1., mpath.Path.LINETO), (-2., 1., mpath.Path.LINETO), (-1., 1., mpath.Path.LINETO), (-1., 2., mpath.Path.LINETO), ( 0., 0., mpath.Path.CLOSEPOLY), ] u, v, codes = zip(*shape_description) my_marker = mpath.Path(np.asarray((u, v)).T, codes) data = np.random.rand(8, 8) plt.scatter(data[:,0], data[:, 1], c = 'm', marker = my_marker, s = 75) plt.show()
Tips:所有带有标记的图形的 pyplot 函数都有一个可选参数 marker,其参数值可以是预定义的 matplotlib 标记,也可以是自定义的路径实例,路径对象在 matplotlib.path 模块中定义。
Path 对象的构造函数将坐标列表和指令列表作为输入;每个坐标一条指令,使用一个列表将坐标和指令融合在一起,然后将坐标列表和指令传递给路径构造函数,如下所示:
u, v, codes = zip(*shape_description) my_marker = mpath.Path(np.asarray((u, v)).T, codes)
形状是通过光标的移动来描述的:
- MOVETO:此指令将光标移动到指定的坐标,并不画线。
- LINETO:这将在光标当前点和目标点之间绘制直线,并将光标移动至目标点。
- CLOSEPOLY:此指令仅用于关闭路径,每个形状都以这条指示结束。
理论上,任何形状都是可能的,我们只需要描述它的路径。但在实践中,如果想使用复杂的形状,最好可以提前进行转换工作。