2014-04-04 18 views
15

Wykreowałem wektorów własnych niektórych danych 3D i zastanawiałem się, czy istnieje (już) sposób na umieszczenie grotów strzałek na liniach? Byłoby wspaniale, gdyby ktoś miał dla mnie wskazówkę. enter image description hereUmieszczanie strzałek na wektorach w działce 3d matplotlib

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

Odpowiedz

30

Aby dodać poprawki strzałek działce 3D, proste rozwiązanie jest użycie FancyArrowPatch klasę zdefiniowaną w /matplotlib/patches.py. Jednak to działa tylko na wykresie 2D (w momencie pisania tego tekstu), jak i jego posAposB mają być krotki długości 2.

Dlatego tworzymy nową klasę strzałka poprawki, imię to Arrow3D, która dziedziczy FancyArrowPatch. Jedyne, czego potrzebujemy, aby zastąpić jego posA i posB. Aby to zrobić, uruchamiamy Arrow3d z posA i posB z s. . Współrzędne 3D xs, ys, zs były następnie wyświetlane z 3D na 2D przy użyciu proj3d.proj_transform(), a wynikowe współrzędne 2D są przypisywane do posA i przy użyciu metody .set_position(), zastępując s. (0,0). W ten sposób uruchomimy strzałkę 3D.

Etapy projekcji przechodzą w metodę .draw, która zastępuje metodę .draw obiektu FancyArrowPatch.

To może wyglądać jak włamanie. Jednak obecnie mplot3d zapewnia jedynie (znowu tylko) prostą zdolność do rysowania 3D, dostarczając projekcje 3D-2D i zasadniczo wykonuje wszystkie wykresy w 2D, które nie są prawdziwie 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) 

Rzeczywisty rysunek. Należy pamiętać, że musimy tylko zmienić jedną linię kodu, które dodają się nowe strzałki Wykonawca:

# 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

Proszę sprawdzić this post, która inspirowana na to pytanie, aby uzyskać więcej szczegółów.

+0

Ten kod działa w 'matplotlib 2.0' bez' plt.draw() '. Czy ta linia kodu jest niezbędna? – Seanny123

+0

@ Seanny123, opcjonalnie, kod '.show()' może być opcjonalny w zależności od konfiguracji środowiska. Dla jasności, jak sądzę. –

Powiązane problemy