数据科学 IPython 笔记本 8.15 Matplotlib

作者: 布客飞龙 | 来源:发表于2019-01-19 11:29 被阅读21次

    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 中创建和展示各种各样的三维对象和图案。

    相关文章

      网友评论

        本文标题:数据科学 IPython 笔记本 8.15 Matplotlib

        本文链接:https://www.haomeiwen.com/subject/fgiadqtx.html