@@ -200,6 +200,7 @@ def __init__(self):
200200
201201 self .embedding = None # type: Optional[np.ndarray]
202202 self .effective_matrix = None # type: Optional[DistMatrix]
203+ self .stress = None
203204
204205 self .size_model = self .gui .points_models [2 ]
205206 self .size_model .order = \
@@ -241,6 +242,8 @@ def _add_controls_optimization(self):
241242 sizePolicy = (QSizePolicy .MinimumExpanding , QSizePolicy .Fixed ),
242243 callback = self .__refresh_rate_combo_changed ),
243244 1 , 1 )
245+ self .stress_label = QLabel ("Kruskal Stress: -" )
246+ grid .addWidget (self .stress_label , 2 , 0 , 1 , 3 )
244247
245248 def __refresh_rate_combo_changed (self ):
246249 if self .task is not None :
@@ -392,17 +395,32 @@ def on_partial_result(self, result: Result):
392395 if need_update :
393396 self .graph .update_coordinates ()
394397 self .graph .update_density ()
398+ self .update_stress ()
395399
396400 def on_done (self , result : Result ):
397401 assert isinstance (result .embedding , np .ndarray )
398402 assert len (result .embedding ) == len (self .effective_matrix )
399403 self .embedding = result .embedding
400404 self .graph .update_coordinates ()
401405 self .graph .update_density ()
406+ self .update_stress ()
402407 self .run_button .setText ("Start" )
403408 self .step_button .setEnabled (True )
404409 self .commit .deferred ()
405410
411+ def update_stress (self ):
412+ self .stress = self ._compute_stress ()
413+ stress_val = "-" if self .stress is None else f"{ self .stress :.3f} "
414+ self .stress_label .setText (f"Kruskal Stress: { stress_val } " )
415+
416+ def _compute_stress (self ):
417+ if self .embedding is None or self .effective_matrix is None :
418+ return None
419+ actual = scipy .spatial .distance .pdist (self .embedding )
420+ actual = scipy .spatial .distance .squareform (actual )
421+ return np .sqrt (np .sum ((actual - self .effective_matrix ) ** 2 )
422+ / (np .sum (self .effective_matrix ** 2 ) or 1 ))
423+
406424 def on_exception (self , ex : Exception ):
407425 if isinstance (ex , MemoryError ):
408426 self .Error .out_of_memory ()
@@ -436,6 +454,7 @@ def jitter_coord(part):
436454 # (Random or PCA), restarting the optimization if necessary.
437455 if self .effective_matrix is None :
438456 self .graph .reset_graph ()
457+ self .update_stress ()
439458 return
440459
441460 X = self .effective_matrix
@@ -451,6 +470,8 @@ def jitter_coord(part):
451470 # restart the optimization if it was interrupted.
452471 if self .task is not None :
453472 self ._run ()
473+ else :
474+ self .update_stress ()
454475
455476 def handleNewSignals (self ):
456477 self ._initialize ()
@@ -473,12 +494,12 @@ def setup_plot(self):
473494
474495 def get_size_data (self ):
475496 if self .attr_size == "Stress" :
476- return self .stress (self .embedding , self .effective_matrix )
497+ return self .get_stress (self .embedding , self .effective_matrix )
477498 else :
478499 return super ().get_size_data ()
479500
480501 @staticmethod
481- def stress (X , distD ):
502+ def get_stress (X , distD ):
482503 assert X .shape [0 ] == distD .shape [0 ] == distD .shape [1 ]
483504 D1_c = scipy .spatial .distance .pdist (X , metric = "euclidean" )
484505 D1 = scipy .spatial .distance .squareform (D1_c , checks = False )
0 commit comments