1+ '''Calculates accuracy statistics for fractional errors.'''
2+
3+ from collections import namedtuple
4+ import pandas as pd # type: ignore
5+ import numpy as np
6+ import matplotlib .pyplot as plt
7+ from matplotlib .axes import Axes
8+ from typing import List , Optional , cast , Union
9+
10+ PERCENTILE_STATISTICS = [0.9 , 0.99 ]
11+ COL_CDF = 'cdf'
12+ COL_ERROR = 'error'
13+
14+
15+ AccuracyResult = namedtuple ('AccuracyResult' , ['accuracy' , 'mean_absolute_error' , 'cdf_df' ])
16+
17+
18+ class AccuracyCalculator (object ):
19+ """Calculates accuracy statistics for fractional errors."""
20+
21+ def __init__ (self , error_arr : np .ndarray ):
22+ """
23+ Args:
24+ error_arr (np.ndarray): Array of fractional errors.
25+ """
26+ self .error_arr = error_arr
27+ self .accuracy_result : Union [AccuracyResult , None ] = None
28+
29+ def calculateCDF (self ) -> AccuracyResult :
30+ """
31+ Calculate the cumulative distribution function (CDF) of the errors.
32+
33+ Returns:
34+ AccuracyResult: Named tuple containing accuracy, mean absolute error, and CDF DataFrame.
35+ """
36+ # Calculate accuracy as the percentage of errors within a threshold
37+ accuracy = np .mean (np .abs (self .error_arr ) < 0.1 )
38+ # Calculate mean absolute error
39+ mean_absolute_error = np .mean (np .abs (self .error_arr ))
40+ # Create a DataFrame for CDF
41+ cdf_df = pd .DataFrame ({COL_ERROR : np .sort (self .error_arr )})
42+ cdf_df [COL_CDF ] = np .arange (1 , len (cdf_df ) + 1 ) / len (cdf_df )
43+ #
44+ self .accuracy_result = cast (AccuracyResult , self .accuracy_result )
45+ self .accuracy_result = AccuracyResult (accuracy = accuracy ,
46+ mean_absolute_error = mean_absolute_error , cdf_df = cdf_df )
47+ return self .accuracy_result
48+
49+ @classmethod
50+ def getStatistics (cls , error_arr : np .ndarray ,
51+ percentiles : List [float ]= PERCENTILE_STATISTICS ) -> List [float ]:
52+ """
53+ Get accuracy statistics for the given error array.
54+
55+ Args:
56+ error_arr (np.ndarray): Array of fractional errors.
57+ percentiles (List[float]): List of percentiles to calculate.
58+
59+ Returns:
60+ List[float]: List of accuracy statistics for the specified percentiles.
61+ """
62+ ser = pd .Series (error_arr )
63+ results = ser .quantile (percentiles ).values .tolist () # type: ignore
64+ return results
65+
66+ def plotCDF (self , ax : Optional [Axes ] = None ,
67+ is_plot : bool = True ) -> None :
68+ """
69+ Plot the CDF of the errors.
70+
71+ Args:
72+ ax (plt.Axes, optional): Matplotlib axes object. If None, creates a new figure.
73+ is_plot (bool, optional): Whether to plot the CDF. Defaults to True.
74+ """
75+ if ax is None :
76+ plt .figure (figsize = (10 , 6 ))
77+ ax = plt .gca ()
78+ # Create data
79+ cdf_df = self .calculateCDF ().cdf_df
80+ # Plot
81+ ax .plot (cdf_df [COL_ERROR ], cdf_df [COL_CDF ], marker = 'o' )
82+ ax .set_title ('Cumulative Distribution Function (CDF) of Errors' )
83+ ax .set_xlabel ('Error' )
84+ ax .set_ylabel ('CDF' )
85+ ax .grid ()
86+ #
87+ if is_plot :
88+ plt .show ()
89+
90+ def plotCDFComparison (self , calculator : 'AccuracyCalculator' ,
91+ names : Optional [List [str ]] = None ,
92+ ax : Optional [Axes ] = None , is_plot : bool = True ) -> None :
93+ """
94+ Plot the CDF of the errors from this calculator and another calculator.
95+
96+ Args:
97+ calculator (AccuracyCalculator): Another AccuracyCalculator instance.
98+ names (Optional[List[str]]): List of names for the plots.
99+ ax (plt.Axes, optional): Matplotlib axes object. If None, creates a new figure.
100+ is_plot (bool, optional): Whether to plot the CDF. Defaults to True.
101+ """
102+ if ax is None :
103+ plt .figure (figsize = (10 , 6 ))
104+ ax = plt .gca ()
105+ if names is None :
106+ names = ['Current' , 'Other' ]
107+ # Create data
108+ cdf_df = self .calculateCDF ().cdf_df
109+ other_cdf_df = calculator .calculateCDF ().cdf_df
110+ # Plot
111+ ax .plot (cdf_df [COL_ERROR ], cdf_df [COL_CDF ], marker = 'o' ,
112+ label = names [0 ])
113+ ax .plot (other_cdf_df [COL_ERROR ], other_cdf_df [COL_CDF ], marker = 'x' ,
114+ label = names [1 ])
115+ ax .set_title ('Cumulative Distribution Function (CDF) Comparison' )
116+ ax .set_xlabel ('Error' )
117+ ax .set_ylabel ('CDF' )
118+ ax .legend ()
119+ if is_plot :
120+ plt .show ()
0 commit comments