///|
pub fn judged_recall_at(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
) -> Double {
let relevance_map = build_relevance_map(qrels)
let ranked = sorted_run_items(run)
let limit = Int::min(Int::max(cutoff, 0), ranked.length())
let mut judged = 0
for index in 0.. Double {
if run.is_empty() {
return 0.0
}
let mut total = 0.0
for item in run {
total += item.score
}
total / Double::from_int(run.length())
}
///|
pub fn score_stddev(run : Array[RetrievedDoc]) -> Double {
if run.is_empty() {
return 0.0
}
let mean = score_mean(run)
let mut squared = 0.0
for item in run {
let delta = item.score - mean
squared += delta * delta
}
Double::sqrt(squared / Double::from_int(run.length()))
}
///|
fn bpref_denominator(relevant_total : Int, non_relevant_total : Int) -> Int {
Int::max(1, Int::min(relevant_total, Int::max(non_relevant_total, 1)))
}
///|
pub fn bpref_at(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
) -> Double {
let relevance_map = build_relevance_map(qrels)
let mut relevant_total = 0
let mut non_relevant_total = 0
for _, relevance in relevance_map {
if relevance >= threshold {
relevant_total += 1
} else {
non_relevant_total += 1
}
}
if relevant_total <= 0 {
return 0.0
}
let ranked = sorted_run_items(run)
let limit = Int::min(Int::max(cutoff, 0), ranked.length())
let denominator = bpref_denominator(relevant_total, non_relevant_total)
let mut seen_non_relevant = 0
let mut total = 0.0
for index in 0..= threshold {
let penalty = Int::min(seen_non_relevant, denominator)
total += 1.0 - to_ratio(penalty, denominator)
} else if relevance_map.contains(ranked[index].doc_id) {
seen_non_relevant += 1
}
}
total / Double::from_int(relevant_total)
}