【问题标题】:Putting arrowheads on vectors in matplotlib's 3d plot在 matplotlib 3d 图中将箭头放在向量上
【发布时间】:2014-05-17 01:14:30
【问题描述】:

我绘制了一些 3D 数据的特征向量,想知道当前是否(已经)有一种方法可以将箭头放在线条上?如果有人给我小费,那就太棒了。

import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

####################################################
# This part is just for reference if
# you are interested where the data is
# coming from
# The plot is at the bottom
#####################################################

# Generate some example data
mu_vec1 = np.array([0,0,0])
cov_mat1 = np.array([[1,0,0],[0,1,0],[0,0,1]])
class1_sample = np.random.multivariate_normal(mu_vec1, cov_mat1, 20)

mu_vec2 = np.array([1,1,1])
cov_mat2 = np.array([[1,0,0],[0,1,0],[0,0,1]])
class2_sample = np.random.multivariate_normal(mu_vec2, cov_mat2, 20)

# concatenate data for PCA
samples = np.concatenate((class1_sample, class2_sample), axis=0)

# mean values
mean_x = mean(samples[:,0])
mean_y = mean(samples[:,1])
mean_z = mean(samples[:,2])

#eigenvectors and eigenvalues
eig_val, eig_vec = np.linalg.eig(cov_mat)

################################
#plotting eigenvectors
################################    

fig = plt.figure(figsize=(15,15))
ax = fig.add_subplot(111, projection='3d')

ax.plot(samples[:,0], samples[:,1], samples[:,2], 'o', markersize=10, color='green', alpha=0.2)
ax.plot([mean_x], [mean_y], [mean_z], 'o', markersize=10, color='red', alpha=0.5)
for v in eig_vec:
    ax.plot([mean_x, v[0]], [mean_y, v[1]], [mean_z, v[2]], color='red', alpha=0.8, lw=3)
ax.set_xlabel('x_values')
ax.set_ylabel('y_values')
ax.set_zlabel('z_values')

plt.title('Eigenvectors')

plt.draw()
plt.show()

【问题讨论】:

    标签: python matplotlib plot


    【解决方案1】:

    要向 3D 绘图添加箭头补丁,简单的解决方案是使用 /matplotlib/patches.py 中定义的 FancyArrowPatch 类。但是,它仅适用于 2D 绘图(在撰写本文时),因为它的 posA 和 posB 应该是长度为 2 的元组。

    因此我们创建了一个新的箭头补丁类,命名为Arrow3D,它继承自FancyArrowPatch。我们唯一需要覆盖它的posA 和posB。为此,我们使用posA 和posB 的(0,0)s 启动Arrow3d。然后使用proj3d.proj_transform() 将3D 坐标xs, ys, zs 从3D 投影到2D,并使用.set_position() 方法将生成的2D 坐标分配给posA 和posB,替换(0,0)s。这样我们就可以让 3D 箭头工作了。

    投影步骤进入.draw 方法,该方法覆盖FancyArrowPatch 对象的.draw 方法。

    这可能看起来像一个黑客。但是,mplot3d 目前仅通过提供 3D-2D 投影来提供(再次,仅)简单的 3D 绘图功能,并且基本上以 2D 进行所有绘图,这并不是真正的 3D。

    import numpy as np
    from numpy import *
    from matplotlib import pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib.patches import FancyArrowPatch
    from mpl_toolkits.mplot3d import proj3d
    
    class Arrow3D(FancyArrowPatch):
        def __init__(self, xs, ys, zs, *args, **kwargs):
            FancyArrowPatch.__init__(self, (0,0), (0,0), *args, **kwargs)
            self._verts3d = xs, ys, zs
    
        def draw(self, renderer):
            xs3d, ys3d, zs3d = self._verts3d
            xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)
            self.set_positions((xs[0],ys[0]),(xs[1],ys[1]))
            FancyArrowPatch.draw(self, renderer)
    
    ####################################################
    # This part is just for reference if
    # you are interested where the data is
    # coming from
    # The plot is at the bottom
    #####################################################
    
    # Generate some example data
    mu_vec1 = np.array([0,0,0])
    cov_mat1 = np.array([[1,0,0],[0,1,0],[0,0,1]])
    class1_sample = np.random.multivariate_normal(mu_vec1, cov_mat1, 20)
    
    mu_vec2 = np.array([1,1,1])
    cov_mat2 = np.array([[1,0,0],[0,1,0],[0,0,1]])
    class2_sample = np.random.multivariate_normal(mu_vec2, cov_mat2, 20)
    

    实物图。请注意,我们只需要更改一行代码,添加一个新的箭头艺术家:

    # concatenate data for PCA
    samples = np.concatenate((class1_sample, class2_sample), axis=0)
    
    # mean values
    mean_x = mean(samples[:,0])
    mean_y = mean(samples[:,1])
    mean_z = mean(samples[:,2])
    
    #eigenvectors and eigenvalues
    eig_val, eig_vec = np.linalg.eig(cov_mat1)
    
    ################################
    #plotting eigenvectors
    ################################    
    
    fig = plt.figure(figsize=(15,15))
    ax = fig.add_subplot(111, projection='3d')
    
    ax.plot(samples[:,0], samples[:,1], samples[:,2], 'o', markersize=10, color='g', alpha=0.2)
    ax.plot([mean_x], [mean_y], [mean_z], 'o', markersize=10, color='red', alpha=0.5)
    for v in eig_vec:
        #ax.plot([mean_x,v[0]], [mean_y,v[1]], [mean_z,v[2]], color='red', alpha=0.8, lw=3)
        #I will replace this line with:
        a = Arrow3D([mean_x, v[0]], [mean_y, v[1]], 
                    [mean_z, v[2]], mutation_scale=20, 
                    lw=3, arrowstyle="-|>", color="r")
        ax.add_artist(a)
    ax.set_xlabel('x_values')
    ax.set_ylabel('y_values')
    ax.set_zlabel('z_values')
    
    plt.title('Eigenvectors')
    
    plt.draw()
    plt.show()
    

    请查看引发此问题的this post,了解更多详情。

    【讨论】:

    • 此代码在matplotlib 2.0 中有效,而没有plt.draw()。那行代码有必要吗?
    • @Seanny123,可选,.show() 代码也可以是可选的,具体取决于环境的设置方式。我想只是为了清楚起见。
    • 很棒的答案。如果有一种方法可以控制所绘制箭头的深度定位,则可以改进。在我的情况下,箭头在数据点上可见是不可取的,但我会对此进行研究。编辑:arrow.set_zorder(-1) 成功了,就像馅饼一样简单。
    • 遗憾的是编辑队列目前已满;添加对 matplotlib>=3.5 的兼容性,在类定义中添加一个 do_3d_projection = draw 别名。
    • 哦,draw/do_3d_projection 函数还需要一个返回值(用于 z 顺序堆叠)
    【解决方案2】:

    另一种选择:您还可以使用plt.quiver 函数,它允许您非常轻松地生成箭头向量,而无需任何额外的导入或类。

    要复制您的示例,您将替换:

    for v in eig_vec:
        ax.plot([mean_x, v[0]], [mean_y, v[1]], [mean_z, v[2]], color='red', alpha=0.8, lw=3)
    

    与:

    for v in eig_vec:
        ax.quiver(
            mean_x, mean_y, mean_z, # <-- starting point of vector
            v[0] - mean_x, v[1] - mean_y, v[2] - mean_z, # <-- directions of vector
            color = 'red', alpha = .8, lw = 3,
        )
    

    【讨论】:

    • 虽然使用内置 quiver 听起来比添加自定义类更简单,但它不支持 dtype float128 作为其前六个参数:X、Y、@987654331 @、U、V 和 W。因为它默默地将参数转换为float,所以它们在我们的系统中被转换为float64。结果,如果我们给它float128 数字,它们溢出!
    • 请注意,如果您在 matplotlib 3.1.2 或更高版本中使用 3D 箭头,您将获得 3D matplotlib object 对象而不是 2D 箭袋 @matt 链接。 cfthis answer
    猜你喜欢
    • 1970-01-01
    • 2016-02-10
    • 2017-07-26
    • 1970-01-01
    • 2011-04-30
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 2021-04-24
    相关资源
    最近更新 更多