Coverage for tests/categorical/test_binary.py: 100%

1"""

2 Tests scores.categorical.binary

3"""

4 import dask

5 import dask.array

6 import numpy as np

7 import pytest

8 import xarray as xr

9

10 from scores.categorical import probability_of_detection, probability_of_false_detection

11

12 fcst0 = xr.DataArray(data=[[0, 0], [0, np.nan]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

13 fcst1 = xr.DataArray(data=[[1, 1], [1, np.nan]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

14 fcst_mix = xr.DataArray(data=[[0, 1], [1, np.nan]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

15 obs0 = xr.DataArray(data=[[0, 0], [0, 0]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

16 obs1 = xr.DataArray(data=[[1, 1], [1, 1]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

17 obs_mix = xr.DataArray(data=[[0, 1], [1, np.nan]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

18 fcst_bad = xr.DataArray(data=[[0, 0.2], [0, np.nan]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

19 obs_bad = xr.DataArray(data=[[0, 3], [0, np.nan]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

20 weight_array = xr.DataArray(data=[[2, 1], [1, 1]], dims=["a", "b"], coords={"a": [100, 200], "b": [500, 600]})

21

22 expected_pod0 = xr.DataArray(data=np.nan, name="ctable_probability_of_detection")

23 expected_pod1 = xr.DataArray(data=1, name="ctable_probability_of_detection")

24 expected_pod2 = xr.DataArray(data=0, name="ctable_probability_of_detection")

25 expected_pod3 = xr.DataArray(data=2 / 3, name="ctable_probability_of_detection")

26 expected_poda = xr.DataArray(data=[0.5, 1], dims="b", coords={"b": [500, 600]}, name="ctable_probability_of_detection")

27 expected_pod_weighted = xr.DataArray(data=1 / 2, name="ctable_probability_of_detection")

28

29

30 expected_pofd0 = xr.DataArray(data=0, name="ctable_probability_of_false_detection")

31 expected_pofd1 = xr.DataArray(data=np.nan, name="ctable_probability_of_false_detection")

32 expected_pofd2 = xr.DataArray(data=1, name="ctable_probability_of_false_detection")

33 expected_pofd3 = xr.DataArray(data=2 / 3, name="ctable_probability_of_false_detection")

34 expected_pofda = xr.DataArray(

35 data=[0.5, 1], dims="b", coords={"b": [500, 600]}, name="ctable_probability_of_false_detection"

36)

37 expected_pofd_weighted = xr.DataArray(data=1 / 2, name="ctable_probability_of_detection")

38

39

40@pytest.mark.parametrize(

41 ("fcst", "obs", "reduce_dims", "check_args", "weights", "expected"),

42 [

43 (fcst0, obs0, None, True, None, expected_pod0), # Fcst zeros, obs zeros

44 (fcst1, obs1, None, True, None, expected_pod1), # Fcst ones, obs ones

45 (fcst1, obs0, None, True, None, expected_pod0), # Fcst ones, obs zeros

46 (fcst0, obs1, None, True, None, expected_pod2), # Fcst zeros, obs ones

47 (fcst_mix, obs1, None, True, None, expected_pod3), # Fcst mixed, obs ones

48 (fcst1, obs_mix, None, True, None, expected_pod1), # Fcst ones, obs mixed

49 (fcst_mix, obs1, "a", True, None, expected_poda), # Fcst mix, obs ones, only reduce one dim

50 (fcst_bad, obs0, None, False, None, expected_pod0), # Don't check for bad data

51 (fcst_mix, obs1, None, True, weight_array, expected_pod_weighted), # Fcst mixed, obs ones, with weights

52 (

53 xr.Dataset({"array1": fcst0, "array2": fcst1}),

54 xr.Dataset({"array1": obs0, "array2": obs1}),

55 None,

56 True,

57 None,

58 xr.Dataset({"array1": expected_pod0, "array2": expected_pod1}),

59 ), # Test with DataSet for inputs

60 ],

61)

62 def test_probability_of_detection(fcst, obs, reduce_dims, check_args, weights, expected):

63 """Tests probability_of_detection"""

64 result = probability_of_detection(fcst, obs, reduce_dims=reduce_dims, weights=weights, check_args=check_args)

65 xr.testing.assert_equal(result, expected)

66

67

68 def test_pod_dask():

69 "Tests that probability_of_detection works with dask"

70 result = probability_of_detection(fcst_mix.chunk(), obs1.chunk())

71 assert isinstance(result.data, dask.array.Array)

72 result = result.compute()

73 assert isinstance(result.data, np.ndarray)

74 xr.testing.assert_equal(result, expected_pod3)

75

76

77@pytest.mark.parametrize(

78 ("fcst", "obs", "error_msg"),

79 [

80 (fcst_bad, obs0, "`fcst` contains values that are not in the set {0, 1, np.nan}"),

81 (fcst0, obs_bad, "`obs` contains values that are not in the set {0, 1, np.nan}"),

82 ],

83)

84 def test_probability_of_detection_raises(fcst, obs, error_msg):

85 """test probability_of_detection raises"""

86 with pytest.raises(ValueError) as exc:

87 probability_of_detection(fcst, obs)

88 assert error_msg in str(exc.value)

89

90

91@pytest.mark.parametrize(

92 ("fcst", "obs", "reduce_dims", "check_args", "weights", "expected"),

93 [

94 (fcst0, obs0, None, True, None, expected_pofd0), # Fcst zeros, obs zeros

95 (fcst1, obs1, None, True, None, expected_pofd1), # Fcst ones, obs ones

96 (fcst1, obs0, None, True, None, expected_pofd2), # Fcst ones, obs zeros

97 (fcst0, obs1, None, True, None, expected_pofd1), # Fcst zeros, obs ones

98 (fcst_mix, obs0, None, True, None, expected_pofd3), # Fcst ones, obs mixed

99 (fcst_mix, obs0, "a", True, None, expected_poda), # Fcst mix, obs ones, only reduce one dim

100 (fcst_bad, obs0, None, False, None, expected_pofd0), # Don't check for bad data

101 (fcst_mix, obs0, None, True, weight_array, expected_pofd_weighted), # Fcst mixed, obs ones, with weights

102 (

103 xr.Dataset({"array1": fcst0, "array2": fcst1}),

104 xr.Dataset({"array1": obs0, "array2": obs1}),

105 None,

106 True,

107 None,

108 xr.Dataset({"array1": expected_pofd0, "array2": expected_pofd1}),

109 ), # Test with DataSet for inputs

110 ],

111)

112 def test_probability_of_false_detection(fcst, obs, reduce_dims, check_args, weights, expected):

113 """Tests probability_of_false_detection"""

114 result = probability_of_false_detection(fcst, obs, reduce_dims=reduce_dims, weights=weights, check_args=check_args)

115 xr.testing.assert_equal(result, expected)

116

117

118 def test_pofd_dask():

119 "Tests that probability_of_false_detection works with dask"

120 result = probability_of_false_detection(fcst_mix.chunk(), obs0.chunk())

121 assert isinstance(result.data, dask.array.Array)

122 result = result.compute()

123 assert isinstance(result.data, np.ndarray)

124 xr.testing.assert_equal(result, expected_pofd3)

125

126

127@pytest.mark.parametrize(

128 ("fcst", "obs", "error_msg"),

129 [

130 (fcst_bad, obs0, "`fcst` contains values that are not in the set {0, 1, np.nan}"),

131 (fcst0, obs_bad, "`obs` contains values that are not in the set {0, 1, np.nan}"),

132 ],

133)

134 def test_probability_of_false_detection_raises(fcst, obs, error_msg):

135 """test probability_of_false_detection raises"""

136 with pytest.raises(ValueError) as exc:

137 probability_of_false_detection(fcst, obs)

138 assert error_msg in str(exc.value)