Python 金融编程第二版(三)(2)https://developer.aliyun.com/article/1559412
第七章:数据可视化
使用图片。一图胜千言。
阿瑟·布里斯班(1911 年)
本章介绍了matplotlib
和plotly
库的基本可视化能力。
尽管有许多其他可用的可视化库,但matplotlib
已经确立了自己作为基准,并且在许多情况下是一个强大而可靠的可视化工具。在标准绘图方面易于使用,在更复杂的绘图和定制方面灵活。此外,它与NumPy
和pandas
及它们提供的数据结构紧密集成。
matplotlib
仅允许以位图形式(例如 PNG 或 JPG 格式)生成图。另一方面,现代网络技术允许基于数据驱动文档(D3.js)标准创建漂亮的交互式图表,例如,可以缩放以更详细地检查某些区域。一个非常方便的库,可以使用 Python 创建这样的 D3.js 图表,是plotly
。一个小的附加库,称为Cufflinks
,将plotly
与pandas
的DataFrame
对象紧密集成,可以创建最受欢迎的金融图表(如蜡烛图)
本章主要涵盖以下主题:
“静态 2D 绘图”
本节介绍了matplotlib
,并呈现了一些典型的 2D 绘图,从最简单的到具有两个比例尺或不同子图的更高级的绘图。
“静态 3D 绘图”
基于matplotlib
,介绍了一些在特定金融应用中有用的 3D 绘图。
“交互式 2D 绘图”
本节介绍了plotly
和Cufflinks
,用于创建交互式 2D 绘图。利用Cufflinks
的QuantFigure
功能,本节还涉及典型的金融绘图,例如在技术股票分析中使用的绘图。
本章无法全面涵盖使用Python
、matplotlib
或plotly
进行数据可视化的所有方面,但它提供了这些包在金融领域的基本和重要功能的一些示例。其他示例也可以在后面的章节中找到。例如,第八章更深入地介绍了如何使用pandas
库可视化金融时间序列数据。
静态 2D 绘图
在创建样本数据并开始绘图之前,首先进行一些导入和自定义:
In [1]: import matplotlib as mpl # ① In [2]: mpl.__version__ # ② Out[2]: '2.0.2' In [3]: import matplotlib.pyplot as plt # ④ In [4]: plt.style.use('seaborn') # ⑤ In [5]: mpl.rcParams['font.family'] = 'serif' # ③ In [6]: %matplotlib inline
①
使用常见缩写mpl
导入了matplotlib
。
②
使用的matplotlib
版本。
③
将所有图的字体设置为serif
。
④
使用常见缩写plt
导入了主要的绘图(子)包。
⑤
将绘图样式设置为seaborn
(请参阅,例如,此处的概述)。
一维数据集
在接下来的所有内容中,我们将绘制存储在NumPy
的ndarray
对象或pandas
的DataFrame
对象中的数据。然而,matplotlib
当然也能够绘制存储在不同Python
格式中的数据,比如list
对象。最基本但相当强大的绘图函数是plt.plot()
。原则上,它需要两组数字:
x
值:包含x
坐标(横坐标值)的列表或数组y
值:包含y
坐标(纵坐标值)的列表或数组
提供的x
和y
值的数量必须相匹配,当然了。考虑下面的代码,其输出如图 7-1 所示。
In [7]: import numpy as np In [8]: np.random.seed(1000) # ① In [9]: y = np.random.standard_normal(20) # ② In [10]: x = np.arange(len(y)) # ③ plt.plot(x, y); # ④ # plt.savefig('../../images/ch07/mpl_01')
①
为了可重复性,设置随机数生成器的种子。
②
绘制随机数(y 值)。
③
固定整数(x 值)。
④
使用x
和y
对象调用plt.plot()
函数。
图 7-1. 绘制给定的 x 和 y 值
plt.plot()
注意到当您传递一个ndarray
对象时。在这种情况下,无需提供x
值的“额外”信息。如果您只提供y
值,则plot
将索引值视为相应的x
值。因此,以下单行代码生成完全相同的输出(参见图 7-2):
In [11]: plt.plot(y); # plt.savefig('../../images/ch07/mpl_02')
图 7-2. 绘制给定的ndarray
对象的数据
NumPy 数组和 matplotlib
您可以简单地将NumPy
的ndarray
对象传递给matplotlib
函数。matplotlib
能够解释数据结构以简化绘图。但是,请注意不要传递过大和/或复杂的数组。
由于大多数ndarray
方法再次返回一个ndarray
对象,因此您还可以通过附加方法(甚至在某些情况下可以是多个方法)来传递您的对象。通过在样本数据上调用cumsum()
方法,我们得到了这些数据的累积和,正如预期的那样,得到了不同的输出(参见图 7-3):
In [12]: plt.plot(y.cumsum()); # plt.savefig('../../images/ch07/mpl_03')
图 7-3. 绘制给定一个带有附加方法的ndarray
对象
通常,默认的绘图样式不能满足报告、出版物等的典型要求。例如,您可能希望自定义使用的字体(例如,与LaTeX
字体兼容),在轴上标记标签,或者绘制网格以提高可读性。这就是绘图样式发挥作用的地方(见上文)。此外,matplotlib
提供了大量函数来自定义绘图样式。有些函数很容易访问;对于其他一些函数,需要深入挖掘。例如,很容易访问的是那些操作轴的函数以及与网格和标签相关的函数(参见图 7-4):
In [13]: plt.plot(y.cumsum()) plt.grid(False); # ① # plt.savefig('../../images/ch07/mpl_04')
①
关闭网格。
图 7-4。没有网格的图
plt.axis()
的其他选项在表 7-1 中给出,其中大多数必须作为string
对象传递。
表 7-1。plt.axis()的选项
参数 | 描述 |
空 | 返回当前轴限制 |
off |
关闭轴线和标签 |
equal |
导致等比例缩放 |
scaled |
通过尺寸变化实现等比例缩放 |
tight |
使所有数据可见(紧缩限制) |
image |
使所有数据可见(带有数据限制) |
[xmin, xmax, ymin, ymax] |
设置给定(列表的)值的限制 |
此外,您可以直接使用plt.xlim()
和plt.ylim()
设置每个轴的最小和最大值。以下代码提供了一个示例,其输出显示在图 7-5 中:
In [14]: plt.plot(y.cumsum()) plt.xlim(-1, 20) plt.ylim(np.min(y.cumsum()) - 1, np.max(y.cumsum()) + 1); # plt.savefig('../../images/ch07/mpl_05')
图 7-5。带有自定义轴限制的图
为了更好地可读性,图表通常包含许多标签,例如标题和描述x
和y
值性质的标签。这些分别通过函数plt.title
、plt.xlabel
和plt.ylabel
添加。默认情况下,plot
绘制连续线条,即使提供了离散数据点。通过选择不同的样式选项来绘制离散点。图 7-6 叠加了(红色)点和(蓝色)线,线宽为 1.5 点:
In [15]: plt.figure(figsize=(10, 6)) # ① plt.plot(y.cumsum(), 'b', lw=1.5) # ② plt.plot(y.cumsum(), 'ro') # ③ plt.xlabel('index') # ④ plt.ylabel('value') # ⑤ plt.title('A Simple Plot'); # ⑥ # plt.savefig('../../images/ch07/mpl_06')
①
增加图的大小。
②
将数据绘制为蓝色线条,线宽为 1.5 点。
③
将数据绘制为红色(粗)点。
④
在 x 轴上放置一个标签。
⑤
在 y 轴上放置一个标签。
⑥
放置一个标题。
图 7-6。具有典型标签的图
默认情况下,plt.plot()
支持表 7-2 中的颜色缩写。
表 7-2。标准颜色缩写
字符 | 颜色 |
b |
蓝色 |
g |
绿色 |
r |
红色 |
c |
青色 |
m |
紫红色 |
y |
黄色 |
k |
黑色 |
w |
白色 |
在线和/或点样式方面,plt.plot()
支持表 7-3 中列出的字符。
表 7-3. 标准样式字符
字符 | 符号 |
- |
实线型 |
-- |
虚线型 |
-. |
短划线-点线型 |
: |
点线型 |
. |
点标记 |
, |
像素标记 |
o |
圆形标记 |
v |
向下三角形标记 |
–0— |
向上三角形标记 |
< |
向左三角形标记 |
> |
向右三角形标记 |
1 |
向下三角形标记 |
2 |
向上三角形标记 |
3 |
向左三角形标记 |
4 |
向右三角形标记 |
s |
正方形标记 |
p |
五边形标记 |
–0— |
星形标记 |
h |
六边形 1 标记 |
H |
六边形 2 标记 |
–0— |
加号标记 |
x |
X 标记 |
D |
菱形标记 |
d |
窄菱形标记 |
`pass:[ | ]` |
垂直线标记 | –0— |
任何颜色缩写都可以与任何样式字符组合。通过这种方式,您可以确保不同的数据集易于区分。正如我们将看到的,绘图样式也将反映在图例中。
二维数据集
绘制一维数据可以被视为一种特殊情况。一般来说,数据集将由多个单独的数据子集组成。与 matplotlib
一维数据一样,处理这样的数据集遵循相同的规则。但是,在这种情况下可能会出现一些额外的问题。例如,两个数据集的缩放可能有如此之大的不同,以至于不能使用相同的 y 轴和/或 x 轴缩放绘制它们。另一个问题可能是您可能希望以不同的方式可视化两个不同的数据集,例如,通过线图绘制一个数据集,通过条形图绘制另一个数据集。
以下代码生成一个具有 20×2 形状的标准正态分布(伪随机)数字的NumPy
ndarray
对象的二维样本数据集。对这个数组,调用cumsum()
方法来计算样本数据沿轴 0(即第一个维度)的累积和:
In [16]: y = np.random.standard_normal((20, 2)).cumsum(axis=0)
一般来说,您也可以将这样的二维数组传递给 plt.plot
。然后,它将自动解释包含的数据为单独的数据集(沿着轴 1,即第二个维度)。相应的图示显示在图 7-7 中:
In [17]: plt.figure(figsize=(10, 6)) plt.plot(y, lw=1.5) plt.plot(y, 'ro') plt.xlabel('index') plt.ylabel('value') plt.title('A Simple Plot'); # plt.savefig('../../images/ch07/mpl_07')
图 7-7. 带有两个数据集的图
在这种情况下,进一步的注释可能有助于更好地阅读图表。您可以为每个数据集添加单独的标签,并在图例中列出它们。 plt.legend()
接受不同的位置参数。0
代表最佳位置,意味着图例尽可能少地遮挡数据。图 7-8 展示了两个数据集的图表,这次有了图例。在生成的代码中,我们现在不再将 ndarray
对象作为一个整体传递,而是分别访问两个数据子集(y[:, 0]
和 y[:, 0]
),这样可以为它们附加单独的标签:
In [18]: plt.figure(figsize=(10, 6)) plt.plot(y[:, 0], lw=1.5, label='1st') # ① plt.plot(y[:, 1], lw=1.5, label='2nd') # ① plt.plot(y, 'ro') plt.legend(loc=0) # ② plt.xlabel('index') plt.ylabel('value') plt.title('A Simple Plot'); # plt.savefig('../../images/ch07/mpl_08')
①
为数据子集定义标签。
②
将图例放在最佳位置。
进一步的 plt.legend()
位置选项包括 表 7-4 中介绍的选项。
表 7-4. plt.legend()
的选项
位置 | 描述 |
空 | 自动 |
0 |
最佳位置 |
1 |
右上角 |
2 |
左上角 |
3 |
左下角 |
4 |
右下角 |
5 |
右 |
6 |
左中 |
7 |
右中 |
8 |
底部中心 |
9 |
上部中心 |
10 |
中心 |
图 7-8. 带标记数据集的图表
具有相似缩放的多个数据集,例如相同财务风险因素的模拟路径,可以使用单个 y 轴绘制。然而,通常数据集显示的缩放相差较大,并且使用单个 y 轴绘制此类数据通常会导致视觉信息的严重丢失。为了说明效果,我们将两个数据子集中的第一个缩放因子放大了 100 倍,并再次绘制数据(参见 图 7-9):
In [19]: y[:, 0] = y[:, 0] * 100 # ① In [20]: plt.figure(figsize=(10, 6)) plt.plot(y[:, 0], lw=1.5, label='1st') plt.plot(y[:, 1], lw=1.5, label='2nd') plt.plot(y, 'ro') plt.legend(loc=0) plt.xlabel('index') plt.ylabel('value') plt.title('A Simple Plot'); # plt.savefig('../../images/ch07/mpl_09')
①
重新调整第一个数据子集的比例。
图 7-9. 具有两个不同缩放数据集的图表
检查 图 7-9 发现,第一个数据集仍然“视觉可读”,而第二个数据集现在看起来像是直线,因为 y 轴的新缩放。在某种意义上,第二个数据集的信息现在“视觉上丢失了”。解决这个问题有两种基本方法:
- 使用两个 y 轴(左/右)
- 使用两个子图(上/下,左/右)
让我们先将第二个 y 轴引入图表中。图 7-10 现在有了两个不同的 y 轴。左侧的 y 轴用于第一个数据集,而右侧的 y 轴用于第二个数据集。因此,也有了两个图例:
In [21]: fig, ax1 = plt.subplots() # ① plt.plot(y[:, 0], 'b', lw=1.5, label='1st') plt.plot(y[:, 0], 'ro') plt.legend(loc=8) plt.xlabel('index') plt.ylabel('value 1st') plt.title('A Simple Plot') ax2 = ax1.twinx() # ② plt.plot(y[:, 1], 'g', lw=1.5, label='2nd') plt.plot(y[:, 1], 'ro') plt.legend(loc=0) plt.ylabel('value 2nd'); # plt.savefig('../../images/ch07/mpl_10')
①
定义 figure
和 axis
对象。
②
创建共享 x 轴的第二个 axis
对象。
图 7-10. 具有两个数据集和两个 y 轴的图表
关键代码行是帮助管理坐标轴的代码行。这些是接下来的代码行:
fig, ax1 = plt.subplots() ax2 = ax1.twinx()
通过使用 plt.subplots()
函数,我们直接访问基础绘图对象(图形、子图等)。例如,它允许生成一个共享 x 轴的第二个子图。在图 7-10 中,我们实际上有两个子图叠加在一起。
接下来,考虑两个分离子图的情况。这个选项给予了更多处理两个数据集的自由,就像图 7-11 所示:
In [22]: plt.figure(figsize=(10, 6)) plt.subplot(211) # ① plt.plot(y[:, 0], lw=1.5, label='1st') plt.plot(y[:, 0], 'ro') plt.legend(loc=0) plt.ylabel('value') plt.title('A Simple Plot') plt.subplot(212) # ② plt.plot(y[:, 1], 'g', lw=1.5, label='2nd') plt.plot(y[:, 1], 'ro') plt.legend(loc=0) plt.xlabel('index') plt.ylabel('value'); # plt.savefig('../../images/ch07/mpl_11')
①
定义了上方子图 1。
②
定义了下方子图 2。
图 7-11. 具有两个子图的绘图
matplotlib
figure
对象中子图的放置是通过使用特殊的坐标系统来完成的。plt.subplot()
接受三个整数作为参数,分别为 numrows
、numcols
和 fignum
(用逗号分隔或不分隔)。numrows
指定行的数量,numcols
指定列的数量,而 fignum
指定子图的数量,从 1 开始,以 numrows * numcols
结束。例如,具有九个等大小子图的图形将具有 numrows=3
,numcols=3
和 fignum=1,2,...,9
。右下方的子图将具有以下“坐标”:plt.subplot(3, 3, 9)
。
有时,选择两种不同的图表类型来可视化这样的数据可能是必要的或者是期望的。通过子图的方法,您可以自由组合 matplotlib
提供的任意类型的图表。1 图 7-12 结合了线条/点图和柱状图:
In [23]: plt.figure(figsize=(10, 6)) plt.subplot(121) plt.plot(y[:, 0], lw=1.5, label='1st') plt.plot(y[:, 0], 'ro') plt.legend(loc=0) plt.xlabel('index') plt.ylabel('value') plt.title('1st Data Set') plt.subplot(122) plt.bar(np.arange(len(y)), y[:, 1], width=0.5, color='g', label='2nd') # ① plt.legend(loc=0) plt.xlabel('index') plt.title('2nd Data Set'); # plt.savefig('../../images/ch07/mpl_12')
①
创建一个 bar
子图。
图 7-12. 将线条/点子图与柱状子图组合的绘图
Python 金融编程第二版(三)(4)https://developer.aliyun.com/article/1559414