-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor evaluation metrics to output Legolas table rows #45
Changes from 22 commits
4f521a6
06ea52d
8bf4cfc
f4fc682
4ccafc4
982ef8b
21349d6
6194a88
4a97d98
571a7fe
ac1f666
424a548
38bac6b
fb3332d
353b22e
eb874d3
8bf1f47
b353c5a
9c82ab6
abaa14e
4a999ce
3af9d23
0067fec
72be90b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ jobs: | |
fail-fast: false | ||
matrix: | ||
version: | ||
- '1.5' | ||
- '1.7' | ||
- '1.6' | ||
os: | ||
- ubuntu-latest | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,12 @@ | ||
name = "Lighthouse" | ||
uuid = "ac2c24cd-07f0-4848-96b2-1b82c3ea0e59" | ||
authors = ["Beacon Biosignals, Inc."] | ||
version = "0.13.4" | ||
version = "0.14.0" | ||
|
||
[deps] | ||
Arrow = "69666777-d1a9-59fb-9406-91d4454c9d45" | ||
hannahilea marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" | ||
Legolas = "741b9549-f6ed-4911-9fbf-4a1c0c97f0cd" | ||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" | ||
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" | ||
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" | ||
|
@@ -15,16 +17,20 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" | |
TensorBoardLogger = "899adc3e-224a-11e9-021f-63837185c80f" | ||
|
||
[compat] | ||
Arrow = "2.2" | ||
CairoMakie = "0.7" | ||
Legolas = "0.3" | ||
Makie = "0.16.5" | ||
StatsBase = "0.33" | ||
Tables = "1.7" | ||
TensorBoardLogger = "0.1" | ||
julia = "1.5" | ||
julia = "1.6" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're making a breaking version bump, taking the opportunity to additionally bump the lowest supported Julia version. |
||
|
||
[extras] | ||
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" | ||
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" | ||
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" | ||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" | ||
|
||
[targets] | ||
test = ["Test", "CairoMakie", "StableRNGs"] | ||
test = ["Test", "CairoMakie", "StableRNGs", "Tables"] |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -6,6 +6,8 @@ using StatsBase: StatsBase | |||||
using TensorBoardLogger | ||||||
using Makie | ||||||
using Printf | ||||||
using Legolas | ||||||
using Arrow | ||||||
|
||||||
include("plotting.jl") | ||||||
|
||||||
|
@@ -18,6 +20,9 @@ export confusion_matrix, accuracy, binary_statistics, cohens_kappa, calibration_ | |||||
include("classifier.jl") | ||||||
export AbstractClassifier | ||||||
|
||||||
include("row.jl") | ||||||
# TODO: export EvaluationRow ? | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we export EvaluationRow?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think so, because it’s needed to fix Arrow serialization issues - maybe we should add a comment about that too. Since normally it's not important to pass through the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do, although I'm not sure I understand the rationale? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so normally, if you do
then you get a table = (EvaluationRow(row) for row in Tables.rows(Legolas.read(path_to_table))) Then you've got a (row-oriented) table whose confusion matrix column has actual matrices. Then you can pass that to a Usually you don't need to pass through the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, gotcha! Thanks for the explanation. |
||||||
|
||||||
include("learn.jl") | ||||||
export LearnLogger, learn!, upon, evaluate!, predict! | ||||||
|
||||||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
# Arrow can't handle matrices---so when we write/read matrices, we have to pack and unpack them o_O | ||
# https://github.com/apache/arrow-julia/issues/125 | ||
vec_to_mat(mat::AbstractMatrix) = mat | ||
|
||
function vec_to_mat(vec::AbstractVector) | ||
n = isqrt(length(vec)) | ||
return reshape(vec, n, n) | ||
end | ||
|
||
vec_to_mat(x::Missing) = return missing | ||
|
||
# Redefinition is workaround for https://github.com/beacon-biosignals/Legolas.jl/issues/9 | ||
const EVALUATION_ROW_SCHEMA = Legolas.Schema("lighthouse.evaluation@1") | ||
|
||
""" | ||
const EvaluationRow = Legolas.@row("lighthouse.evaluation@1", | ||
class_labels::Union{Missing,Vector{String}}, | ||
confusion_matrix::Union{Missing,Array{Int64}} = vec_to_mat(confusion_matrix), | ||
discrimination_calibration_curve::Union{Missing, | ||
Tuple{Vector{Float64}, | ||
Vector{Float64}}}, | ||
discrimination_calibration_score::Union{Missing,Float64}, | ||
multiclass_IRA_kappas::Union{Missing,Float64}, | ||
multiclass_kappa::Union{Missing,Float64}, | ||
optimal_threshold::Union{Missing,Float64}, | ||
optimal_threshold_class::Union{Missing,Int64}, | ||
per_class_IRA_kappas::Union{Missing,Vector{Float64}}, | ||
per_class_kappas::Union{Missing,Vector{Float64}}, | ||
stratified_kappas::Union{Missing, | ||
Vector{NamedTuple{(:per_class, | ||
:multiclass, | ||
:n), | ||
Tuple{Vector{Float64}, | ||
Float64, | ||
Int64}}}}, | ||
per_class_pr_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_class_reliability_calibration_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_class_reliability_calibration_scores::Union{Missing, | ||
Vector{Float64}}, | ||
per_class_roc_aucs::Union{Missing,Vector{Float64}}, | ||
per_class_roc_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_expert_discrimination_calibration_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_expert_discrimination_calibration_scores::Union{Missing, | ||
Vector{Float64}}, | ||
spearman_correlation::Union{Missing, | ||
NamedTuple{(:ρ, :n, | ||
:ci_lower, | ||
:ci_upper), | ||
Tuple{Float64, | ||
Int64, | ||
Float64, | ||
Float64}}}, | ||
thresholds::Union{Missing,Vector{Float64}}) | ||
EvaluationRow(evaluation_row_dict::Dict{String, Any}) -> EvaluationRow | ||
|
||
A type alias for [`Legolas.Row{typeof(Legolas.Schema("lighthouse.evaluation@1@1"))}`](https://beacon-biosignals.github.io/Legolas.jl/stable/#Legolas.@row) | ||
representing the output metrics computed by [`evaluation_metrics_row`](@ref) and | ||
[`evaluation_metrics`](@ref). | ||
|
||
Constructor that takes `evaluation_row_dict` converts [`evaluation_metrics`](@ref) | ||
`Dict` of metrics results (e.g. from Lighthouse <v0.14.0) into an [`EvaluationRow`](@ref). | ||
""" | ||
const EvaluationRow = Legolas.@row("lighthouse.evaluation@1", | ||
class_labels::Union{Missing,Vector{String}}, | ||
confusion_matrix::Union{Missing,Array{Int64}} = vec_to_mat(confusion_matrix), | ||
discrimination_calibration_curve::Union{Missing, | ||
Tuple{Vector{Float64}, | ||
Vector{Float64}}}, | ||
discrimination_calibration_score::Union{Missing,Float64}, | ||
multiclass_IRA_kappas::Union{Missing,Float64}, | ||
multiclass_kappa::Union{Missing,Float64}, | ||
optimal_threshold::Union{Missing,Float64}, | ||
optimal_threshold_class::Union{Missing,Int64}, | ||
per_class_IRA_kappas::Union{Missing,Vector{Float64}}, | ||
per_class_kappas::Union{Missing,Vector{Float64}}, | ||
stratified_kappas::Union{Missing, | ||
Vector{NamedTuple{(:per_class, | ||
:multiclass, | ||
:n), | ||
Tuple{Vector{Float64}, | ||
Float64, | ||
Int64}}}}, | ||
per_class_pr_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_class_reliability_calibration_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_class_reliability_calibration_scores::Union{Missing, | ||
Vector{Float64}}, | ||
per_class_roc_aucs::Union{Missing,Vector{Float64}}, | ||
per_class_roc_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_expert_discrimination_calibration_curves::Union{Missing, | ||
Vector{Tuple{Vector{Float64}, | ||
Vector{Float64}}}}, | ||
per_expert_discrimination_calibration_scores::Union{Missing, | ||
Vector{Float64}}, | ||
spearman_correlation::Union{Missing, | ||
NamedTuple{(:ρ, :n, | ||
:ci_lower, | ||
:ci_upper), | ||
Tuple{Float64, | ||
Int64, | ||
Float64, | ||
Float64}}}, | ||
thresholds::Union{Missing,Vector{Float64}}) | ||
|
||
function Legolas.Row{S}(evaluation_row_dict::Dict) where {S<:Legolas.Schema{Symbol("lighthouse.evaluation"), | ||
1}} | ||
row = (; (Symbol(k) => v for (k, v) in pairs(evaluation_row_dict))...) | ||
return EvaluationRow(row) | ||
end | ||
|
||
""" | ||
_evaluation_row_dict(row::EvaluationRow) -> Dict{String,Any} | ||
|
||
Convert [`EvaluationRow`](@ref) into `::Dict{String, Any}` results, as are | ||
output by `[`evaluation_metrics`](@ref)` (and predated use of `EvaluationRow` in | ||
Lighthouse <v0.14.0). | ||
""" | ||
function _evaluation_row_dict(row::EvaluationRow) | ||
return Dict(string(k) => v for (k, v) in pairs(NamedTuple(row)) if !ismissing(v)) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bumping the version b/c we've changed the expected output for "failure" cases from
missing
toNaN
for any metrics output types that show up in vectors (to aid in Arrow serialization).