2016-10-06 2 views
6

J'ai 350 notes de documents qui, quand je les intrigue, ont cette forme:projection 2D Orthogonal du vecteur sur la ligne avec numpy donne un résultat erroné

docScores = [(0, 68.62998962), (1, 60.21374512), (2, 54.72480392), 
      (3, 50.71389389), (4, 49.39723969), ..., 
      (345, 28.3756237), (346, 28.37126923), 
      (347, 28.36397934), (348, 28.35762787), (349, 28.34219933)] 

J'ai posté le tableau complet here sur pastebin (elle correspond à la liste dataPoints sur le code ci-dessous).

Score distribution

Maintenant, il me fallait d'abord trouver la elbow point de cette courbe L-shape, que j'ai trouvé grâce à this post.

Maintenant, sur le graphique suivant, le vecteur rouge p représente le point de coude. Je voudrais trouver le point x=(?,?) (l'étoile jaune) sur le vecteur b qui correspond à la projection orthogonale de p sur b.

enter image description here

Le point rouge sur la parcelle est celui que j'obtenir (ce qui est évidemment faux). Je l'obtiens procédant comme suit:

b_hat = b/np.linalg.norm(b) #unit vector of b 
proj_p_onto_b = p.dot(b_hat)*b_hat 
red_point = proj_p_onto_b + s 

Maintenant, si la projection de p sur b est définie par la son point de départ et de fin, à savoir s et x (l'étoile jaune), il en résulte que proj_p_onto_b = x - s donc x = proj_p_onto_b + s ?

Ai-je commis une erreur ici?

EDIT: En réponse à @cxw, voici le code pour le calcul du point de coude:

def findElbowPoint(self, rawDocScores): 
    dataPoints = zip(range(0, len(rawDocScores)), rawDocScores) 
    s = np.array(dataPoints[0]) 
    l = np.array(dataPoints[len(dataPoints)-1]) 
    b_vect = l-s 
    b_hat = b_vect/np.linalg.norm(b_vect) 
    distances = [] 
    for scoreVec in dataPoints[1:]: 
     p = np.array(scoreVec) - s 
     proj = p.dot(b_hat)*b_hat 
     d = abs(np.linalg.norm(p - proj)) # orthgonal distance between b and the L-curve 
     distances.append((scoreVec[0], scoreVec[1], proj, d)) 

    elbow_x = max(distances, key=itemgetter(3))[0] 
    elbow_y = max(distances, key=itemgetter(3))[1] 
    proj = max(distances, key=itemgetter(3))[2] 
    max_distance = max(distances, key=itemgetter(3))[3] 

    red_point = proj + s 

EDIT: Voici le code de la parcelle:

>>> l_curve_x_values = [x[0] for x in docScores] 
>>> l_curve_y_values = [x[1] for x in docScores] 
>>> b_line_x_values = [x[0] for x in docScores] 
>>> b_line_y_values = np.linspace(s[1], l[1], len(docScores)) 
>>> p_line_x_values = l_curve_x_values[:elbow_x] 
>>> p_line_y_values = np.linspace(s[1], elbow_y, elbow_x) 
>>> plt.plot(l_curve_x_values, l_curve_y_values, b_line_x_values, b_line_y_values, p_line_x_values, p_line_y_values) 
>>> red_point = proj + s 
>>> plt.plot(red_point[0], red_point[1], 'ro') 
>>> plt.show() 
+1

Si vous utilisez le tracé pour déterminer visuellement si la solution semble correcte, vous devez tracer les données en utilisant la même échelle sur chaque axe, c'est-à-dire utiliser 'plt.axis ('equal')'. Si les axes n'ont pas d'échelles égales, les angles entre les lignes sont déformés dans le tracé. –

+0

Wow, je pense que c'est l'astuce ...laissez-moi essayer rapidement –

+0

@WarrenWeckesser Eh bien, c'était la chose, je me sens bête. Merci beaucoup d'avoir signalé cela, pouvez-vous l'écrire comme réponse pour que je puisse l'accepter? –

Répondre

3

Si vous utilisez le tracé pour déterminer visuellement si la solution semble correcte, vous devez tracer les données en utilisant la même échelle sur chaque axe, c'est-à-dire utiliser plt.axis('equal'). Si les axes n'ont pas d'échelles égales, les angles entre les lignes sont déformés dans le tracé.

1

Tout d'abord, est le point à ~ (50, 37) p ou s+p? Si p, cela pourrait être votre problème là! Si le composant Y de votre variable p est positif, vous n'obtiendrez pas les résultats escomptés lorsque vous utilisez le produit scalaire.

En supposant que le point est s+p, si un peu de gribouillages Post-It est correct,

p_len = np.linalg.norm(p) 
p_hat = p/p_len 
red_len = p_hat.dot(b_hat) * p_len # red_len = |x-s| 
    # because p_hat . b_hat = 1 * 1 * cos(angle) = |x-s|/|p| 
red_point = s + red_len * b_hat 

Non testé! YMMV. J'espère que cela t'aides.

+0

Salut! Merci pour votre réponse. Je pense que le problème réside en fait avec 'p', car j'ai utilisé le POINT' p' pour faire le produit scalaire au lieu du vecteur 'p' (qui est' s + p')! Je vais essayer votre suggestion et revenir à vous quand je reçois le résultat :) –

+0

Aaah, ignorer le dernier commentaire, le vecteur 'p' est obtenu en faisant' p (50, 37) - s (0,60) 'est que Pas correcte ? –

+0

En utilisant votre code, j'obtiens le même 'red_point' que précédemment. bizarrement, en utilisant 'p' ou' p + s' deux me donnent le même point rouge :( –