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

1"""

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

3"""

4

5 import xarray as xr

6

7 import scores.functions

8 import scores.utils

9 from scores.typing import FlexibleArrayType, FlexibleDimensionTypes

10

11

12 def 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

79 def 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

138 def 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