///|
pub fn reciprocal_rank_at(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
) -> Double {
let relevance = build_relevance_map(qrels)
let ranked = sorted_run_items(run)
let limit = Int::min(Int::max(cutoff, 0), ranked.length())
for index in 0..= threshold {
return reciprocal_rank(index)
}
}
0.0
}
///|
pub fn average_precision_at(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
) -> Double {
let relevance = build_relevance_map(qrels)
let ranked = sorted_run_items(run)
let limit = Int::min(Int::max(cutoff, 0), ranked.length())
let mut relevant_total = 0
for _, value in relevance {
if value >= threshold {
relevant_total += 1
}
}
if relevant_total == 0 {
return 0.0
}
let mut hits = 0
let mut total = 0.0
for index in 0..= threshold {
hits += 1
total += to_ratio(hits, index + 1)
}
}
total / Double::from_int(relevant_total)
}
///|
pub fn ndcg_at(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
gain_scheme : GainScheme,
) -> Double {
ignore(threshold)
let relevance = build_relevance_map(qrels)
let ranked = sorted_run_items(run)
let limit = Int::min(Int::max(cutoff, 0), ranked.length())
let values : Array[Int] = []
for _, value in relevance {
values.push(value)
}
let best = ideal_dcg(values, cutoff, gain_scheme)
if best == 0.0 {
return 0.0
}
let mut actual = 0.0
for index in 0.. Double {
judged_recall_at(qrels, run, cutoff)
}
///|
pub fn score_monotonicity(run : Array[RetrievedDoc]) -> Double {
if run.length() < 2 {
return if run.is_empty() { 0.0 } else { 1.0 }
}
let mut ordered = 0
for index in 1..= run[index].score {
ordered += 1
}
}
to_ratio(ordered, run.length() - 1)
}
///|
pub fn run_quality(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
) -> RunQuality {
let profile = profile_dataset(qrels, run)
let unique = retrieved_document_count(run)
let mut finite = 0
for item in run {
if !item.score.is_nan() && !item.score.is_inf() {
finite += 1
}
}
{
query_count: query_id_set_from_run(run).length(),
row_count: run.length(),
unique_row_count: unique,
duplicate_rate: if run.is_empty() {
0.0
} else {
1.0 - to_ratio(unique, run.length())
},
unjudged_rate: if run.is_empty() {
0.0
} else {
to_ratio(profile.unjudged_retrievals, run.length())
},
score_monotonicity: score_monotonicity(run),
finite_score_rate: if run.is_empty() {
0.0
} else {
to_ratio(finite, run.length())
},
}
}