Python 数据可视化1

二维绘图

一维数据集

Matplotlib 能够按照不同的 Python 格式存储的数据(如列表对象)绘制图表,在下面的所有例子中,将按照存储在 NumPy ndarray 对象中的数据绘制图表。首先,生成 20 个标准正态分布(伪)随机数,保存在一个 NumPy ndarray 中:

import numpy as np

np.random.seed(1000)
y = np.random.standard_normal(20)
y

pyplot 子库中的 plot 函数是最基础的绘图函数,但是也相当强大。原则上,它需要两组数值:

  • x 值:包含 x 坐标(横坐标)的列表或者数组
  • y 值:包含 y 坐标(纵坐标)的列表或者数组

当然,x 和 y 必须一一对应,所以它们的值数量必须相等,考虑下面两行代码:

import matplotlib.pyplot as plt
%matplotlib inline

x = range(len(y))
plt.plot(x, y)

plt 会注意到何时传递了 ndarray 对象。在这种情况下,没有必要提供 x 值的“额外”信息。如果你只提供 y 值,plot 以索引值作为对应的 x 值。因此,下面一行代码会生成完全一样的输出:

plt.plot(y)

可以简单地向 Matplotlib 函数传递 Numpy ndarray 对象,函数能够解释数据结构以简化绘图工作。但是要注意,不要传递太大或者太复杂的数组。

由于大部分 ndarray 方法返回的仍然是一个 ndarray 对象,也可以附加一个方法(有些时候甚至可以附加多个方法)传递对象。我们用样板数据调用 ndarray 对象上的 cumsum 方法,可以获得这些数据的总和,并且和预想的一样得到不同的输出:

plt.plot(y.cumsum())

一般来说,默认绘图样式不能满足报表、出版等的典型要求。例如,你可能希望自定义所使用的字体(例如,为了 LaTeX 字体兼容)、在坐标轴上有标签或者为了更好的可辨认性而绘制网格。

因此,Matplotlib 提供了大量函数以自定义绘图样式。有些函数容易理解,有些则需要更深入一步研究。例如,操纵坐标轴和增加网格及标签的函数很容易理解。

plt.plot(y.cumsum())
plt.grid(True) # 添加网格线
plt.axis('tight') # 紧凑坐标轴

下面列出了 plt.axis 的其它选项,大部分都以字符串对象的形式传递:

参数 描述
Empty 返回当前坐标抽限值
off 关闭坐标轴线和标签
scaled 通过尺寸变化平衡刻度
equal 使用等刻度
tight 使所有数据可见(缩小限值)
image 使所有数据可见(使用数据限值)
[xmin,xmax,ymin,ymax] 将设置限制为给定的(一组)值

此外,可以使用 plt.xlimplt.ylim 设置每个坐标轴的最小值和最大值。下面的代码提供了一个示例:

plt.plot(y.cumsum())
plt.grid(True)
plt.xlim(-1,20)
plt.ylim(np.min(y.cumsum()) - 1,
         np.max(y.cumsum()) + 1)

为了得到更好的易理解性,图表通常包含一些标签——例如描述 x 和 y 值性质的标题和标签。这些标签分别通过 plt.titleplt.xlabeplt.ylabel 添加。默认情况下,即使提供的数据点是离散的,plot 也绘制连续线条。离散点的绘制通过选择不同的样式选项实现。下图覆盖(红色)点和线宽为 1.5 个点的(蓝色)线条:

plt.figure(figsize=(7, 4))
# the figsize parameter defines the size of the figure in(width, height)

plt.plot(y.cumsum(), 'b', lw=1.5)
plt.plot(y.cumsum(), 'ro')
plt.grid(True)
plt.axis('tight')
plt.xlabel('index')
plt.ylabel('value')
plt.title('A Simple Plot')

默认情况下,plt.plot 支持的标准颜色缩写:
bg绿 rcm品红 ykw

对于线和点的样式,plt.plot 支持下表中列出的标准样式字符:
Python 数据可视化1_第1张图片

任何颜色缩写都可以与任何样式字符组合,这样,你可以确保不同的数据集能够轻松区分。我们将会看到,绘图样式也会反映到图例中。

二维数据集

按照一维数据绘图可以看做一种特例。一般来说,数据集包含多个单独的子集,这种数据的处理遵循Matplotlib处理一维数据时的原则。但是,这种情况会出现其他一些问题,例如,两个数据集可能有不同的刻度,无法用相同的 y 或 x 轴刻度绘制。

另一个问题是,你可能希望以不同的方式可视化两组不同数据。例如,一组数据使用线图,另一组使用柱状图。

首先,我们生成一个二维样本数据集。下面的代码生成包含标准正态分布(伪)随机数的 20×2 NumPy ndarray。在这个数组上调用 cumsum 计算样本数据在 0 轴(即第一维)上的总和:

np.random.seed(2000)
y = np.random.standard_normal((20,2)).cumsum(axis=0)
y

一般来说,也可以将这样的二维数组传递给 plt.plot。它将自动把包含的数据解释为单独的数据集(沿着 1 轴,即第二维):

plt.figure(figsize=(7, 4))
plt.plot(y, lw=1.5)
plt.plot(y, 'ro')
plt.grid(True)
plt.axis('tight')
plt.xlabel('index')
plt.ylabel('value')
plt.title('A Simple Plot')

在这种情况下,进一步的注释有助于更好地理解图表,可以为每个数据集添加单独的标签并在图例中列出。plt.legend 接受不同的位置参数。0表示“最佳位置”,也就是图例尽可能少地遮盖数据。

下面是包含两个数据集的图表,这一次带有图例。在生成代码中,我们没有传递整个 ndarray 对象,而是分别访问两个数据子集(y[:, 0]y[:, 1]),可以为它们附加单独的标签:

plt.figure(figsize=(7,4))
plt.plot(y[:, 0], lw=1.5, label='1st')
plt.plot(y[:, 1], lw=1.5, label='2nd')
plt.plot(y, 'ro')
plt.grid(True)
plt.legend(loc=0)
plt.axis('tight')
plt.xlabel('index')
plt.ylabel('value')
plt.title('A Simple Plot')

plt.legend 的其它位置选项如下图所示:
Python 数据可视化1_第2张图片

多个具有类似刻度的数据集(如同一金融风险因素的模拟路径)可以用单一的 y 轴绘制。但是,数据集常常有不同的刻度,用单一 y 轴刻度绘制这种数据的图表通常会导致可视化信息的显著丢失。为了说明这种效果,我们将两个数据子集中的第一个扩大 100 倍,再次绘制该图。

y[:, 0] = y[:, 0] * 100
plt.figure(figsize=(7,4))
plt.plot(y[:, 0], lw=1.5, label='1st')
plt.plot(y[:, 1], lw=1.5, label='2nd')
plt.plot(y, 'ro')
plt.grid(True)
plt.legend(loc=0)
plt.axis('tight')
plt.xlabel('index')
plt.ylabel('value')
plt.title('A Simple Plot')

观察上述结果我们可以知道,第一个数据集仍然是“在视觉上易于辨认的”,而第二个数据集在新的 Y 轴刻度上看起来像一条直线。在某种程度上,第二个数据集的有关信息现在“在视觉上已经丢失”。解决这个问题有两种基本方法:

  • 使用 2 个 y 轴(左/右)
  • 使用两个子图(上/下,左/右)

我们首先在图表中引入第二个 y 轴。下图中有两个不同的 y 轴,左侧的 y 轴用于第一个数据集,右侧的 y 轴用于第二个数据集,因此,有两个图例:

fig, ax1 = plt.subplots()
plt.plot(y[:, 0], 'b', lw=1.5, label='1st')
plt.plot(y[:, 0], 'ro')
plt.grid(True)
plt.legend(loc=8)
plt.axis('tight')
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.subplots 函数,可以直接访问底层绘图对象(图、子图等)。例如,可以用它生成和第一个子图共享 x 轴的第二个子图。上图中有两个相互重叠的子图。

接下来,考虑两个单独子图的情况。这个选项提供了处理两个数据集的更大自由度,如下:

plt.figure(figsize=(7,5))

plt.subplot(211)
plt.plot(y[:, 0], lw=1.5, label='1st')
plt.plot(y[:, 0], 'ro')
plt.grid(True)
plt.legend(loc=0)
plt.axis('tight')
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.grid(True)
plt.legend(loc=0)
plt.axis('tight')
plt.xlabel('index')
plt.ylabel('value')

matplotlib figure 对象中子图的定位通过使用一种特殊的坐标系来实现。plt.subplot 有3个整数参数,即 numrowsnumcolsfignum(可能由逗号分隔,也可能没有)。numrows 指定行数,numcols 指定列数,fignum 指定子图编号(从 1 到 numrows×numcols)。例如,有 9 个大小相同子图的图表有 numrows=3,numcols=3,fignum=1,2,…,9。右下角的子图“坐标”如下:plt.subplot(3,3,9)

有时候,选择两个不同的图表类型来可视化数据可能是必要的或者是理想的。利用子图方法,就可以自由地组合Matplotlib提供的任意图表类型,下图组合了线图/点图和柱状图:

plt.figure(figsize=(9, 4))

plt.subplot(121)
plt.plot(y[:, 0], lw=1.5, label='1st')
plt.plot(y[:, 0], 'ro')
plt.grid(True)
plt.legend(loc=0)
plt.axis('tight')
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.grid(True)
plt.legend(loc=0)
plt.axis('tight')
plt.xlabel('index')
plt.title('2nd Data Set')

其它绘图样式和 3D 图

对于二维绘图,线图和点图可能是金融学中最重要的;这是因为许多数据集用于表示时间序列数据,此类数据通常可以由这些图表进行可视化。

散点图

我们要介绍的第一种图表是散点图,这种图表中一个数据集的值作为其他数据集的 x 值。下图展示了一个这种图表。例如,这种图表类型可用于绘制一个金融时间序列的收益和另一个时间序列收益的对比。在下面的例子中,我们将使用二维数据集和其他一些数据:

y = np.random.standard_normal((1000, 2))

plt.figure(figsize=(7, 5))
plt.plot(y[:, 0], y[:, 1], 'ro')
plt.grid(True)
plt.xlabel('1st')
plt.ylabel('2nd')
plt.title('Scatter Plot')

matplotlib 还提供了生成散点图的一个特殊函数。它的工作方式本质上相同,但是提供了一些额外的功能。这次使用的是 scatter 函数:

plt.figure(figsize=(7, 5))
plt.scatter(y[:, 0], y[:, 1], marker='o')
plt.grid(True)
plt.xlabel('1st')
plt.ylabel('2nd')
plt.title('Scatter Plot')

例如,scatter 绘图函数可以加入第三维,通过不同的颜色进行可视化,并使用彩条加以描述。为此,我们用随机数据生成第三个数据集,这次使用的是 0 和 10 之间的整数:

c = np.random.randint(0, 10, len(y))
c

下面展示的散点图有不同颜色小点表示的第三维,还有作为颜色图例的彩条:

plt.figure(figsize=(7, 5))
plt.scatter(y[:, 0], y[:, 1], c=c, marker='o')
plt.colorbar()
plt.grid(True)
plt.xlabel('1st')
plt.ylabel('2nd')
plt.title('Scatter Plot')
直方图

在同一个图表中放置两个数据集的频率值:

plt.figure(figsize=(7, 4))
plt.hist(y, label=['1st', '2nd'], bins=25)
plt.grid(True)
plt.legend(loc=0)
plt.xlabel('value')
plt.ylabel('frequency')
plt.title('Histogram')

plt.hist 主要参数的描述如下:
Python 数据可视化1_第3张图片
两个数据集的数据在直方图中堆叠:

y = np.random.standard_normal((1000, 2))

plt.figure(figsize=(7, 4))
plt.hist(y, label=['1st', '2nd'], color=['b', 'g'], stacked=True, bins=20)
plt.grid(True)
plt.legend(loc=0)
plt.xlabel('value')
plt.ylabel('frequency')
plt.title('Histogram')
箱形图

另一种实用的图表类型是箱形图。和直方图类似,箱形图可以简洁地概述数据集的特性,很容易比较多个数据集。下面展示按照我们的数据集绘制的这类图表

fig, ax = plt.subplots(figsize=(7,4))
plt.boxplot(y)
plt.grid(True)
plt.setp(ax, xticklabels=['1st', '2nd'])
plt.xlabel('data set')
plt.ylabel('value')
plt.title('Boxplot')
数学示例

在本节的最后一个例证中,我们考虑一个受到数学启迪的图表,这个例子也可以在 matplotlib 的“展厅”中找到:http://www/matplotlib.org/gallery.html 。

它绘制一个函数的图像,并以图形的方式说明了某个下限和上限之间函数图像下方区域的面积。换言之,从下限到上限之间的函数积分值。下面展示了结果图表,说明Matplotlib能够无缝地处理 LaTeX 字体设置,在图表中加入数学公式:

from matplotlib.patches import Polygon

# 第一步
def func(x):
    return 0.5 * np.exp(x) + 1

# 第二步
a, b = 0.5, 1.5
x = np.linspace(0, 2)
y = func(x)

# 第三步
fig, ax = plt.subplots(figsize=(7, 5))
plt.plot(x, y, 'b', linewidth=2)
plt.ylim(ymin=0)

# 第四步
Ix = np.linspace(a, b)
Iy = func(Ix)
verts = [(a, 0)] + list(zip(Ix, Iy)) + [(b, 0)]
poly = Polygon(verts, facecolor='0.7', edgecolor='0.5')
ax.add_patch(poly)

# 第五步
plt.text(0.5 * (a + b), 1, r"$\int_a^b fx\mathrm{d}x$", horizontalalignment='center', fontsize=20)
plt.figtext(0.9, 0.075, '$x$')
plt.figtext(0.075, 0.9, '$f(x)$')

# 最后
ax.set_xticks((a, b))
ax.set_xticklabels(('$a$', '$b$'))
ax.set_yticks([func(a), func(b)])
ax.set_yticklabels(('$f(a)$', '$f(b)$'))
plt.grid(True)

通过代码,我们来解析步骤:

  • 第一步是定义需要求取积分的函数
  • 第二步是定义积分区间,生成必需的数值
  • 第三步,绘制函数图形
  • 第四步是核心,我们使用 Polygon 函数生成阴影部分(“补丁”),表示积分面积
  • 第五步是用 plt.text 和 plt.figtext 在图表上添加数学公式和一些坐标轴标签。LaTeX 代码在两个美元符号之间传递(…
    … …)。两个函数的前两个参数都是放置对应文本的坐标值
  • 最后,我们分别设置 x 和 y 刻度标签的位置。注意,尽管我们以 LaTeX
    渲染变量名称,但是用于定位的是正确的数字值。我们还添加了网格,在这个特殊例子中,只是为了强调选中的刻度
3D 绘图

金融中从 3 维可视化中获益的领域不是太多。但是,波动率平面是一个应用领域,它可以同时展示许多到期日和行权价的隐含波动率。在下面的例子中,我们人为生成一个类似波动率平面的图表。为此,我们考虑如下因素:

  • 行权价格在 50~150 元之间
  • 到期日在 0.5~2.5 年之间

这为我们提供了一个 2 维坐标系。我们可以使用 NumPy 的 meshgrid 函数,根据两个 1 维 ndarray 对象生成这样的坐标系:

strike = np.linspace(50, 150, 24)
ttm = np.linspace(0.5, 2.5, 24)
strike, ttm = np.meshgrid(strike, ttm)

strike, ttm

上述代码将两个 1 维数组转换为 2 维数组,在必要时重复原始坐标轴值。
现在,根据新的 ndarray 对象,我们通过简单的比例调整二次函数生成模拟的隐含波动率:

iv = (strike - 100) ** 2 / (100 * strike) / ttm

iv

通过下面的代码得出一个三维图形:

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(9,6))
ax = fig.gca(projection='3d')

surf = ax.plot_surface(strike, ttm, iv, rstride=2, cstride=2, cmap=plt.cm.coolwarm, linewidth=0.5, antialiased=True)

ax.set_xlabel('strike')
ax.set_ylabel('time-to-maturity')
ax.set_zlabel('implied volatility')

fig.colorbar(surf, shrink=0.5, aspect=5)

下图提供了 plot_surface 函数使用的不同参数的描述:

参数 描述
X,Y,Z 2D 数组形式的数据值
rstride 数组行距(步长大小)
cstride 数组列距(步长大小)
color 曲面块颜色
cmap 曲面块颜色映射
facecolors 单独曲面块表面颜色
norm 将值映射为颜色的 Normalize 实例
vmin 映射到最大值

和 2 维图表一样,线样式可以由单个点或者下例中的单个三角形表示。下面用相同的数据绘制 3D 散点图,使用 view_init 函数设置不同的视角:

fig = plt.figure(figsize=(8, 5))
ax = fig.add_subplot(111, projection='3d')
ax.view_init(30, 60)

ax.scatter(strike, ttm, iv, zdir='z', s=25, c='b', marker='^')

ax.set_xlabel('strike')
ax.set_ylabel('time-to-maturity')
ax.set_zlabel('implied volatility')

https://www.lanqiao.cn/courses/881/learning/

你可能感兴趣的:(python)