Coverage for src/scores/continuous/standard_impl.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-02-28 12:51 +1100

1""" 

2This module contains standard methods which may be used for continuous scoring 

3""" 

4 

5import xarray as xr 

6 

7import scores.functions 

8import scores.utils 

9from scores.typing import FlexibleArrayType, FlexibleDimensionTypes 

10 

11 

12def mse( 

13 fcst: FlexibleArrayType, 

14 obs: FlexibleArrayType, 

15 reduce_dims: FlexibleDimensionTypes = None, 

16 preserve_dims: FlexibleDimensionTypes = None, 

17 weights: xr.DataArray = None, 

18 angular: bool = False, 

19): 

20 """Calculates the mean squared error from forecast and observed data. 

21 

22 Dimensional reduction is not supported for pandas and the user should 

23 convert their data to xarray to formulate the call to the metric. At 

24 most one of reduce_dims and preserve_dims may be specified. 

25 Specifying both will result in an exception. 

26 

27 Args: 

28 fcst (Union[xr.Dataset, xr.DataArray, pd.Dataframe, pd.Series]): 

29 Forecast or predicted variables in xarray or pandas. 

30 obs (Union[xr.Dataset, xr.DataArray, pd.Dataframe, pd.Series]): 

31 Observed variables in xarray or pandas. 

32 reduce_dims (Union[str, Iterable[str]): Optionally specify which 

33 dimensions to reduce when calculating MSE. All other dimensions 

34 will be preserved. 

35 preserve_dims (Union[str, Iterable[str]): Optionally specify which 

36 dimensions to preserve when calculating MSE. All other dimensions 

37 will be reduced. As a special case, 'all' will allow all dimensions 

38 to be preserved. In this case, the result will be in the same 

39 shape/dimensionality as the forecast, and the errors will be 

40 the squared error at each point (i.e. single-value comparison 

41 against observed), and the forecast and observed dimensions 

42 must match precisely. 

43 weights: Optionally provide an array for weighted averaging (e.g. by area, by latitude, 

44 by population, custom) 

45 angular: specifies whether `fcst` and `obs` are angular 

46 data (e.g. wind direction). If True, a different function is used 

47 to calculate the difference between `fcst` and `obs`, which 

48 accounts for circularity. Angular `fcst` and `obs` data should be in 

49 degrees rather than radians. 

50 

51 

52 Returns: 

53 Union[xr.Dataset, xr.DataArray, pd.Dataframe, pd.Series]: An object containing 

54 a single floating point number representing the mean absolute 

55 error for the supplied data. All dimensions will be reduced. 

56 Otherwise: Returns an object representing the mean squared error, 

57 reduced along the relevant dimensions and weighted appropriately. 

58 """ 

59 if angular: 

60 error = scores.functions.angular_difference(fcst, obs) 

61 else: 

62 error = fcst - obs 

63 squared = error * error 

64 squared = scores.functions.apply_weights(squared, weights) 

65 

66 if preserve_dims or reduce_dims: 

67 reduce_dims = scores.utils.gather_dimensions( 

68 fcst.dims, obs.dims, reduce_dims=reduce_dims, preserve_dims=preserve_dims 

69 ) 

70 

71 if reduce_dims is not None: 

72 _mse = squared.mean(dim=reduce_dims) 

73 else: 

74 _mse = squared.mean() 

75 

76 return _mse 

77 

78 

79def rmse( 

80 fcst: FlexibleArrayType, 

81 obs: FlexibleArrayType, 

82 reduce_dims: FlexibleDimensionTypes = None, 

83 preserve_dims: FlexibleDimensionTypes = None, 

84 weights: xr.DataArray = None, 

85 angular: bool = False, 

86) -> FlexibleArrayType: 

87 """Calculate the Root Mean Squared Error from xarray or pandas objects. 

88 

89 A detailed explanation is on [Wikipedia](https://en.wikipedia.org/wiki/Root-mean-square_deviation) 

90 

91 

92 Dimensional reduction is not supported for pandas and the user should 

93 convert their data to xarray to formulate the call to the metric. 

94 At most one of `reduce_dims` and `preserve_dims` may be specified. 

95 Specifying both will result in an exception. 

96 

97 

98 Args: 

99 fcst: Forecast 

100 or predicted variables in xarray or pandas. 

101 obs: Observed 

102 variables in xarray or pandas. 

103 reduce_dims: Optionally specify which dimensions to reduce when 

104 calculating RMSE. All other dimensions will be preserved. 

105 preserve_dims: Optionally specify which dimensions to preserve 

106 when calculating RMSE. All other dimensions will be reduced. 

107 As a special case, 'all' will allow all dimensions to be 

108 preserved. In this case, the result will be in the same 

109 shape/dimensionality as the forecast, and the errors will be 

110 the absolute error at each point (i.e. single-value comparison 

111 against observed), and the forecast and observed dimensions 

112 must match precisely. 

113 weights: Optionally provide an array for weighted averaging (e.g. by area, by latitude, 

114 by population, custom) 

115 angular: specifies whether `fcst` and `obs` are angular 

116 data (e.g. wind direction). If True, a different function is used 

117 to calculate the difference between `fcst` and `obs`, which 

118 accounts for circularity. Angular `fcst` and `obs` data should be in 

119 degrees rather than radians. 

120 

121 Returns: 

122 An object containing 

123 a single floating point number representing the root mean squared 

124 error for the supplied data. All dimensions will be reduced. 

125 Otherwise: Returns an object representing the root mean squared error, 

126 reduced along the relevant dimensions and weighted appropriately. 

127 

128 """ 

129 _mse = mse( 

130 fcst=fcst, obs=obs, reduce_dims=reduce_dims, preserve_dims=preserve_dims, weights=weights, angular=angular 

131 ) 

132 

133 _rmse = pow(_mse, (1 / 2)) 

134 

135 return _rmse 

136 

137 

138def mae( 

139 fcst: FlexibleArrayType, 

140 obs: FlexibleArrayType, 

141 reduce_dims: FlexibleDimensionTypes = None, 

142 preserve_dims: FlexibleDimensionTypes = None, 

143 weights: xr.DataArray = None, 

144 angular: bool = False, 

145) -> FlexibleArrayType: 

146 """Calculates the mean absolute error from forecast and observed data. 

147 

148 A detailed explanation is on [Wikipedia](https://en.wikipedia.org/wiki/Mean_absolute_error) 

149 

150 Dimensional reduction is not supported for pandas and the user should 

151 convert their data to xarray to formulate the call to the metric. 

152 At most one of reduce_dims and preserve_dims may be specified. 

153 Specifying both will result in an exception. 

154 

155 Args: 

156 fcst: Forecast or predicted variables in xarray or pandas. 

157 obs: Observed variables in xarray or pandas. 

158 reduce_dims: Optionally specify which dimensions to reduce when 

159 calculating MAE. All other dimensions will be preserved. 

160 preserve_dims: Optionally specify which dimensions to preserve when 

161 calculating MAE. All other dimensions will be reduced. As a 

162 special case, 'all' will allow all dimensions to be preserved. In 

163 this case, the result will be in the same shape/dimensionality 

164 as the forecast, and the errors will be the absolute error at each 

165 point (i.e. single-value comparison against observed), and the 

166 forecast and observed dimensions must match precisely. 

167 weights: Optionally provide an array for weighted averaging (e.g. by area, by latitude, 

168 by population, custom) 

169 angular: specifies whether `fcst` and `obs` are angular 

170 data (e.g. wind direction). If True, a different function is used 

171 to calculate the difference between `fcst` and `obs`, which 

172 accounts for circularity. Angular `fcst` and `obs` data should be in 

173 degrees rather than radians. 

174 

175 Returns: 

176 By default an xarray DataArray containing 

177 a single floating point number representing the mean absolute error for the 

178 supplied data. All dimensions will be reduced. 

179 

180 Alternatively, an xarray structure with dimensions preserved as appropriate 

181 containing the score along reduced dimensions 

182 """ 

183 if angular: 

184 error = scores.functions.angular_difference(fcst, obs) 

185 else: 

186 error = fcst - obs 

187 ae = abs(error) 

188 ae = scores.functions.apply_weights(ae, weights) 

189 

190 if preserve_dims is not None or reduce_dims is not None: 

191 reduce_dims = scores.utils.gather_dimensions(fcst.dims, obs.dims, reduce_dims, preserve_dims) 

192 

193 if reduce_dims is not None: 

194 _ae = ae.mean(dim=reduce_dims) 

195 else: 

196 _ae = ae.mean() 

197 

198 # Returns unhinted types if nonstandard types passed in, but this is useful 

199 return _ae # type: ignore