Skip to content

Commit 710925a

Browse files
Fix binary metrics so they properly return NaN when appropriate (#113)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 2a4494e commit 710925a

File tree

5 files changed

+24
-24
lines changed

5 files changed

+24
-24
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Lighthouse"
22
uuid = "ac2c24cd-07f0-4848-96b2-1b82c3ea0e59"
33
authors = ["Beacon Biosignals, Inc."]
4-
version = "0.17.1"
4+
version = "0.17.2"
55

66
[deps]
77
ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd"

src/metrics.jl

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,11 @@ function binary_statistics(confusion::AbstractMatrix, class_index::Integer)
7373
false_positives = predicted_positives - true_positives
7474
false_negatives = actual_positives - true_positives
7575
true_negatives = actual_negatives - false_positives
76-
true_positive_rate = (true_positives == 0 && actual_positives == 0) ?
77-
(one(true_positives) / one(actual_positives)) :
78-
(true_positives / actual_positives)
79-
true_negative_rate = (true_negatives == 0 && actual_negatives == 0) ?
80-
(one(true_negatives) / one(actual_negatives)) :
81-
(true_negatives / actual_negatives)
82-
false_positive_rate = (false_positives == 0 && actual_negatives == 0) ?
83-
(zero(false_positives) / one(actual_negatives)) :
84-
(false_positives / actual_negatives)
85-
false_negative_rate = (false_negatives == 0 && actual_positives == 0) ?
86-
(zero(false_negatives) / one(actual_positives)) :
87-
(false_negatives / actual_positives)
88-
precision = (true_positives == 0 && predicted_positives == 0) ? NaN :
89-
(true_positives / predicted_positives)
76+
true_positive_rate = true_positives / actual_positives
77+
true_negative_rate = true_negatives / actual_negatives
78+
false_positive_rate = false_positives / actual_negatives
79+
false_negative_rate = false_negatives / actual_positives
80+
precision = true_positives / predicted_positives
9081
f1 = true_positives / (true_positives + 0.5 * (false_positives + false_negatives))
9182
return (; predicted_positives, predicted_negatives, actual_positives, actual_negatives,
9283
true_positives, true_negatives, false_positives, false_negatives,

src/utilities.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ end
1515
area_under_curve(x, y)
1616
1717
Calculates the area under the curve specified by the `x` vector and `y` vector
18-
using the trapezoidal rule. If inputs are empty, return `missing`.
18+
using the trapezoidal rule. If inputs are empty, return `NaN`. Excludes NaN entries.
1919
"""
2020
function area_under_curve(x, y)
2121
length(x) == length(y) || throw(ArgumentError("Length of inputs must match."))
22-
length(x) == 0 && return missing
22+
isempty(x) && return NaN
23+
non_nan = (!).(isnan.(x) .| isnan.(y))
24+
x = x[non_nan]
25+
y = y[non_nan]
26+
isempty(x) && return NaN
2327
auc = zero(middle(one(eltype(x)), one(eltype(y))))
2428
perms = sortperm(x)
2529
sorted_x = view(x, perms)

test/metrics.jl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@
8585
@test stats.true_negatives == 0
8686
@test stats.false_positives == 0
8787
@test stats.false_negatives == 0
88-
@test stats.true_positive_rate == 1
89-
@test stats.true_negative_rate == 1
90-
@test stats.false_positive_rate == 0
91-
@test stats.false_negative_rate == 0
88+
@test isnan(stats.true_positive_rate)
89+
@test isnan(stats.true_negative_rate)
90+
@test isnan(stats.false_positive_rate)
91+
@test isnan(stats.false_negative_rate)
9292
@test isnan(stats.precision)
9393
@test isnan(stats.f1)
9494

@@ -100,6 +100,8 @@
100100
@test stats.false_positives == 0
101101
@test stats.false_negatives == 0
102102
@test isnan(stats.f1)
103+
@test isnan(stats.true_positive_rate)
104+
@test isnan(stats.false_negative_rate)
103105

104106
c = [0 2
105107
0 6]
@@ -109,6 +111,8 @@
109111
@test stats.false_positives == 2
110112
@test stats.false_negatives == 0
111113
@test stats.f1 == 0
114+
@test isnan(stats.true_positive_rate)
115+
@test isnan(stats.false_negative_rate)
112116

113117
c = [0 0
114118
2 6]
@@ -118,7 +122,6 @@
118122
@test stats.false_positives == 0
119123
@test stats.false_negatives == 2
120124
@test stats.f1 == 0
121-
122125
for p in 0:0.1:1
123126
@test Lighthouse._cohens_kappa(p, p) == 0
124127
if p > 0

test/utilities.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ end
88

99
@testset "`Lighthouse.area_under_curve`" begin
1010
@test_throws ArgumentError Lighthouse.area_under_curve([0, 1, 2], [0, 1])
11-
@test ismissing(Lighthouse.area_under_curve([], []))
11+
@test isnan(Lighthouse.area_under_curve([], []))
12+
@test isnan(Lighthouse.area_under_curve([NaN], [NaN]))
1213
@test isapprox(Lighthouse.area_under_curve(collect(0:0.01:1), collect(0:0.01:1)), 0.5;
1314
atol=0.01)
1415
@test isapprox(Lighthouse.area_under_curve(collect(0:0.01:(2π)), sin.(0:0.01:(2π))),
@@ -17,7 +18,8 @@ end
1718

1819
@testset "`Lighthouse.area_under_curve_unit_square`" begin
1920
@test_throws ArgumentError Lighthouse.area_under_curve_unit_square([0, 1, 2], [0, 1])
20-
@test ismissing(Lighthouse.area_under_curve_unit_square([], []))
21+
@test isnan(Lighthouse.area_under_curve_unit_square([], []))
22+
@test isnan(Lighthouse.area_under_curve_unit_square([NaN], [NaN]))
2123
@test isapprox(Lighthouse.area_under_curve_unit_square(collect(0:0.01:1),
2224
collect(0:0.01:1)), 0.5;
2325
atol=0.01)

0 commit comments

Comments
 (0)