@@ -1809,7 +1809,7 @@ def test_precision_recall_f1_score_with_an_empty_prediction(
1809
1809
1810
1810
assert_array_almost_equal (p , [zero_division_expected , 1.0 , 1.0 , 0.0 ], 2 )
1811
1811
assert_array_almost_equal (r , [0.0 , 0.5 , 1.0 , zero_division_expected ], 2 )
1812
- expected_f = 0 if not np . isnan ( zero_division_expected ) else np . nan
1812
+ expected_f = 0
1813
1813
assert_array_almost_equal (f , [expected_f , 1 / 1.5 , 1 , expected_f ], 2 )
1814
1814
assert_array_almost_equal (s , [1 , 2 , 1 , 0 ], 2 )
1815
1815
@@ -1826,7 +1826,7 @@ def test_precision_recall_f1_score_with_an_empty_prediction(
1826
1826
1827
1827
assert_almost_equal (p , (2 + value_to_sum ) / values_to_average )
1828
1828
assert_almost_equal (r , (1.5 + value_to_sum ) / values_to_average )
1829
- expected_f = (2 / 3 + 1 ) / ( 4 if not np . isnan ( zero_division_expected ) else 2 )
1829
+ expected_f = (2 / 3 + 1 ) / 4
1830
1830
assert_almost_equal (f , expected_f )
1831
1831
assert s is None
1832
1832
assert_almost_equal (
@@ -1859,7 +1859,7 @@ def test_precision_recall_f1_score_with_an_empty_prediction(
1859
1859
)
1860
1860
assert_almost_equal (p , 3 / 4 if zero_division_expected == 0 else 1.0 )
1861
1861
assert_almost_equal (r , 0.5 )
1862
- values_to_average = 4 if not np . isnan ( zero_division_expected ) else 3
1862
+ values_to_average = 4
1863
1863
assert_almost_equal (f , (2 * 2 / 3 + 1 ) / values_to_average )
1864
1864
assert s is None
1865
1865
assert_almost_equal (
@@ -1877,12 +1877,12 @@ def test_precision_recall_f1_score_with_an_empty_prediction(
1877
1877
assert_almost_equal (r , 1 / 3 )
1878
1878
assert_almost_equal (f , 1 / 3 )
1879
1879
assert s is None
1880
- expected_result = { 1 : 0.666 , np . nan : 1.0 }
1880
+ expected_result = 0.333
1881
1881
assert_almost_equal (
1882
1882
fbeta_score (
1883
1883
y_true , y_pred , beta = 2 , average = "samples" , zero_division = zero_division
1884
1884
),
1885
- expected_result . get ( zero_division , 0.333 ) ,
1885
+ expected_result ,
1886
1886
2 ,
1887
1887
)
1888
1888
@@ -2012,7 +2012,7 @@ def test_prf_warnings():
2012
2012
f , w = precision_recall_fscore_support , UndefinedMetricWarning
2013
2013
for average in [None , "weighted" , "macro" ]:
2014
2014
msg = (
2015
- "Precision and F-score are ill-defined and "
2015
+ "Precision is ill-defined and "
2016
2016
"being set to 0.0 in labels with no predicted samples."
2017
2017
" Use `zero_division` parameter to control"
2018
2018
" this behavior."
@@ -2021,7 +2021,7 @@ def test_prf_warnings():
2021
2021
f ([0 , 1 , 2 ], [1 , 1 , 2 ], average = average )
2022
2022
2023
2023
msg = (
2024
- "Recall and F-score are ill-defined and "
2024
+ "Recall is ill-defined and "
2025
2025
"being set to 0.0 in labels with no true samples."
2026
2026
" Use `zero_division` parameter to control"
2027
2027
" this behavior."
@@ -2031,7 +2031,7 @@ def test_prf_warnings():
2031
2031
2032
2032
# average of per-sample scores
2033
2033
msg = (
2034
- "Precision and F-score are ill-defined and "
2034
+ "Precision is ill-defined and "
2035
2035
"being set to 0.0 in samples with no predicted labels."
2036
2036
" Use `zero_division` parameter to control"
2037
2037
" this behavior."
@@ -2040,7 +2040,7 @@ def test_prf_warnings():
2040
2040
f (np .array ([[1 , 0 ], [1 , 0 ]]), np .array ([[1 , 0 ], [0 , 0 ]]), average = "samples" )
2041
2041
2042
2042
msg = (
2043
- "Recall and F-score are ill-defined and "
2043
+ "Recall is ill-defined and "
2044
2044
"being set to 0.0 in samples with no true labels."
2045
2045
" Use `zero_division` parameter to control"
2046
2046
" this behavior."
@@ -2050,7 +2050,7 @@ def test_prf_warnings():
2050
2050
2051
2051
# single score: micro-average
2052
2052
msg = (
2053
- "Precision and F-score are ill-defined and "
2053
+ "Precision is ill-defined and "
2054
2054
"being set to 0.0 due to no predicted samples."
2055
2055
" Use `zero_division` parameter to control"
2056
2056
" this behavior."
@@ -2059,7 +2059,7 @@ def test_prf_warnings():
2059
2059
f (np .array ([[1 , 1 ], [1 , 1 ]]), np .array ([[0 , 0 ], [0 , 0 ]]), average = "micro" )
2060
2060
2061
2061
msg = (
2062
- "Recall and F-score are ill-defined and "
2062
+ "Recall is ill-defined and "
2063
2063
"being set to 0.0 due to no true samples."
2064
2064
" Use `zero_division` parameter to control"
2065
2065
" this behavior."
@@ -2069,7 +2069,7 @@ def test_prf_warnings():
2069
2069
2070
2070
# single positive label
2071
2071
msg = (
2072
- "Precision and F-score are ill-defined and "
2072
+ "Precision is ill-defined and "
2073
2073
"being set to 0.0 due to no predicted samples."
2074
2074
" Use `zero_division` parameter to control"
2075
2075
" this behavior."
@@ -2078,7 +2078,7 @@ def test_prf_warnings():
2078
2078
f ([1 , 1 ], [- 1 , - 1 ], average = "binary" )
2079
2079
2080
2080
msg = (
2081
- "Recall and F-score are ill-defined and "
2081
+ "Recall is ill-defined and "
2082
2082
"being set to 0.0 due to no true samples."
2083
2083
" Use `zero_division` parameter to control"
2084
2084
" this behavior."
@@ -2090,14 +2090,20 @@ def test_prf_warnings():
2090
2090
warnings .simplefilter ("always" )
2091
2091
precision_recall_fscore_support ([0 , 0 ], [0 , 0 ], average = "binary" )
2092
2092
msg = (
2093
- "Recall and F-score are ill-defined and "
2093
+ "F-score is ill-defined and being set to 0.0 due to no true nor "
2094
+ "predicted samples. Use `zero_division` parameter to control this"
2095
+ " behavior."
2096
+ )
2097
+ assert str (record .pop ().message ) == msg
2098
+ msg = (
2099
+ "Recall is ill-defined and "
2094
2100
"being set to 0.0 due to no true samples."
2095
2101
" Use `zero_division` parameter to control"
2096
2102
" this behavior."
2097
2103
)
2098
2104
assert str (record .pop ().message ) == msg
2099
2105
msg = (
2100
- "Precision and F-score are ill-defined and "
2106
+ "Precision is ill-defined and "
2101
2107
"being set to 0.0 due to no predicted samples."
2102
2108
" Use `zero_division` parameter to control"
2103
2109
" this behavior."
@@ -2818,6 +2824,24 @@ def test_classification_metric_pos_label_types(metric, classes):
2818
2824
assert not np .any (np .isnan (result ))
2819
2825
2820
2826
2827
+ @pytest .mark .parametrize (
2828
+ "y_true, y_pred, expected_score" ,
2829
+ [
2830
+ (np .array ([0 , 1 ]), np .array ([1 , 0 ]), 0.0 ),
2831
+ (np .array ([0 , 1 ]), np .array ([0 , 1 ]), 1.0 ),
2832
+ (np .array ([0 , 1 ]), np .array ([0 , 0 ]), 0.0 ),
2833
+ (np .array ([0 , 0 ]), np .array ([0 , 0 ]), 1.0 ),
2834
+ ],
2835
+ )
2836
+ def test_f1_for_small_binary_inputs_with_zero_division (y_true , y_pred , expected_score ):
2837
+ """Check the behaviour of `zero_division` for f1-score.
2838
+
2839
+ Non-regression test for:
2840
+ https://github.com/scikit-learn/scikit-learn/issues/26965
2841
+ """
2842
+ assert f1_score (y_true , y_pred , zero_division = 1.0 ) == pytest .approx (expected_score )
2843
+
2844
+
2821
2845
@pytest .mark .parametrize (
2822
2846
"scoring" ,
2823
2847
[
0 commit comments