@@ -301,12 +301,11 @@ def _update_curve(self, items: List, show: bool, add: Callable):
301301 add (x [mask ], y [mask ], self .palette [val ].darker (135 ))
302302
303303 def _add_ellipse (self , x : np .ndarray , y : np .ndarray , color : QColor ) -> np .ndarray :
304- # https://stats.stackexchange.com/questions/577628/trying-to-understand-how-to-calculate-a-hotellings-t2-confidence-ellipse
305- # https://stackoverflow.com/questions/66179256/how-to-check-if-a-point-is-in-an-ellipse-in-python
304+ # https://github.com/ChristianGoueguel/HotellingEllipse/blob/master/R/ellipseCoord.R
306305 points = np .vstack ([x , y ]).T
307306 mu = np .mean (points , axis = 0 )
308307 cov = np .cov (* (points - mu ).T )
309- _ , vects = np .linalg .eig (cov )
308+ vals , vects = np .linalg .eig (cov )
310309 angle = math .atan2 (vects [1 , 0 ], vects [0 , 0 ])
311310 matrix = np .array ([[np .cos (angle ), - np .sin (angle )],
312311 [np .sin (angle ), np .cos (angle )]])
@@ -315,8 +314,8 @@ def _add_ellipse(self, x: np.ndarray, y: np.ndarray, color: QColor) -> np.ndarra
315314 f = ss .f .ppf (0.95 , 2 , n - 2 )
316315 f = f * 2 * (n - 1 ) / (n - 2 )
317316 m = [np .pi * i / 100 for i in range (201 )]
318- cx = np .cos (m ) * np .sqrt (cov [ 0 , 0 ] * f )
319- cy = np .sin (m ) * np .sqrt (cov [ 1 , 1 ] * f )
317+ cx = np .cos (m ) * np .sqrt (vals [ 0 ] * f )
318+ cy = np .sin (m ) * np .sqrt (vals [ 1 ] * f )
320319
321320 pts = np .vstack ([cx , cy ])
322321 pts = matrix .dot (pts )
@@ -413,7 +412,8 @@ def _add_controls(self):
413412 gui .checkBox (
414413 self ._plot_box , self ,
415414 value = "graph.show_ellipse" ,
416- label = "Show ellipse" ,
415+ label = "Show confidence ellipse" ,
416+ tooltip = "Hotelling's T² confidence ellipse (α=95%)" ,
417417 callback = self .graph .update_ellipse )
418418
419419 def _add_controls_axis (self ):
0 commit comments