///|
fn ranked_query_run(
run : Array[RetrievedDoc],
query_id : String,
) -> Array[RetrievedDoc] {
group_runs(run).get_or_default(query_id, [])
}
///|
pub fn retrieved_document_count(run : Array[RetrievedDoc]) -> Int {
let unique : Map[String, Unit] = Map([])
for item in run {
unique["\{item.query_id}:\{item.doc_id}"] = ()
}
unique.length()
}
///|
pub fn relevant_rank_positions(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
threshold : Int,
) -> Array[Int] {
if qrels.is_empty() {
return []
}
let relevant = build_relevance_map(qrels)
let positions : Array[Int] = []
let ranked = ranked_query_run(run, qrels[0].query_id)
for index in 0..= threshold {
positions.push(index + 1)
}
}
positions
}
///|
pub fn first_relevant_rank(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
threshold : Int,
) -> Int {
let positions = if qrels.is_empty() {
[]
} else {
relevant_rank_positions(qrels, run, threshold)
}
if positions.is_empty() {
0
} else {
positions[0]
}
}
///|
pub fn recall_curve(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
) -> Array[Double] {
let curve : Array[Double] = []
for rank in 1..<=Int::max(cutoff, 0) {
curve.push(recall_at(qrels, run, rank, threshold))
}
curve
}
///|
pub fn precision_curve(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
) -> Array[Double] {
let curve : Array[Double] = []
for rank in 1..<=Int::max(cutoff, 0) {
curve.push(precision_at(qrels, run, rank, threshold))
}
curve
}
///|
pub fn precision_recall_points(
qrels : Array[JudgedDoc],
run : Array[RetrievedDoc],
cutoff : Int,
threshold : Int,
) -> Array[RankPoint] {
let precision = precision_curve(qrels, run, cutoff, threshold)
let recall = recall_curve(qrels, run, cutoff, threshold)
let points : Array[RankPoint] = []
for index in 0.. Double {
let precision = precision_curve(qrels, run, cutoff, threshold)
let recall = recall_curve(qrels, run, cutoff, threshold)
if precision.length() <= 1 {
if precision.is_empty() {
0.0
} else {
precision[0] * recall[0]
}
} else {
let mut area = 0.0
for index in 1.. Array[String] {
let ranked = ranked_query_run(run, query_id)
let result : Array[String] = []
let seen : Map[String, Unit] = Map([])
let limit = Int::min(Int::max(cutoff, 0), ranked.length())
for index in 0.. Double {
let left_docs = unique_documents_at(left, query_id, cutoff)
let right_docs = unique_documents_at(right, query_id, cutoff)
let left_set : Map[String, Unit] = Map([])
let right_set : Map[String, Unit] = Map([])
for doc_id in left_docs {
left_set[doc_id] = ()
}
for doc_id in right_docs {
right_set[doc_id] = ()
}
let mut intersection = 0
for doc_id, _ in left_set {
if right_set.contains(doc_id) {
intersection += 1
}
}
let union = left_set.length() + right_set.length() - intersection
to_ratio(intersection, union)
}
///|
pub fn rank_biased_overlap(
left : Array[RetrievedDoc],
right : Array[RetrievedDoc],
query_id : String,
cutoff : Int,
persistence? : Double = 0.8,
) -> Double {
let p = Double::min(Double::max(persistence, 0.0), 1.0)
let left_docs = unique_documents_at(left, query_id, cutoff)
let right_docs = unique_documents_at(right, query_id, cutoff)
let left_seen : Map[String, Unit] = Map([])
let right_seen : Map[String, Unit] = Map([])
let mut total = 0.0
for rank in 0..