matplotlib基础
matplotlib是面向对象的绘图工具包,绘制的图形中的每一个元素都是一个对象,可以修改这些对象的属性,从而改变图样式。
matplotlib中主要绘图对象列表如下:
- Figure对象,可以想象为一张画布;
- Axes对象,字面理解为坐标轴也可以认为时子图,在一个Figure对象中可以包含多个Axes对象,也就是说一张画布可以包含多个子图;* Line2D对象,代表线条;
- Text对象,代表了文字;
虽然matplotlib是面向对象的绘图工具包,但是也提供了一些常用的绘图方法,可以直接调用plt模块相关方法就可以完成绘图需求,这是因为plt模块内部保存了当前的Figure对象信息,当使用plt的相关方法绘图时,底层实际调用了当前Figure对象的相关方法。可以通过plt.gcf()和plt.gca()分别获取当前用于绘图的Figure和Axes对象。
通过plt.figure()创建一个新的Figure对象,然后在此Figure对象上创建Axies对象:
In [1]: import numpy as np
In [2]: import matplotlib.pyplot as plt
In [3]: fig1 = plt.figure()
In [4]: ax1 = fig1.add_subplot(1, 1, 1)
In [5]: fig1
Out[5]: <matplotlib.figure.Figure at 0x2b4d0bdf4e0>
In [6]: plt.gcf()
Out[6]: <matplotlib.figure.Figure at 0x2b4d0bdf4e0>
In [7]: ax1
Out[7]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0bff780>
In [9]: plt.gca()
Out[9]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0bff780>
In [10]: fig2 = plt.figure()
In [11]: ax2 = fig2.add_subplot(1, 1, 1)
In [12]: fig2
Out[12]: <matplotlib.figure.Figure at 0x2b4d0f602e8>
In [13]: plt.gcf()
Out[13]: <matplotlib.figure.Figure at 0x2b4d0f602e8>
In [14]: ax2
Out[14]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0f882e8>
In [15]: plt.gca()
Out[15]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d0f882e8>
每当创建新的Figure对象时,plt.gcf()获取到的当前的Figure对象也变成了新创建的Figure对象,因为它们的内存地址相同。fig1.add_subplot(1, 1, 1)方法,用于在Figure对象上创建一个新的Axes对象,由于Figure可以包含多个Axes对象,所以这里的参数含义是说,添加一个Axes对象到布局为一行一列的第一个位置上。
在Figure对象上插入四个Axes对象,布局为两行两列:
In [16]: fig = plt.figure()
In [17]: ax1 = fig.add_subplot(2, 2, 1)
In [18]: ax2 = fig.add_subplot(2, 2, 2)
In [19]: ax3 = fig.add_subplot(2, 2, 3)
In [20]: ax1.plot(np.random.randn(50).cumsum(), 'k--')
Out[20]: [<matplotlib.lines.Line2D at 0x2b4d1ab5550>]
In [21]: _ = ax2.hist(np.random.randn(100), bins=20, color='k')
In [22]: ax3.scatter(np.arange(30), np.arange(30) + 3 * np.random.randn(30))
Out[22]: <matplotlib.collections.PathCollection at 0x2b4d1b1f9b0>
In [23]: fig.show()
以上代码,首先创建一个Figure对象,然后插入3个Axes对象,接着分别在Axes对象上绘制了累积和线图,直方图以及散点图,最后通过fig.show()方法显示出图形如下:
常用属性设置
matplotlib绘制的图形可以设置各种属性,如坐标系的刻度,标题,标签等属性。
In [24]: fig = plt.figure()
In [25]: ax = fig.add_subplot(1, 1, 1)
In [26]: # 设置标题
In [27]: ax.set_title("Axes Example")
Out[27]: Text(0.5,1,'Axes Example')
In [28]: major_ticks = np.arange(0, 101, 20)
In [29]: minor_ticks = np.arange(0, 101, 5)
In [30]: # 设置刻度
In [31]: ax.set_xticks(major_ticks)
In [33]: ax.set_xticks(minor_ticks, minor=True)
In [34]: ax.set_yticks(major_ticks)
In [35]: ax.set_yticks(minor_ticks, minor=True)
In [36]: # 设置X, Y轴标签
In [37]: ax.set_xlabel("X axis")
Out[37]: Text(0.5,0,'X axis')
In [38]: ax.set_ylabel("Y axis")
Out[38]: Text(0,0.5,'Y axis')
In [39]: # 设备网格
In [41]: ax.grid(which='minor', alpha=0.2)
In [42]: ax.grid(which='major', alpha=0.5)
In [43]: # 添加文字
In [44]: ax.text(42.5, 50, "shiyanlou")
Out[44]: Text(42.5,50,'shiyanlou')
In [45]: fig.show()
执行以上代码,绘制的图形如下:
以上代码首先创建了一个Figure对象,接着创建了唯一一个Axes对象,后续的所有属性都基于该Axes对象设置:
- ax.set_title 设置图形的标题;
- ax.set_xticks 设置X轴的刻度,其中minor=True参数表示设置更小的刻度;
- ax.set_yticks 设置Y轴的刻度;
- ax.set_xlabel 设置X轴的标签;
- ax.set_ylabel 设置X轴的标签;
- ax.grid 开启图形的刻度网格,其中minor=True参数表示显示小刻度的网格;
- ax.text 为图形添加文字,前两个参数表示添加的文字在坐标系中的位置;
绘制曲线的颜色,设置图例,代码如下:
In [46]: x = np.linspace(0, 1, 100)
In [47]: fig = plt.figure()
In [48]: ax = fig.add_subplot(1, 1, 1)
In [49]: ax.set_title("shiyanlou")
Out[49]: Text(0.5,1,'shiyanlou')
In [50]: ax.plot(x, x ** (1/8), 'b--', label=r'$y = x^{1/8}$')
Out[50]: [<matplotlib.lines.Line2D at 0x2b4d1453908>]
In [51]: ax.plot(x, x ** 8, 'r--', label=r'$y = x^{8}$')
Out[51]: [<matplotlib.lines.Line2D at 0x2b4d1453f98>]
In [52]: ax.plot(x, x ** (1/2), 'r.', label=r'$y = x^{1/2}$')
Out[52]: [<matplotlib.lines.Line2D at 0x2b4d146c358>]
In [53]: ax.plot(x, x ** 2, 'b.', label=r'$y = x^{2}$')
Out[53]: [<matplotlib.lines.Line2D at 0x2b4d1453588>]
In [54]: ax.plot(x, x, 'g-', label=r'$y = x$')
Out[54]: [<matplotlib.lines.Line2D at 0x2b4d1453e48>]
In [55]: ax.legend()
Out[55]: <matplotlib.legend.Legend at 0x2b4d1453128>
In [56]: ax.axis([0, 1, 0, 1])
Out[56]: [0, 1, 0, 1]
In [58]: fig.show()
图形显示如下:
在上面的代码中,通过 np.linspace(0, 1, 100) 创建了 100 个值,这些值平均分布在 0 到 1 的范围内。接着我们在 Axes 对象上绘制了 5 条曲线,这 5 条曲线分别对应于 5 个函数,函数名称已经通过图例的形式显示在图形上了。
其中,我们使用类似于 ax.plot(x, x ** (1/8), 'b--', label=r'y = x^{1/8}y=x
1/8
') 方法绘制曲线,该方法有四个参数,前两个参数分别对应于 X, Y 轴的数据,绘图时会根据 x 和 x ** (1/8) 值序列确定曲线的位置。第三个参数 b-- 代表绘制的曲线是 blue 蓝色,样式是虚线。matplotlib 中有多种指定线条颜色和样式的办法,如 r-- 指明红色虚线,r. 代表红色点,更多的样式可以参考 matplotlib 文档。最后一个参数 label=r'y = x^{1/8}y=x
1/8
') 设置了线条的标签,该标签文字内容是数学公式,matplotlib 支持 LaTeX 语法显示各种数学公式。
有的时候,我们想在图形上显示曲线标签信息,这个时候可以使用 ax.legend() 方法,该方法使用曲线的标签信息(也就是绘制曲线是传入的 label 参数)自动生成图例,并将图例放到图形合适的位置。
最后为了方便查看图形,我们将图形的视口坐标设置成了 [0, 1, 0, 1] ,代表X, Y坐标轴范围都设置为 0 到 1。如果图形显示范围超过了 0 到 1 的范围,则会自动截取 0 到 1 范围内的图形进行显示。
matplotlib 绘图时可以设置很多属性,比如设置注解,设置外部图片等,这些都可以通过调用 Axes 对象相应方法进行,更多的用法可以查看 Axes 对象 API 文档。
常用图形
线形图
线性图更多时候是用来观察变量之间的函数关系,如果满足y = 2x的线性关系,那么绘制的图形是一条直线,如果没关系,则是一堆涂鸦。
使用Axes.plot方法绘制线形图:
In [78]: fig1 = plt.figure()
In [79]: ax1 = fig1.add_subplot(1, 1, 1)
In [80]: x = np.random.randn(100)
In [81]: y = np.random.randn(100)
In [83]: ax1.plot(x, y)
Out[83]: [<matplotlib.lines.Line2D at 0x2b4d2a6ba58>]
In [84]: fig1.show()
以上代码创建了两个随机的数值序列,这两个数值序列之间理论上没有任何关系,显示的图形如下:
直方图
直方图可以显示数据分布情况,X 轴一般是统计的样本,而 Y 是样本对应的统计度量。为了构建直方图,第一步是将值的范围分段,即将整个值的范围分成一系列间隔,然后计算每个间隔中有多少值。 这些值通常被指定为连续的,不重叠的变量间隔。 间隔必须相邻,并且通常是(但不是必须的)相等的大小。在 matplotlib 中可以使用 Axes.hist 方法绘制直方图:
In [85]: fig2 = plt.figure()
In [86]: ax2 = fig2.add_subplot(1, 1, 1)
In [87]: data = np.random.normal(0, 20, 1000)
In [88]: bins = np.arange(-100, 100, 5)
In [90]: ax2.hist(data, bins=bins)
Out[90]:
(array([ 0., 0., 0., 0., 0., 1., 0., 2., 2.,
4., 8., 9., 14., 30., 39., 60., 73., 83.,
88., 100., 80., 103., 75., 69., 58., 44., 25.,
7., 12., 7., 4., 0., 2., 1., 0., 0.,
0., 0., 0.]),
array([-100, -95, -90, -85, -80, -75, -70, -65, -60, -55, -50,
-45, -40, -35, -30, -25, -20, -15, -10, -5, 0, 5,
10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90, 95]),
<a list of 39 Patch objects>)
In [91]: fig2.show()
上面的代码首先通过 np.random.normal 方法,在 0 到 20 的范围产生 1000 个符合正态分布的数据值,然后通过 np.arange(-100, 100, 5) 创建了 X 轴的区间刻度。创建的图形类似下图:
散点图
散点图,将所有的数据值在图形中绘制成点,这样有多少数据值在图形中就会有多少个点。通过这些数据点可以看出数据值的分布模式,比如是否有聚类模式,或者相关关系或者发现离群点。在 matplotlib 中可以通过 Axes.scatter 绘制散点图:
In [92]: fig3 = plt.figure()
In [93]: ax3 = fig3.add_subplot(1, 1, 1)
In [94]: x = np.arange(1, 101)
In [95]: y = 20 + 3 * x + np.random.normal(0, 60, 100)
In [96]: ax3.scatter(x, y)
Out[96]: <matplotlib.collections.PathCollection at 0x2b4d4747198>
In [97]: fig3.show()
为了模拟散点图,先通过 np.arange(1, 101) 创建了 100 个数据值,接着基于此创建了 100 个 Y 轴数据,可以看到这里两组数据值之间有相关关系,因为是基于线性关系然后加上随机数生成的,绘制的散点图也证明了这点(图形向上倾斜):
箱线图
箱线图可以看出数据的分散程度,异常值等信息,箱线图根据一组数据的以下5个统计值进行绘制:
- 最小值
- 第1四分位数
- 中位数
- 第3四分位数
- 最大值
其中四分位数,是指将一组数据值按大小排序后分成四等分,每一部分包含 1/4 的数据,这种划分的分割点就是四分位数。其中第1部分和第2部分的分割点称为第1分位数Q 1, 也被称为第25百分位数,第3部分和第4部分的分割点称为第3四分位数 Q 3,也被称为第75百分位数。而第二部分和第三部分的分割点是第2四分数,也就是中位数。其中四分位距 IQRIQR 是指第三四分位数和第一分四分位数的差,也就是IQR=Q 3−Q 1。四分位距反映了中间 50% 数据的离散程度,数值越小代表数据越集中,越大代表数据越分散。
在 matplotlib 中可以使用 Axes.boxplot 方法绘制箱线图:
In [98]: fig4 = plt.figure()
In [99]: ax4 = fig4.add_subplot(1, 1, 1)
In [100]: # 产生50个小于100的随机数
In [101]: spread = np.random.rand(50) * 100
In [102]: # 产生25个值为50的数据
In [103]: center = np.ones(25) * 50
In [104]: # 异常值
In [105]: outlier_high = np.random.rand(10) * 100 +100
In [106]: outlier_low = np.random.rand(10) * -100
In [107]: data = np.concatenate((spread, center, outlier_high, outlier_low), 0)
In [108]: ax4.boxplot(data)
Out[108]:
{'boxes': [<matplotlib.lines.Line2D at 0x2b4d507eb70>],
'caps': [<matplotlib.lines.Line2D at 0x2b4d5087438>,
<matplotlib.lines.Line2D at 0x2b4d508e828>],
'fliers': [<matplotlib.lines.Line2D at 0x2b4d508ef28>],
'means': [],
'medians': [<matplotlib.lines.Line2D at 0x2b4d508eb00>],
'whiskers': [<matplotlib.lines.Line2D at 0x2b4d507ec18>,
<matplotlib.lines.Line2D at 0x2b4d5087668>]}
In [109]: fig4.show()
上面代码中,我们特意创建data数据,可以推断出该数据的中位数是50,还有一些其他异常值,绘制图形如下:
Pandas绘图
Pandas高度整合了matplotlib,可以直接调用DataFrame对象的某些方法进行绘图,更加方便的时候DataFrame的标签信息会自动绘制到图形上。
直接在Series对象上绘制线型图,代码如下:
在DataFrame对象上绘制箱线图:
In [117]: df = pd.DataFrame(np.random.rand(5, 4), columns=['A', 'B', 'C', 'D'])
In [118]: df.boxplot()
Out[118]: <matplotlib.axes._subplots.AxesSubplot at 0x2b4d8f7a4e0>
In [119]: plt.show()
其它绘图工具
Seaborn 建立在 matplotlib 之上,提供了更高级更方便的使用方式。
通过 Seaborn 可以很方面绘制出线各种常见关系的图形,比如聚类关系,线性关系,分布关系等:
In [120]: import numpy as np
In [121]: import matplotlib.pyplot as plt
In [122]: import seaborn as sns
In [123]: np.random.seed(sum(map(ord, "aesthetics")))
In [124]:
In [124]: def sinplot(flip=1):
...: x = np.linspace(0, 14, 100)
...: for i in range(1, 7):
...: plt.plot(x, np.sin(x + i * .5) * (7 - i) * flip)
...:
In [125]: sns.set()
In [126]: sinplot()
In [127]: plt.show()
如下图:
Bokeh 是一个现代化的绘图工具,底层使用 D3.js 进行绘图,也就是说 Bokeh 绘制的图形是基于浏览器显示的,这也非常符合当今的技术潮流,而且绘制的图形交互功能更加强大。
In [131]: import numpy as np
In [132]: from bokeh.layouts import gridplot
In [133]: from bokeh.plotting import figure, show, output_file
In [134]: # sin曲线
In [135]: x = np.linspace(0, 4*np.pi, 100)
In [136]: y = np.sin(x)
In [137]: p = figure(title="Shiyanlou Bokeh Example")
In [138]: p.circle(x, y, legend="sin(x)")
Out[138]: GlyphRenderer(id='9c2202a5-15c2-4282-b074-d3a964790ba7', ...)
In [139]: p.circle(x, 2*y, legend="2*sin(x)", color="orange")
Out[139]: GlyphRenderer(id='d3e000b9-c792-4321-a1a5-b51db3981841', ...)
In [140]: p.circle(x, 3*y, legend="3*sin(x)", color="green")
Out[140]: GlyphRenderer(id='ba52e19d-3238-463d-bfda-7dc9063fc070', ...)
In [141]: show(p)