Python 将箭头放在 matplotlib 的 3d 图中的向量上
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22867620/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Putting arrowheads on vectors in matplotlib's 3d plot
提问by
I plotted the eigenvectors of some 3D-data and was wondering if there is currently (already) a way to put arrowheads on the lines? Would be awesome if someone has a tip for me.
我绘制了一些 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()
采纳答案by CT Zhu
To add arrow patches to a 3D plot, the simple solution is to use FancyArrowPatch
class defined in /matplotlib/patches.py
. However, it only works for 2D plot (at the time of writing), as its posA
and posB
are supposed to be tuples of length 2.
修补程序添加箭头,3D绘图,简单的解决方案是使用FancyArrowPatch
中定义的类/matplotlib/patches.py
。但是,它仅适用于 2D 绘图(在撰写本文时),因为它posA
和posB
应该是长度为 2 的元组。
Therefore we create a new arrow patch class, name it Arrow3D
, which inherits from FancyArrowPatch
. The only thing we need to override its posA
and posB
. To do that, we initiate Arrow3d
with posA
and posB
of (0,0)
s. The 3D coordinates xs, ys, zs
was then projected from 3D to 2D using proj3d.proj_transform()
, and the resultant 2D coordinates get assigned to posA
and posB
using .set_position()
method, replacing the (0,0)
s. This way we get the 3D arrow to work.
因此我们创建了一个新的箭头补丁类,命名为Arrow3D
,它继承自FancyArrowPatch
. 我们唯一需要覆盖它的posA
和posB
。为此,我们Arrow3d
以sposA
和posB
of开始(0,0)
。xs, ys, zs
然后使用将 3D 坐标从 3D 投影到 2D proj3d.proj_transform()
,并将生成的 2D 坐标分配给posA
并posB
使用.set_position()
方法,替换(0,0)
s。这样我们就可以使 3D 箭头起作用。
The projection steps go into the .draw
method, which overrides the .draw
method of the FancyArrowPatch
object.
投影步骤进入.draw
方法,该.draw
方法覆盖FancyArrowPatch
对象的方法。
This might appear like a hack. However, the mplot3d
currently only provides (again, only) simple 3D plotting capacity by supplying 3D-2D projections and essentially does all the plotting in 2D, which is not truly 3D.
这可能看起来像一个黑客。但是,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)
Actual drawing. Note that we only need to change one line of your code, which add an new arrow artist:
实物图。请注意,我们只需要更改一行代码,即添加一个新的箭头艺术家:
# 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()
Please check this post, which inspired this question, for further details.
回答by Matt W
Another option: you can also use the plt.quiver
function, which allows you to produce arrow vectors pretty easily without any extra imports or classes.
另一种选择:您也可以使用该plt.quiver
函数,它允许您非常轻松地生成箭头向量,而无需任何额外的导入或类。
To replicate your example, you would replace:
要复制您的示例,您将替换:
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)
with:
和:
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,
)