2014-04-04 16 views
15

Ich zeichnete die Eigenvektoren einiger 3D-Daten und fragte mich, ob es derzeit (schon) eine Möglichkeit gibt, Pfeilspitzen auf die Linien zu setzen? Wäre toll wenn jemand einen Tipp für mich hat. enter image description hereSetzen von Pfeilspitzen auf Vektoren in Matplotlib 3D-Plot

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

Antwort

30

Pfeil Patches zu einem 3D-Plot hinzuzufügen, ist die einfache Lösung FancyArrowPatch Klasse in /matplotlib/patches.py definiert zu verwenden. Aber es funktioniert nur für 2D-Plot (zum Zeitpunkt des Schreibens), als seine posA und posB soll Tupel der Länge seinen 2.

Deshalb erstellen wir eine neue Pfeil Patch-Klasse, Namen es Arrow3D, die erbt von FancyArrowPatch. Das einzige, was wir brauchen, um seine posA und posB zu überschreiben. Um dies zu tun, initiieren wir Arrow3d mit posA und posB von (0,0) s. Die 3D-Koordinaten xs, ys, zs wurden dann unter Verwendung von proj3d.proj_transform() von 3D auf 2D projiziert, und die resultierenden 2D-Koordinaten werden posA und posB unter Verwendung der .set_position()-Methode zugewiesen, wobei die (0,0) s ersetzt wird. Auf diese Weise erhalten wir den 3D-Pfeil zum Arbeiten. Die Projektionsschritte gehen in die .draw-Methode über, die die .draw-Methode des Objekts FancyArrowPatch überschreibt.

Dies könnte wie ein Hack aussehen. Die mplot3d bietet jedoch derzeit nur (wieder nur) einfache 3D-Plot-Kapazität durch die Bereitstellung von 3D-2D-Projektionen und führt im Wesentlichen alle Plotten in 2D durch, was nicht wirklich 3D ist.

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) 

Tatsächliche Zeichnung. Beachten Sie, dass wir brauchen, um nur eine Zeile Code zu ändern, die einen neuen Pfeil Künstler hinzufügen:

# 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

Bitte überprüfen this post, die diese Frage inspiriert, für weitere Details.

+0

Dieser Code funktioniert in 'matplotlib 2.0' ohne' plt.draw() '. Ist diese Codezeile notwendig? – Seanny123

+0

@ Seanny123, optional, '.show()' Code könnte auch optional sein hängt davon ab, wie die Umgebung eingerichtet ist. Nur um der Klarheit willen nehme ich an. –