数据科学 IPython 笔记本 8.15 Matplotlib 中的三维绘图

8.15 Matplotlib 中的三维绘图

原文:Three-Dimensional Plotting in Matplotlib

译者:飞龙

协议:CC BY-NC-SA 4.0

本节是《Python 数据科学手册》(Python Data Science Handbook)的摘录。

Matplotlib 最初设计时只考虑了二维绘图。在 1.0 版本发布时,一些三维绘图工具构建在 Matplotlib 的二维显示之上,结果是一组方便(但是有限)的三维数据可视化工具。通过导入mplot3d工具包来启用三维绘图,它包含在主要的 Matplotlib 安装中:

from mpl_toolkits import mplot3d

导入子模块后,可以通过将关键字projection ='3d'传递给任何普通轴域创建例程来创建三维轴域:

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax = plt.axes(projection='3d')
png

启用此三维轴后,我们现在可以绘制各种三维绘图。三维绘图通过交互式查看图形,而非静态地在笔记本中查看图形而获益;回想一下,要使用交互式图形,运行此代码时可以使用%matplotlib notebook而不是%matplotlib inline

三维的点和线

最基本的三维图是根据(x, y, z)三元组创建的散点图的线或集合。与前面讨论的更常见的二维图类比,这些可以使用ax.plot3Dax.scatter3D函数创建。

这些调用签名几乎与它们的二维对应的签名相同,所以对于控制输出的更多信息,你可以参考“简单的折线图”和“简单的散点图”。在这里,我们将绘制一个三角螺旋线,并且在线条附近随机绘制一些点:

ax = plt.axes(projection='3d')

# 三维线条的数据
zline = np.linspace(0, 15, 1000)
xline = np.sin(zline)
yline = np.cos(zline)
ax.plot3D(xline, yline, zline, 'gray')

# 三维散点的数据
zdata = 15 * np.random.random(100)
xdata = np.sin(zdata) + 0.1 * np.random.randn(100)
ydata = np.cos(zdata) + 0.1 * np.random.randn(100)
ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='Greens');
png

请注意,默认情况下,散点会调整其透明度,以便在页面上给出深度感。虽然在静态图像中有时难以看到三维效果,但是交互式视图可以产生点的布局的一些很好的直觉。

三维等高线图

类似于我们在“密度和等高线图”中探索的等高线图,mplot3d包含使用相同输入创建三维浮雕图的工具。像二维ax.contour图一样,ax.contour3D要求所有输入数据都是二维规则网格的形式,带有每个点求得的Z数据。这里我们将展示三维正弦函数的三维等高线图:

def f(x, y):
    return np.sin(np.sqrt(x ** 2 + y ** 2))

x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)

X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
png

有时默认的视角不是最佳的,在这种情况下我们可以使用view_init方法来设置俯仰角和方位角。 在下面的示例中,我们将使用 60 度的俯仰角(即,在 x-y 平面上方 60 度)和 35 度的方位角(即绕 z 轴逆时针旋转 35 度):

ax.view_init(60, 35)
fig
png

再次注意,当使用 Matplotlib 的交互式后端之一时,通过单击和拖动可以交互式地完成这种类型的旋转。

线框和曲面图

处理网格化数据的另外两种类型的三维图是线框和曲面图。它们接受值的网格,并将其投影到指定的三维表面上,并且可以使得到的三维形式非常容易可视化。以下是使用线框图的示例:

fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_wireframe(X, Y, Z, color='black')
ax.set_title('wireframe');
png

曲面图类似于线框图,但线框的每个面都是填充多边形。将颜色表添加到填充多边形,有助于感知可视化的表面拓扑:

ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none')
ax.set_title('surface');
png

请注意,虽然曲面图的值的网格需要是二维的,但它不必是直线的。下面是一个创建部分极坐标网格的示例,与surface3D图形一起使用时,可以为我们提供我们正在可视化的函数的切面:

r = np.linspace(0, 6, 20)
theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40)
r, theta = np.meshgrid(r, theta)

X = r * np.sin(theta)
Y = r * np.cos(theta)
Z = f(X, Y)

ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                cmap='viridis', edgecolor='none');
png

表面的三角剖分

对于某些应用,上述例程所需的均匀采样网格过于严格且不方便。在这些情况下,基于三角剖分的图形可能非常有用。如果我们不从笛卡尔坐标或极坐标网格中均匀抽取,而是随机抽取一组的话,会如何呢?

theta = 2 * np.pi * np.random.random(1000)
r = 6 * np.random.random(1000)
x = np.ravel(r * np.sin(theta))
y = np.ravel(r * np.cos(theta))
z = f(x, y)

我们可以创建点的散点图,来了解我们从中采样的表面:

ax = plt.axes(projection='3d')
ax.scatter(x, y, z, c=z, cmap='viridis', linewidth=0.5);
png

这留下了许多不足之处。在这种情况下帮助我们的函数是ax.plot_trisurf,它通过首先找到在相邻点之间形成的一组三角形来创建表面(请记住,这里xyz是一维数组):

ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z,
                cmap='viridis', edgecolor='none');
png

结果当然不像用网格绘制时那样干净,但这种三角剖分的灵活性,允许一些非常有趣的三维图。例如,实际上可以使用它绘制三维莫比乌斯条带,我们将在下面看到。

示例:可视化莫比乌斯带

莫比乌斯条带类似于旋转 90 度而拼接的纸条。在拓扑上,它非常有趣,因为外观只有一面!在这里,我们将使用 Matplotlib 的三维工具来可视化这样的对象。

创建莫比乌斯带的关键是考虑它的参数化:它是一个二维条带,所以我们需要两个内在维度。 让我们称它们为θ,其范围从0,并且w的范围从-11,跨越条带的宽度:

theta = np.linspace(0, 2 * np.pi, 30)
w = np.linspace(-0.25, 0.25, 8)
w, theta = np.meshgrid(w, theta)

现在根据这个参数化,我们必须确定嵌入条带的(x, y, z)位置。

考虑到这一点,我们可能会发现有两个发生的旋转:一个是环绕其中心的位置(我们称之为θ),而另一个是条带绕其轴的扭曲(我会称其为φ)。 对于莫比乌斯条带,我们必须让条带在完整循环期间产生半个扭曲,或者Δφ = Δθ/2

phi = 0.5 * theta

现在我们使用三角函数的记忆来推导三维嵌入。我们将定义r,每个点距离中心的距离,并使用它来查找嵌入的(x, y, z)坐标:

# x-y 平面中的半径
r = 1 + w * np.cos(phi)

x = np.ravel(r * np.cos(theta))
y = np.ravel(r * np.sin(theta))
z = np.ravel(w * np.sin(phi))

最后,为了绘制对象,我们必须确保三角剖分是正确的。 执行此操作的最佳方法是,在底层参数化中定义三角剖分,然后让 Matplotlib 将此三角剖分投影到莫比乌斯条带的三维空间中。这可以通过以下方式完成:

# 在底层参数化中进行三角剖分
from matplotlib.tri import Triangulation
tri = Triangulation(np.ravel(w), np.ravel(theta))

ax = plt.axes(projection='3d')
ax.plot_trisurf(x, y, z, triangles=tri.triangles,
                cmap='viridis', linewidths=0.2);

ax.set_xlim(-1, 1); ax.set_ylim(-1, 1); ax.set_zlim(-1, 1);
png

结合所有这些技巧,可以在 Matplotlib 中创建和展示各种各样的三维对象和图案。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,701评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,649评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,037评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,994评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,018评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,796评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,481评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,370评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,868评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,014评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,153评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,832评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,494评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,039评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,437评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,131评论 2 356

推荐阅读更多精彩内容