Poner puntas de flecha en vectores en la ttwig 3d de matplotlib

Grafiqué los vectores propios de algunos datos en 3D y me preguntaba si actualmente (ya) hay una manera de poner puntas de flecha en las líneas. Sería increíble si alguien tiene un consejo para mí. introduzca la descripción de la imagen aquí

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() 

Para agregar parches de flecha a una gráfica 3D, la solución simple es usar la clase FancyArrowPatch definida en /matplotlib/patches.py . Sin embargo, solo funciona para la ttwig 2D (en el momento de la escritura), ya que se supone que su posA y posB son tuplas de longitud 2.

Por lo tanto, creamos una nueva clase de parche de flecha, con el nombre Arrow3D , que se hereda de FancyArrowPatch . Lo único que necesitamos para anular su posA y posB . Para hacer eso, iniciamos Arrow3d con posA y posB de (0,0) s. Las coordenadas 3D xs, ys, zs luego se proyectaron de 3D a 2D usando proj3d.proj_transform() , y las coordenadas 2D resultantes se asignan a posA y posB usando el método .set_position() , reemplazando los (0,0) s. De esta manera conseguimos que la flecha 3D funcione.

Los pasos de proyección van al método .draw , que reemplaza al método FancyArrowPatch objeto FancyArrowPatch .

Esto podría aparecer como un hack. Sin embargo, el mplot3d actualmente solo proporciona (nuevamente, solo) capacidad de trazado 3D simple mediante el suministro de proyecciones 3D-2D y esencialmente realiza todo el trazado en 2D, que no es realmente 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) 

Dibujo actual Tenga en cuenta que solo necesitamos cambiar una línea de su código, que agrega un nuevo artista de flecha:

 # 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() 

final_output

Por favor, consulte esta publicación , que inspiró esta pregunta, para más detalles.